File indexing completed on 2024-04-28 04:49:52
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 }