|
|
|
/*
|
|
|
|
*
|
|
|
|
* $Id: k3blibdvdcss.cpp 619556 2007-01-03 17:38:12Z trueg $
|
|
|
|
* Copyright (C) 2004 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This file is part of the K3b project.
|
|
|
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
* See the file "COPYING" for the exact licensing terms.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "k3blibdvdcss.h"
|
|
|
|
|
|
|
|
#include <k3bdevice.h>
|
|
|
|
#include <k3biso9660.h>
|
|
|
|
#include <k3biso9660backend.h>
|
|
|
|
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqcstring.h>
|
|
|
|
#include <tqvaluevector.h>
|
|
|
|
#include <tqpair.h>
|
|
|
|
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
|
|
|
|
|
|
void* K3bLibDvdCss::s_libDvdCss = 0;
|
|
|
|
int K3bLibDvdCss::s_counter = 0;
|
|
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
struct dvdcss_s;
|
|
|
|
typedef struct dvdcss_s* dvdcss_t;
|
|
|
|
|
|
|
|
dvdcss_t (*k3b_dvdcss_open)(char*);
|
|
|
|
int (*k3b_dvdcss_close)( dvdcss_t );
|
|
|
|
int (*k3b_dvdcss_seek)( dvdcss_t, int, int );
|
|
|
|
int (*k3b_dvdcss_read)( dvdcss_t, void*, int, int );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class K3bLibDvdCss::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Private()
|
|
|
|
:dvd(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
dvdcss_t dvd;
|
|
|
|
K3bDevice::Device* device;
|
|
|
|
TQValueVector< TQPair<int,int> > titleOffsets;
|
|
|
|
int currentSector;
|
|
|
|
bool currentSectorInTitle;
|
|
|
|
};
|
|
|
|
|
|
|
|
K3bLibDvdCss::K3bLibDvdCss()
|
|
|
|
{
|
|
|
|
d = new Private();
|
|
|
|
s_counter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bLibDvdCss::~K3bLibDvdCss()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
delete d;
|
|
|
|
s_counter--;
|
|
|
|
if( s_counter == 0 ) {
|
|
|
|
dlclose( s_libDvdCss );
|
|
|
|
s_libDvdCss = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bLibDvdCss::open( K3bDevice::Device* dev )
|
|
|
|
{
|
|
|
|
d->device = dev;
|
|
|
|
dev->close();
|
|
|
|
d->dvd = k3b_dvdcss_open( const_cast<char*>( TQFile::encodeName(dev->blockDeviceName()).data() ) );
|
|
|
|
d->currentSector = 0;
|
|
|
|
d->currentSectorInTitle = false;
|
|
|
|
return ( d->dvd != 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bLibDvdCss::close()
|
|
|
|
{
|
|
|
|
if( d->dvd )
|
|
|
|
k3b_dvdcss_close( d->dvd );
|
|
|
|
d->dvd = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int K3bLibDvdCss::seek( int sector, int flags )
|
|
|
|
{
|
|
|
|
return k3b_dvdcss_seek( d->dvd, sector, flags );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int K3bLibDvdCss::read( void* buffer, int sectors, int flags )
|
|
|
|
{
|
|
|
|
return k3b_dvdcss_read( d->dvd, buffer, sectors, flags );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int K3bLibDvdCss::readWrapped( void* buffer, int firstSector, int sectors )
|
|
|
|
{
|
|
|
|
// 1. are we in a title?
|
|
|
|
// 2. does a new title start in the read sector area?
|
|
|
|
// - see below, set title if firstSector is the first sector of a new title
|
|
|
|
// 3. does a title end in the read sector area?
|
|
|
|
// 3.1 does a previous title end
|
|
|
|
// 3.2 does the title from 2. already end
|
|
|
|
|
|
|
|
// we need to seek to the first sector. Otherwise we get faulty data.
|
|
|
|
bool needToSeek = ( firstSector != d->currentSector || firstSector == 0 );
|
|
|
|
bool inTitle = false;
|
|
|
|
bool startOfTitle = false;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Make sure we never read encrypted and unencrypted data at once since libdvdcss
|
|
|
|
// only decrypts the whole area of read sectors or nothing at all.
|
|
|
|
//
|
|
|
|
for( unsigned int i = 0; i < d->titleOffsets.count(); ++i ) {
|
|
|
|
int titleStart = d->titleOffsets[i].first;
|
|
|
|
int titleEnd = titleStart + d->titleOffsets[i].second - 1;
|
|
|
|
|
|
|
|
// update key when entrering a new title
|
|
|
|
// FIXME: we also need this if we seek into a new title (not only the start of the title)
|
|
|
|
if( titleStart == firstSector )
|
|
|
|
startOfTitle = needToSeek = inTitle = true;
|
|
|
|
|
|
|
|
// check if a new title or non-title area starts inside the read sector range
|
|
|
|
if( firstSector < titleStart && firstSector+sectors > titleStart ) {
|
|
|
|
kdDebug() << "(K3bLibDvdCss) title start inside of sector range ("
|
|
|
|
<< firstSector << "-" << (firstSector+sectors-1)
|
|
|
|
<< "). only reading " << (titleStart - firstSector) << " sectors up to title offset "
|
|
|
|
<< (titleStart-1) << endl;
|
|
|
|
sectors = titleStart - firstSector;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( firstSector < titleEnd && firstSector+sectors > titleEnd ) {
|
|
|
|
kdDebug() << "(K3bLibDvdCss) title end inside of sector range ("
|
|
|
|
<< firstSector << "-" << (firstSector+sectors-1)
|
|
|
|
<< "). only reading " << (titleEnd - firstSector + 1) << " sectors up to title offset "
|
|
|
|
<< titleEnd << endl;
|
|
|
|
sectors = titleEnd - firstSector + 1;
|
|
|
|
inTitle = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// is our read range part of one title
|
|
|
|
if( firstSector >= titleStart && firstSector+sectors-1 <= titleEnd )
|
|
|
|
inTitle = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( needToSeek ) {
|
|
|
|
int flags = DVDCSS_NOFLAGS;
|
|
|
|
if( startOfTitle )
|
|
|
|
flags = DVDCSS_SEEK_KEY;
|
|
|
|
else if( inTitle )
|
|
|
|
flags = DVDCSS_SEEK_MPEG;
|
|
|
|
|
|
|
|
kdDebug() << "(K3bLibDvdCss) need to seek from " << d->currentSector << " to " << firstSector << " with " << flags << endl;
|
|
|
|
|
|
|
|
d->currentSector = seek( firstSector, flags );
|
|
|
|
if( d->currentSector != firstSector ) {
|
|
|
|
kdDebug() << "(K3bLibDvdCss) seek failed: " << d->currentSector << endl;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
kdDebug() << "(K3bLibDvdCss) seek done: " << d->currentSector << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int flags = DVDCSS_NOFLAGS;
|
|
|
|
if( inTitle )
|
|
|
|
flags = DVDCSS_READ_DECRYPT;
|
|
|
|
|
|
|
|
int ret = read( buffer, sectors, flags );
|
|
|
|
if( ret >= 0 )
|
|
|
|
d->currentSector += ret;
|
|
|
|
else
|
|
|
|
d->currentSector = 0; // force a seek the next time
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bLibDvdCss::crackAllKeys()
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Loop over all titles and crack the keys (inspired by libdvdread)
|
|
|
|
//
|
|
|
|
kdDebug() << "(K3bLibDvdCss) cracking all keys." << endl;
|
|
|
|
|
|
|
|
d->titleOffsets.clear();
|
|
|
|
|
|
|
|
K3bIso9660 iso( new K3bIso9660DeviceBackend( d->device ) );
|
|
|
|
iso.setPlainIso9660( true );
|
|
|
|
if( !iso.open() ) {
|
|
|
|
kdDebug() << "(K3bLibDvdCss) could not open iso9660 fs." << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef K3B_DEBUG
|
|
|
|
iso.debug();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const K3bIso9660Directory* dir = iso.firstIsoDirEntry();
|
|
|
|
|
|
|
|
int title = 0;
|
|
|
|
for( ; title < 100; ++title ) {
|
|
|
|
TQString filename;
|
|
|
|
|
|
|
|
// first we get the menu vob
|
|
|
|
if( title == 0 )
|
|
|
|
filename.sprintf( "VIDEO_TS/VIDEO_TS.VOB" );
|
|
|
|
else
|
|
|
|
filename.sprintf( "VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
|
|
|
|
|
|
|
|
const K3bIso9660File* file = dynamic_cast<const K3bIso9660File*>( dir->entry( filename ) );
|
|
|
|
if( file && file->size() > 0 ) {
|
|
|
|
d->titleOffsets.append( tqMakePair( (int)file->startSector(), (int)(file->size() / 2048U) ) );
|
|
|
|
kdDebug() << "(K3bLibDvdCss) Get key for /" << filename << " at " << file->startSector() << endl;
|
|
|
|
if( seek( (int)file->startSector(), DVDCSS_SEEK_KEY ) < 0 ) {
|
|
|
|
kdDebug() << "(K3bLibDvdCss) unable to seek to " << file->startSector() << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( title > 0 ) {
|
|
|
|
TQPair<int,int> p;
|
|
|
|
int vob = 1;
|
|
|
|
for( ; vob < 100; ++vob ) {
|
|
|
|
filename.sprintf( "VIDEO_TS/VTS_%02d_%d.VOB", title, vob );
|
|
|
|
file = dynamic_cast<const K3bIso9660File*>( dir->entry( filename ) );
|
|
|
|
if( file ) {
|
|
|
|
if( file->size() % 2048 )
|
|
|
|
kdError() << "(K3bLibDvdCss) FILESIZE % 2048 != 0!!!" << endl;
|
|
|
|
if( vob == 1 ) {
|
|
|
|
p.first = file->startSector();
|
|
|
|
p.second = file->size() / 2048;
|
|
|
|
kdDebug() << "(K3bLibDvdCss) Get key for /" << filename << " at " << file->startSector() << endl;
|
|
|
|
if( seek( (int)file->startSector(), DVDCSS_SEEK_KEY ) < 0 ) {
|
|
|
|
kdDebug() << "(K3bLibDvdCss) unable to seek to " << file->startSector() << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p.second += file->size() / 2048;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// last vob
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
--vob;
|
|
|
|
|
|
|
|
// last title
|
|
|
|
if( vob == 0 )
|
|
|
|
break;
|
|
|
|
|
|
|
|
kdDebug() << "(K3bLibDvdCss) Title " << title << " " << vob << " vobs with length " << p.second << endl;
|
|
|
|
d->titleOffsets.append( p );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
--title;
|
|
|
|
|
|
|
|
kdDebug() << "(K3bLibDvdCss) found " << title << " titles." << endl;
|
|
|
|
|
|
|
|
return (title > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bLibDvdCss* K3bLibDvdCss::create()
|
|
|
|
{
|
|
|
|
if( s_libDvdCss == 0 ) {
|
|
|
|
s_libDvdCss = dlopen( "libdvdcss.so.2", RTLD_LAZY|RTLD_GLOBAL );
|
|
|
|
if( s_libDvdCss ) {
|
|
|
|
k3b_dvdcss_open = (dvdcss_t (*)(char*))dlsym( s_libDvdCss, "dvdcss_open" );
|
|
|
|
k3b_dvdcss_close = (int (*)( dvdcss_t ))dlsym( s_libDvdCss, "dvdcss_close" );
|
|
|
|
k3b_dvdcss_seek = (int (*)( dvdcss_t, int, int ))dlsym( s_libDvdCss, "dvdcss_seek" );
|
|
|
|
k3b_dvdcss_read = (int (*)( dvdcss_t, void*, int, int ))dlsym( s_libDvdCss, "dvdcss_read" );
|
|
|
|
|
|
|
|
if( !k3b_dvdcss_open || !k3b_dvdcss_close || !k3b_dvdcss_seek || !k3b_dvdcss_read ) {
|
|
|
|
kdDebug() << "(K3bLibDvdCss) unable to resolve libdvdcss." << endl;
|
|
|
|
dlclose( s_libDvdCss );
|
|
|
|
s_libDvdCss = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
kdDebug() << "(K3bLibDvdCss) unable to load libdvdcss." << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( s_libDvdCss )
|
|
|
|
return new K3bLibDvdCss();
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|