File indexing completed on 2024-04-28 04:49:44
0001 /* 0002 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "k3btocfilewriter.h" 0007 0008 #include "k3btrack.h" 0009 #include "k3bmsf.h" 0010 #include "k3bcore.h" 0011 #include "k3bversion.h" 0012 0013 #include <QDateTime> 0014 #include <QFile> 0015 0016 0017 K3b::TocFileWriter::TocFileWriter() 0018 : m_hideFirstTrack(false), 0019 m_sessionToWrite(1) 0020 { 0021 } 0022 0023 0024 bool K3b::TocFileWriter::save( const QString& filename ) 0025 { 0026 QFile f( filename ); 0027 0028 if( !f.open( QIODevice::WriteOnly ) ) { 0029 qDebug() << "(K3b::CueFileWriter) could not open file " << f.fileName(); 0030 return false; 0031 } 0032 0033 QTextStream s( &f ); 0034 0035 return save( s ); 0036 } 0037 0038 0039 bool K3b::TocFileWriter::save( QTextStream& t ) 0040 { 0041 writeHeader(t); 0042 0043 if( !m_cdText.isEmpty() ) 0044 writeGlobalCdText(t); 0045 0046 // 0047 // see if we have multiple sessions 0048 // 0049 int sessions = 1; 0050 for( K3b::Device::Toc::iterator it = m_toc.begin(); it != m_toc.end(); ++it ) { 0051 if( (*it).session() > 1 ) 0052 sessions = (*it).session(); 0053 } 0054 0055 if( m_sessionToWrite > sessions ) 0056 m_sessionToWrite = 1; 0057 0058 // 0059 // We can only hide the first track if both the first and the second track are 0060 // audio tracks. 0061 // We also can only hide the first track in the first session. 0062 // 0063 bool hideFirstTrack = m_hideFirstTrack; 0064 if( m_toc.count() < 2 || 0065 m_toc[0].type() != K3b::Device::Track::TYPE_AUDIO || 0066 m_toc[1].type() != K3b::Device::Track::TYPE_AUDIO || 0067 (sessions > 1 && m_sessionToWrite != 1 ) ) 0068 hideFirstTrack = false; 0069 0070 0071 // the dataStart will be the offset in case we do not write the first session 0072 K3b::Msf dataStart; 0073 0074 int trackIndex = 0; 0075 if( hideFirstTrack ) { 0076 const K3b::Device::Track& hiddenTrack = m_toc[0]; 0077 const K3b::Device::Track& track = m_toc[1]; 0078 0079 t << "// Track number 1 (hidden) and track number 2 (as track 1)" << Qt::endl; 0080 t << "TRACK AUDIO" << Qt::endl; 0081 0082 if( track.copyPermitted() ) 0083 t << "COPY" << Qt::endl; 0084 else 0085 t << "NO COPY" << Qt::endl; 0086 0087 if( track.preEmphasis() ) 0088 t << "PRE_EMPHASIS" << Qt::endl; 0089 else 0090 t << "NO PRE_EMPHASIS" << Qt::endl; 0091 0092 if( !m_cdText.isEmpty() ) 0093 writeTrackCdText( m_cdText[0], t ); 0094 0095 // the "hidden" file will be used as pregap for the "first" track 0096 t << "AUDIOFILE "; 0097 writeDataSource( 0, t ); 0098 if( readFromStdin() ) 0099 t << hiddenTrack.firstSector().toString(); 0100 else 0101 t << " 0"; 0102 t << " " << hiddenTrack.length().toString() << Qt::endl; 0103 t << "START" << Qt::endl; // use the whole hidden file as pregap 0104 0105 // now comes the "real" first track 0106 t << "AUDIOFILE "; 0107 writeDataSource( 1, t ); 0108 if( readFromStdin() ) 0109 t << track.firstSector().toString() << " "; 0110 else 0111 t << "0 "; 0112 // no index 0 for the last track. Or should we allow this??? 0113 if( m_toc.count() == 2 ) 0114 t << track.length().toString(); 0115 else 0116 t << track.realAudioLength().toString(); 0117 t << Qt::endl << Qt::endl; 0118 0119 trackIndex+=2; 0120 } 0121 else { 0122 // 0123 // Seek to the first track to write. 0124 // In case we hid the first track above it was the first track anyway. 0125 // 0126 while( m_toc[trackIndex].session() < m_sessionToWrite && 0127 m_toc[trackIndex].session() > 0 ) 0128 ++trackIndex; 0129 0130 dataStart = m_toc[trackIndex].firstSector(); 0131 } 0132 0133 qDebug() << "(K3b::TocFileWriter) using offset of: " << dataStart.toString(); 0134 0135 while( trackIndex < m_toc.count() ) { 0136 if( m_toc[trackIndex].session() == 0 || m_toc[trackIndex].session() == m_sessionToWrite ) 0137 writeTrack( trackIndex, dataStart, t ); 0138 trackIndex++; 0139 } 0140 0141 return ( t.status() == QTextStream::Ok ); 0142 } 0143 0144 0145 void K3b::TocFileWriter::writeHeader( QTextStream& t ) 0146 { 0147 // little comment 0148 t << "// TOC-file to use with cdrdao created by K3b " << k3bcore->version() 0149 << ", " << QDateTime::currentDateTime().toString() << Qt::endl << Qt::endl; 0150 0151 t << "// " << m_toc.count() << " tracks" << Qt::endl; 0152 if( m_toc.back().session() > 0 ) { 0153 t << "// " << m_toc.back().session() << " sessions" << Qt::endl 0154 << "// this is session number " << m_sessionToWrite << Qt::endl; 0155 } 0156 t << Qt::endl; 0157 0158 // check the cd type 0159 if( m_toc.contentType() == K3b::Device::AUDIO ) { 0160 t << "CD_DA"; 0161 } 0162 else { 0163 bool hasMode2Tracks = false; 0164 for( K3b::Device::Toc::iterator it = m_toc.begin(); it != m_toc.end(); ++it ) { 0165 const K3b::Device::Track& track = *it; 0166 if( track.type() == K3b::Device::Track::TYPE_DATA && 0167 (track.mode() == K3b::Device::Track::MODE2 || 0168 track.mode() == K3b::Device::Track::XA_FORM1 || 0169 track.mode() == K3b::Device::Track::XA_FORM2 ) ) { 0170 hasMode2Tracks = true; 0171 break; 0172 } 0173 } 0174 0175 if( hasMode2Tracks ) 0176 t << "CD_ROM_XA"; 0177 else 0178 t << "CD_ROM"; 0179 } 0180 0181 t << Qt::endl << Qt::endl; 0182 } 0183 0184 0185 void K3b::TocFileWriter::writeTrack( int index, const K3b::Msf& offset, QTextStream& t ) 0186 { 0187 const K3b::Device::Track& track = m_toc[index]; 0188 0189 t << "// Track number " << (index+1) << Qt::endl; 0190 0191 if( track.type() == K3b::Device::Track::TYPE_AUDIO ) { 0192 t << "TRACK AUDIO" << Qt::endl; 0193 0194 if( track.copyPermitted() ) 0195 t << "COPY" << Qt::endl; 0196 else 0197 t << "NO COPY" << Qt::endl; 0198 0199 if( track.preEmphasis() ) 0200 t << "PRE_EMPHASIS" << Qt::endl; 0201 else 0202 t << "NO PRE_EMPHASIS" << Qt::endl; 0203 0204 if( !m_cdText.isEmpty() ) 0205 writeTrackCdText( m_cdText[index], t ); 0206 0207 // 0208 // cdrdao sees the pregap as part of the current track and not as part of 0209 // the previous like it really is. 0210 // 0211 0212 if( index == 0 ) { 0213 if( (track.firstSector()-offset) > 0 ) { 0214 // 0215 // the first track is the only track K3b does not generate null-pregap data for 0216 // since cdrecord does not allow this. So We just do it here the same way and tell 0217 // cdrdao to create the first pregap for us 0218 // 0219 0220 t << "PREGAP " 0221 << (track.firstSector()-offset).toString() << Qt::endl; 0222 } 0223 } 0224 else { 0225 const K3b::Device::Track& lastTrack = m_toc[index-1]; 0226 0227 // 0228 // the pregap data 0229 // 0230 if( lastTrack.index0() > 0 ) { 0231 t << "AUDIOFILE "; 0232 writeDataSource( index-1, t ); 0233 if( readFromStdin() ) 0234 t << (lastTrack.firstSector() + lastTrack.index0() - offset).toString(); 0235 else 0236 t << (lastTrack.index0() - offset).toString(); 0237 t << " " 0238 << (lastTrack.length() - lastTrack.index0()).toString() 0239 << Qt::endl 0240 << "START" << Qt::endl; 0241 } 0242 } 0243 0244 // 0245 // The track data 0246 // 0247 t << "AUDIOFILE "; 0248 writeDataSource( index, t ); 0249 if( readFromStdin() ) 0250 t << (track.firstSector() - offset).toString() << " "; 0251 else 0252 t << "0 "; 0253 // no index 0 for the last track. Or should we allow this??? 0254 if( index == m_toc.count()-1 ) 0255 t << track.length().toString(); 0256 else 0257 t << track.realAudioLength().toString(); 0258 t << Qt::endl; 0259 } 0260 else { 0261 if( track.mode() == K3b::Device::Track::XA_FORM1 ) 0262 t << "TRACK MODE2_FORM1" << Qt::endl; 0263 else if( track.mode() == K3b::Device::Track::XA_FORM2 ) 0264 t << "TRACK MODE2_FORM2" << Qt::endl; 0265 else 0266 t << "TRACK MODE1" << Qt::endl; 0267 0268 if( !m_cdText.isEmpty() && !m_toc.contentType() != K3b::Device::DATA ) { 0269 // 0270 // insert fake cdtext 0271 // cdrdao does not work without it and it seems not to do any harm. 0272 // 0273 t << "CD_TEXT {" << Qt::endl 0274 << " LANGUAGE 0 {" << Qt::endl 0275 << " TITLE " << "\"\"" << Qt::endl 0276 << " PERFORMER " << "\"\"" << Qt::endl 0277 << " ISRC " << "\"\"" << Qt::endl 0278 << " ARRANGER " << "\"\"" << Qt::endl 0279 << " SONGWRITER " << "\"\"" << Qt::endl 0280 << " COMPOSER " << "\"\"" << Qt::endl 0281 << " MESSAGE " << "\"\"" << Qt::endl 0282 << " }" << Qt::endl 0283 << "}" << Qt::endl; 0284 } 0285 0286 if( readFromStdin() ) 0287 t << "DATAFILE \"-\" " << track.length().toString() << Qt::endl; 0288 else 0289 t << "DATAFILE \"" << m_filenames[index] << "\"" << Qt::endl; 0290 t << Qt::endl; 0291 } 0292 0293 t << Qt::endl; 0294 } 0295 0296 0297 void K3b::TocFileWriter::writeGlobalCdText( QTextStream& t ) 0298 { 0299 t << "CD_TEXT {" << Qt::endl; 0300 t << " LANGUAGE_MAP { 0: EN }" << Qt::endl; 0301 t << " LANGUAGE 0 {" << Qt::endl; 0302 t << " TITLE " << "\"" << m_cdText.title() << "\"" << Qt::endl; 0303 t << " PERFORMER " << "\"" << m_cdText.performer() << "\"" << Qt::endl; 0304 t << " DISC_ID " << "\"" << m_cdText.discId() << "\"" << Qt::endl; 0305 t << " UPC_EAN " << "\"" << m_cdText.upcEan() << "\"" << Qt::endl; 0306 t << Qt::endl; 0307 t << " ARRANGER " << "\"" << m_cdText.arranger() << "\"" << Qt::endl; 0308 t << " SONGWRITER " << "\"" << m_cdText.songwriter() << "\"" << Qt::endl; 0309 t << " COMPOSER " << "\"" << m_cdText.composer() << "\"" << Qt::endl; 0310 t << " MESSAGE " << "\"" << m_cdText.message() << "\"" << Qt::endl; 0311 t << " }" << Qt::endl; 0312 t << "}" << Qt::endl; 0313 t << Qt::endl; 0314 } 0315 0316 0317 void K3b::TocFileWriter::writeTrackCdText( const K3b::Device::TrackCdText& track, QTextStream& t ) 0318 { 0319 t << "CD_TEXT {" << Qt::endl; 0320 t << " LANGUAGE 0 {" << Qt::endl; 0321 t << " TITLE " << "\"" << track.title() << "\"" << Qt::endl; 0322 t << " PERFORMER " << "\"" << track.performer() << "\"" << Qt::endl; 0323 t << " ISRC " << "\"" << track.isrc() << "\"" << Qt::endl; 0324 t << " ARRANGER " << "\"" << track.arranger() << "\"" << Qt::endl; 0325 t << " SONGWRITER " << "\"" << track.songwriter() << "\"" << Qt::endl; 0326 t << " COMPOSER " << "\"" << track.composer() << "\"" << Qt::endl; 0327 t << " MESSAGE " << "\"" << track.message() << "\"" << Qt::endl; 0328 t << " }" << Qt::endl; 0329 t << "}" << Qt::endl; 0330 } 0331 0332 0333 void K3b::TocFileWriter::writeDataSource( int trackIndex, QTextStream& t ) 0334 { 0335 if( readFromStdin() ) 0336 t << "\"-\" "; 0337 else 0338 t << "\"" << m_filenames[trackIndex] << "\" "; 0339 } 0340 0341 0342 bool K3b::TocFileWriter::readFromStdin() const 0343 { 0344 return ( m_toc.count() > m_filenames.count() ); 0345 }