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

0001 /*
0002     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include <config-k3b.h>
0007 
0008 #include "k3blibdvdcss.h"
0009 
0010 #include "k3bdevice.h"
0011 #include "k3biso9660.h"
0012 #include "k3biso9660backend.h"
0013 
0014 #include <QFile>
0015 #include <QGlobalStatic>
0016 #include <QLibrary>
0017 #include <QPair>
0018 
0019 
0020 
0021 extern "C" {
0022     struct dvdcss_s;
0023     typedef struct dvdcss_s* dvdcss_t;
0024 
0025     dvdcss_t (*k3b_dvdcss_open)(char*);
0026     int (*k3b_dvdcss_close)( dvdcss_t );
0027     int (*k3b_dvdcss_seek)( dvdcss_t, int, int );
0028     int (*k3b_dvdcss_read)( dvdcss_t, void*, int, int );
0029 }
0030 
0031 
0032 Q_GLOBAL_STATIC(QLibrary, s_libDvdCss)
0033 
0034 
0035 
0036 class K3b::LibDvdCss::Private
0037 {
0038 public:
0039     Private()
0040         :dvd(0) {
0041     }
0042 
0043     dvdcss_t dvd;
0044     K3b::Device::Device* device;
0045     QVector< QPair<int,int> > titleOffsets;
0046     int currentSector;
0047     bool currentSectorInTitle;
0048 };
0049 
0050 K3b::LibDvdCss::LibDvdCss()
0051 {
0052     d = new Private();
0053 }
0054 
0055 
0056 K3b::LibDvdCss::~LibDvdCss()
0057 {
0058     close();
0059     delete d;
0060 }
0061 
0062 
0063 bool K3b::LibDvdCss::open( K3b::Device::Device* dev )
0064 {
0065     d->device = dev;
0066     dev->close();
0067     d->dvd = k3b_dvdcss_open( const_cast<char*>( QFile::encodeName(dev->blockDeviceName()).data() ) );
0068     d->currentSector = 0;
0069     d->currentSectorInTitle = false;
0070     return ( d->dvd != 0 );
0071 }
0072 
0073 
0074 void K3b::LibDvdCss::close()
0075 {
0076     if( d->dvd )
0077         k3b_dvdcss_close( d->dvd );
0078     d->dvd = 0;
0079 }
0080 
0081 
0082 int K3b::LibDvdCss::seek( int sector, int flags )
0083 {
0084     return k3b_dvdcss_seek( d->dvd, sector, flags );
0085 }
0086 
0087 
0088 int K3b::LibDvdCss::read( void* buffer, int sectors, int flags )
0089 {
0090     return k3b_dvdcss_read( d->dvd, buffer, sectors, flags );
0091 }
0092 
0093 
0094 int K3b::LibDvdCss::readWrapped( void* buffer, int firstSector, int sectors )
0095 {
0096     // 1. are we in a title?
0097     // 2. does a new title start in the read sector area?
0098     //    - see below, set title if firstSector is the first sector of a new title
0099     // 3. does a title end in the read sector area?
0100     //    3.1 does a previous title end
0101     //    3.2 does the title from 2. already end
0102 
0103     // we need to seek to the first sector. Otherwise we get faulty data.
0104     bool needToSeek = ( firstSector != d->currentSector || firstSector == 0 );
0105     bool inTitle = false;
0106     bool startOfTitle = false;
0107 
0108     //
0109     // Make sure we never read encrypted and unencrypted data at once since libdvdcss
0110     // only decrypts the whole area of read sectors or nothing at all.
0111     //
0112     for( int i = 0; i < d->titleOffsets.count(); ++i ) {
0113         int titleStart = d->titleOffsets[i].first;
0114         int titleEnd = titleStart + d->titleOffsets[i].second - 1;
0115 
0116         // update key when entrering a new title
0117         // FIXME: we also need this if we seek into a new title (not only the start of the title)
0118         if( titleStart == firstSector )
0119             startOfTitle = needToSeek = inTitle = true;
0120 
0121         // check if a new title or non-title area starts inside the read sector range
0122         if( firstSector < titleStart && firstSector+sectors > titleStart ) {
0123             qDebug() << "(K3b::LibDvdCss) title start inside of sector range ("
0124                      << firstSector << "-" << (firstSector+sectors-1)
0125                      << "). only reading " << (titleStart - firstSector) << " sectors up to title offset "
0126                      << (titleStart-1);
0127             sectors = titleStart - firstSector;
0128         }
0129 
0130         if( firstSector < titleEnd && firstSector+sectors > titleEnd ) {
0131             qDebug() << "(K3b::LibDvdCss) title end inside of sector range ("
0132                      << firstSector << "-" << (firstSector+sectors-1)
0133                      << "). only reading " << (titleEnd - firstSector + 1) << " sectors up to title offset "
0134                      << titleEnd;
0135             sectors = titleEnd - firstSector + 1;
0136             inTitle = true;
0137         }
0138 
0139         // is our read range part of one title
0140         if( firstSector >= titleStart && firstSector+sectors-1 <= titleEnd )
0141             inTitle = true;
0142     }
0143 
0144     if( needToSeek ) {
0145         int flags = DVDCSS_NOFLAGS;
0146         if( startOfTitle )
0147             flags = DVDCSS_SEEK_KEY;
0148         else if( inTitle )
0149             flags = DVDCSS_SEEK_MPEG;
0150 
0151         qDebug() << "(K3b::LibDvdCss) need to seek from " << d->currentSector << " to " << firstSector << " with " << flags;
0152 
0153         d->currentSector = seek( firstSector, flags );
0154         if( d->currentSector != firstSector ) {
0155             qDebug() << "(K3b::LibDvdCss) seek failed: " << d->currentSector;
0156             return -1;
0157         }
0158 
0159         qDebug() << "(K3b::LibDvdCss) seek done: " << d->currentSector;
0160     }
0161 
0162 
0163     int flags = DVDCSS_NOFLAGS;
0164     if( inTitle )
0165         flags = DVDCSS_READ_DECRYPT;
0166 
0167     int ret = read( buffer, sectors, flags );
0168     if( ret >= 0 )
0169         d->currentSector += ret;
0170     else
0171         d->currentSector = 0; // force a seek the next time
0172 
0173     return ret;
0174 }
0175 
0176 
0177 bool K3b::LibDvdCss::crackAllKeys()
0178 {
0179     //
0180     // Loop over all titles and crack the keys (inspired by libdvdread)
0181     //
0182     qDebug() << "(K3b::LibDvdCss) cracking all keys.";
0183 
0184     d->titleOffsets.clear();
0185 
0186     K3b::Iso9660 iso( new K3b::Iso9660DeviceBackend( d->device ) );
0187     iso.setPlainIso9660( true );
0188     if( !iso.open() ) {
0189         qDebug() << "(K3b::LibDvdCss) could not open iso9660 fs.";
0190         return false;
0191     }
0192 
0193 #ifdef K3B_DEBUG
0194     iso.debug();
0195 #endif
0196 
0197     const K3b::Iso9660Directory* dir = iso.firstIsoDirEntry();
0198 
0199     int title = 0;
0200     for( ; title < 100; ++title ) {
0201         QString filename;
0202 
0203         // first we get the menu vob
0204         if( title == 0 )
0205             filename = QLatin1String( "VIDEO_TS/VIDEO_TS.VOB" );
0206         else
0207             filename = QString::asprintf( "VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
0208 
0209         const K3b::Iso9660File* file = dynamic_cast<const K3b::Iso9660File*>( dir->entry( filename ) );
0210         if( file && file->size() > 0 ) {
0211             d->titleOffsets.append( qMakePair( (int)file->startSector(), (int)(file->size() / 2048U) ) );
0212             qDebug() << "(K3b::LibDvdCss) Get key for /" << filename << " at " << file->startSector();
0213             if( seek( (int)file->startSector(), DVDCSS_SEEK_KEY ) < 0 ) {
0214                 qDebug() << "(K3b::LibDvdCss) failed to crash key for " << filename << " at " << file->startSector();
0215             }
0216         }
0217 
0218         if( title > 0 ) {
0219             QPair<int,int> p;
0220             int vob = 1;
0221             for( ; vob < 100; ++vob ) {
0222                 filename = QString::asprintf( "VIDEO_TS/VTS_%02d_%d.VOB", title, vob );
0223                 file = dynamic_cast<const K3b::Iso9660File*>( dir->entry( filename ) );
0224                 if( file ) {
0225                     if( file->size() % 2048 )
0226                         qCritical() << "(K3b::LibDvdCss) FILESIZE % 2048 != 0!!!" << Qt::endl;
0227                     if( vob == 1 ) {
0228                         p.first = file->startSector();
0229                         p.second = file->size() / 2048;
0230                         qDebug() << "(K3b::LibDvdCss) Get key for /" << filename << " at " << file->startSector();
0231                         if( seek( (int)file->startSector(), DVDCSS_SEEK_KEY ) < 0 ) {
0232                             qDebug() << "(K3b::LibDvdCss) failed to crash key for " << filename << " at " << file->startSector();
0233                         }
0234                     }
0235                     else {
0236                         p.second += file->size() / 2048;
0237                     }
0238                 }
0239                 else {
0240                     // last vob
0241                     break;
0242                 }
0243             }
0244             --vob;
0245 
0246             // last title
0247             if( vob == 0 )
0248                 break;
0249 
0250             qDebug() << "(K3b::LibDvdCss) Title " << title << " " << vob << " vobs with length " << p.second;
0251             d->titleOffsets.append( p );
0252         }
0253     }
0254 
0255     --title;
0256 
0257     qDebug() << "(K3b::LibDvdCss) found " << title << " titles.";
0258 
0259     return (title > 0);
0260 }
0261 
0262 
0263 K3b::LibDvdCss* K3b::LibDvdCss::create()
0264 {
0265     if( !s_libDvdCss->isLoaded() ) {
0266         s_libDvdCss->setFileNameAndVersion( "dvdcss", 2 );
0267         s_libDvdCss->setLoadHints( QLibrary::ExportExternalSymbolsHint );
0268 
0269         if( s_libDvdCss->load() ) {
0270             k3b_dvdcss_open = (dvdcss_t (*)(char*))s_libDvdCss->resolve( "dvdcss_open" );
0271             k3b_dvdcss_close = (int (*)( dvdcss_t ))s_libDvdCss->resolve( "dvdcss_close" );
0272             k3b_dvdcss_seek = (int (*)( dvdcss_t, int, int ))s_libDvdCss->resolve( "dvdcss_seek" );
0273             k3b_dvdcss_read = (int (*)( dvdcss_t, void*, int, int ))s_libDvdCss->resolve( "dvdcss_read" );
0274 
0275             if( !k3b_dvdcss_open || !k3b_dvdcss_close || !k3b_dvdcss_seek || !k3b_dvdcss_read ) {
0276                 qDebug() << "(K3b::LibDvdCss) unable to resolve libdvdcss.";
0277                 s_libDvdCss->unload();
0278                 return 0;
0279             }
0280         }
0281         else {
0282             qDebug() << "(K3b::LibDvdCss) unable to load libdvdcss.";
0283             return 0;
0284         }
0285     }
0286 
0287     return new K3b::LibDvdCss();
0288 }