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 }