summaryrefslogtreecommitdiffstats
path: root/libk3b/tools/k3bcdparanoialib.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libk3b/tools/k3bcdparanoialib.cpp')
-rw-r--r--libk3b/tools/k3bcdparanoialib.cpp783
1 files changed, 783 insertions, 0 deletions
diff --git a/libk3b/tools/k3bcdparanoialib.cpp b/libk3b/tools/k3bcdparanoialib.cpp
new file mode 100644
index 0000000..5976941
--- /dev/null
+++ b/libk3b/tools/k3bcdparanoialib.cpp
@@ -0,0 +1,783 @@
+/*
+ *
+ * $Id: k3bcdparanoialib.cpp 621693 2007-01-09 14:38:25Z trueg $
+ * Copyright (C) 2003-2007 Sebastian Trueg <[email protected]>
+ *
+ * This file is part of the K3b project.
+ * Copyright (C) 1998-2007 Sebastian Trueg <[email protected]>
+ *
+ * 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 "k3bcdparanoialib.h"
+
+#include <k3bdevice.h>
+#include <k3btoc.h>
+#include <k3bmsf.h>
+
+#include <kdebug.h>
+
+#include <dlfcn.h>
+
+#include <qfile.h>
+#include <qmutex.h>
+
+
+static bool s_haveLibCdio = false;
+
+
+void* K3bCdparanoiaLib::s_libInterface = 0;
+void* K3bCdparanoiaLib::s_libParanoia = 0;
+int K3bCdparanoiaLib::s_counter = 0;
+
+
+#define CDDA_IDENTIFY s_haveLibCdio ? "cdio_cddap_identify" : "cdda_identify"
+#define CDDA_CLOSE s_haveLibCdio ? "cdio_cddap_close" : "cdda_close"
+#define CDDA_OPEN s_haveLibCdio ? "cdio_cddap_open" : "cdda_open"
+#define CDDA_TRACK_FIRSTSECTOR s_haveLibCdio ? "cdio_cddap_track_firstsector" : "cdda_track_firstsector"
+#define CDDA_TRACK_LASTSECTOR s_haveLibCdio ? "cdio_cddap_track_lastsector" : "cdda_track_lastsector"
+#define CDDA_VERBOSE_SET s_haveLibCdio ? "cdio_cddap_verbose_set" : "cdda_verbose_set"
+#define CDDA_DISC_FIRSTSECTOR s_haveLibCdio ? "cdio_cddap_disc_firstsector" : "cdda_disc_firstsector"
+
+#define PARANOIA_INIT s_haveLibCdio ? "cdio_paranoia_init" : "paranoia_init"
+#define PARANOIA_FREE s_haveLibCdio ? "cdio_paranoia_free" : "paranoia_free"
+#define PARANOIA_MODESET s_haveLibCdio ? "cdio_paranoia_modeset" : "paranoia_modeset"
+#define PARANOIA_SEEK s_haveLibCdio ? "cdio_paranoia_seek" : "paranoia_seek"
+#define PARANOIA_READ_LIMITED s_haveLibCdio ? "cdio_paranoia_read_limited" : "paranoia_read_limited"
+
+
+// from cdda_paranoia.h
+#define PARANOIA_CB_READ 0
+#define PARANOIA_CB_VERIFY 1
+#define PARANOIA_CB_FIXUP_EDGE 2
+#define PARANOIA_CB_FIXUP_ATOM 3
+#define PARANOIA_CB_SCRATCH 4
+#define PARANOIA_CB_REPAIR 5
+#define PARANOIA_CB_SKIP 6
+#define PARANOIA_CB_DRIFT 7
+#define PARANOIA_CB_BACKOFF 8
+#define PARANOIA_CB_OVERLAP 9
+#define PARANOIA_CB_FIXUP_DROPPED 10
+#define PARANOIA_CB_FIXUP_DUPED 11
+#define PARANOIA_CB_READERR 12
+
+
+
+static void paranoiaCallback( long, int status )
+{
+ // do nothing so far....
+ return;
+
+ switch( status ) {
+ case -1:
+ break;
+ case -2:
+ break;
+ case PARANOIA_CB_READ:
+ // no problem
+ // does only this mean that the sector has been read?
+// m_lastReadSector = sector; // this seems to be rather useless
+// m_readSectors++;
+ break;
+ case PARANOIA_CB_VERIFY:
+ break;
+ case PARANOIA_CB_FIXUP_EDGE:
+ break;
+ case PARANOIA_CB_FIXUP_ATOM:
+ break;
+ case PARANOIA_CB_SCRATCH:
+ // scratch detected
+ break;
+ case PARANOIA_CB_REPAIR:
+ break;
+ case PARANOIA_CB_SKIP:
+ // skipped sector
+ break;
+ case PARANOIA_CB_DRIFT:
+ break;
+ case PARANOIA_CB_BACKOFF:
+ break;
+ case PARANOIA_CB_OVERLAP:
+ // sector does not seem to contain the current
+ // sector but the amount of overlapped data
+ // m_overlap = sector;
+ break;
+ case PARANOIA_CB_FIXUP_DROPPED:
+ break;
+ case PARANOIA_CB_FIXUP_DUPED:
+ break;
+ case PARANOIA_CB_READERR:
+ break;
+ }
+}
+
+
+
+extern "C" {
+ struct cdrom_drive;
+ struct cdrom_paranoia;
+
+ // HINT: these pointers must NOT have the same name like the actual methods!
+ // I added "cdda_" as prefix
+ // Before doing that K3b crashed in cdda_open!
+ // Can anyone please explain that to me?
+
+ // cdda_interface
+ cdrom_drive* (*cdda_cdda_identify)(const char*, int, char**);
+ int (*cdda_cdda_open)(cdrom_drive *d);
+ int (*cdda_cdda_close)(cdrom_drive *d);
+ long (*cdda_cdda_track_firstsector)( cdrom_drive*, int );
+ long (*cdda_cdda_track_lastsector)( cdrom_drive*, int );
+ long (*cdda_cdda_disc_firstsector)(cdrom_drive *d);
+ void (*cdda_cdda_verbose_set)(cdrom_drive *d,int err_action, int mes_action);
+
+ // cdda_paranoia
+ cdrom_paranoia* (*cdda_paranoia_init)(cdrom_drive*);
+ void (*cdda_paranoia_free)(cdrom_paranoia *p);
+ void (*cdda_paranoia_modeset)(cdrom_paranoia *p, int mode);
+ int16_t* (*cdda_paranoia_read_limited)(cdrom_paranoia *p, void(*callback)(long,int), int);
+ long (*cdda_paranoia_seek)(cdrom_paranoia *p,long seek,int mode);
+}
+
+// from cdda_paranoia.h
+#define PARANOIA_MODE_FULL 0xff
+#define PARANOIA_MODE_DISABLE 0
+
+#define PARANOIA_MODE_VERIFY 1
+#define PARANOIA_MODE_FRAGMENT 2
+#define PARANOIA_MODE_OVERLAP 4
+#define PARANOIA_MODE_SCRATCH 8
+#define PARANOIA_MODE_REPAIR 16
+#define PARANOIA_MODE_NEVERSKIP 32
+
+
+
+/**
+ * Internal class used by K3bCdparanoiaLib
+ */
+class K3bCdparanoiaLibData
+{
+ public:
+ K3bCdparanoiaLibData( K3bDevice::Device* dev )
+ : m_device(dev),
+ m_drive(0),
+ m_paranoia(0),
+ m_currentSector(0) {
+ s_dataMap.insert( dev, this );
+ }
+
+ ~K3bCdparanoiaLibData() {
+ paranoiaFree();
+
+ s_dataMap.erase( m_device );
+ }
+
+ K3bDevice::Device* device() const { return m_device; }
+ void paranoiaModeSet( int );
+ bool paranoiaInit();
+ void paranoiaFree();
+ int16_t* paranoiaRead( void(*callback)(long,int), int maxRetries );
+ long paranoiaSeek( long, int );
+ long firstSector( int );
+ long lastSector( int );
+ long sector() const { return m_currentSector; }
+
+ static K3bCdparanoiaLibData* data( K3bDevice::Device* dev ) {
+ QMap<K3bDevice::Device*, K3bCdparanoiaLibData*>::const_iterator it = s_dataMap.find( dev );
+ if( it == s_dataMap.constEnd() )
+ return new K3bCdparanoiaLibData( dev );
+ else
+ return *it;
+ }
+
+ static void freeAll() {
+ // clean up all K3bCdparanoiaLibData instances
+ for( QMap<K3bDevice::Device*, K3bCdparanoiaLibData*>::iterator it = s_dataMap.begin();
+ it != s_dataMap.end(); ++it )
+ delete it.data();
+ }
+
+ private:
+ //
+ // We have exactly one instance of K3bCdparanoiaLibData per device
+ //
+ static QMap<K3bDevice::Device*, K3bCdparanoiaLibData*> s_dataMap;
+
+ K3bDevice::Device* m_device;
+
+ cdrom_drive* m_drive;
+ cdrom_paranoia* m_paranoia;
+
+ long m_currentSector;
+
+ QMutex mutex;
+};
+
+
+QMap<K3bDevice::Device*, K3bCdparanoiaLibData*> K3bCdparanoiaLibData::s_dataMap;
+
+bool K3bCdparanoiaLibData::paranoiaInit()
+{
+ mutex.lock();
+
+ if( m_drive )
+ paranoiaFree();
+
+ // since we use cdparanoia to open the device it is important to close
+ // the device here
+ m_device->close();
+
+ m_drive = cdda_cdda_identify( QFile::encodeName(m_device->blockDeviceName()), 0, 0 );
+ if( m_drive == 0 ) {
+ mutex.unlock();
+ return false;
+ }
+
+ // cdda_cdda_verbose_set( m_drive, 1, 1 );
+
+ cdda_cdda_open( m_drive );
+ m_paranoia = cdda_paranoia_init( m_drive );
+ if( m_paranoia == 0 ) {
+ mutex.unlock();
+ paranoiaFree();
+ return false;
+ }
+
+ m_currentSector = 0;
+
+ mutex.unlock();
+
+ return true;
+}
+
+
+void K3bCdparanoiaLibData::paranoiaFree()
+{
+ mutex.lock();
+
+ if( m_paranoia ) {
+ cdda_paranoia_free( m_paranoia );
+ m_paranoia = 0;
+ }
+ if( m_drive ) {
+ cdda_cdda_close( m_drive );
+ m_drive = 0;
+ }
+
+ mutex.unlock();
+}
+
+
+void K3bCdparanoiaLibData::paranoiaModeSet( int mode )
+{
+ mutex.lock();
+ cdda_paranoia_modeset( m_paranoia, mode );
+ mutex.unlock();
+}
+
+
+int16_t* K3bCdparanoiaLibData::paranoiaRead( void(*callback)(long,int), int maxRetries )
+{
+ if( m_paranoia ) {
+ mutex.lock();
+ int16_t* data = cdda_paranoia_read_limited( m_paranoia, callback, maxRetries );
+ if( data )
+ m_currentSector++;
+ mutex.unlock();
+ return data;
+ }
+ else
+ return 0;
+}
+
+
+long K3bCdparanoiaLibData::firstSector( int track )
+{
+ if( m_drive ) {
+ mutex.lock();
+ long sector = cdda_cdda_track_firstsector( m_drive, track );
+ mutex.unlock();
+ return sector;
+ }
+ else
+ return -1;
+}
+
+long K3bCdparanoiaLibData::lastSector( int track )
+{
+ if( m_drive ) {
+ mutex.lock();
+ long sector = cdda_cdda_track_lastsector(m_drive, track );
+ mutex.unlock();
+ return sector;
+ }
+ else
+ return -1;
+}
+
+
+long K3bCdparanoiaLibData::paranoiaSeek( long sector, int mode )
+{
+ if( m_paranoia ) {
+ mutex.lock();
+ m_currentSector = cdda_paranoia_seek( m_paranoia, sector, mode );
+ mutex.unlock();
+ return m_currentSector;
+ }
+ else
+ return -1;
+}
+
+
+
+class K3bCdparanoiaLib::Private
+{
+public:
+ Private()
+ : device(0),
+ currentSector(0),
+ startSector(0),
+ lastSector(0),
+ status(S_OK),
+ paranoiaLevel(0),
+ neverSkip(true),
+ maxRetries(5),
+ data(0) {
+ }
+
+ ~Private() {
+ }
+
+ void updateParanoiaMode() {
+ // from cdrdao 1.1.7
+ int paranoiaMode = PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP;
+
+ switch( paranoiaLevel ) {
+ case 0:
+ paranoiaMode = PARANOIA_MODE_DISABLE;
+ break;
+
+ case 1:
+ paranoiaMode |= PARANOIA_MODE_OVERLAP;
+ paranoiaMode &= ~PARANOIA_MODE_VERIFY;
+ break;
+
+ case 2:
+ paranoiaMode &= ~(PARANOIA_MODE_SCRATCH|PARANOIA_MODE_REPAIR);
+ break;
+ }
+
+ if( neverSkip )
+ paranoiaMode |= PARANOIA_MODE_NEVERSKIP;
+
+ data->paranoiaModeSet( paranoiaMode );
+ }
+
+ // high-level api
+ K3bDevice::Device* device;
+ K3bDevice::Toc toc;
+ long currentSector;
+ long startSector;
+ long lastSector;
+ int status;
+ unsigned int currentTrack;
+ int paranoiaLevel;
+ bool neverSkip;
+ int maxRetries;
+
+ K3bCdparanoiaLibData* data;
+};
+
+
+K3bCdparanoiaLib::K3bCdparanoiaLib()
+{
+ d = new Private();
+ s_counter++;
+}
+
+
+K3bCdparanoiaLib::~K3bCdparanoiaLib()
+{
+ delete d;
+ s_counter--;
+ if( s_counter == 0 ) {
+ K3bCdparanoiaLibData::freeAll();
+
+ // cleanup the dynamically loaded lib
+ dlclose( s_libInterface );
+ dlclose( s_libParanoia );
+ s_libInterface = 0;
+ s_libParanoia = 0;
+ }
+}
+
+
+bool K3bCdparanoiaLib::load()
+{
+ cdda_cdda_identify = (cdrom_drive* (*) (const char*, int, char**))dlsym( s_libInterface, CDDA_IDENTIFY );
+ cdda_cdda_open = (int (*) (cdrom_drive*))dlsym( s_libInterface, CDDA_OPEN );
+ cdda_cdda_close = (int (*) (cdrom_drive*))dlsym( s_libInterface, CDDA_CLOSE );
+ cdda_cdda_track_firstsector = (long (*)(cdrom_drive*, int))dlsym( s_libInterface, CDDA_TRACK_FIRSTSECTOR );
+ cdda_cdda_track_lastsector = (long (*)(cdrom_drive*, int))dlsym( s_libInterface, CDDA_TRACK_LASTSECTOR );
+ cdda_cdda_verbose_set = (void (*)(cdrom_drive *d,int err_action, int mes_action))dlsym( s_libInterface, CDDA_VERBOSE_SET );
+ cdda_cdda_disc_firstsector = (long (*)(cdrom_drive *d))dlsym( s_libInterface, CDDA_DISC_FIRSTSECTOR );
+
+ cdda_paranoia_init = (cdrom_paranoia* (*)(cdrom_drive*))dlsym( s_libParanoia, PARANOIA_INIT );
+ cdda_paranoia_free = (void (*)(cdrom_paranoia *p))dlsym( s_libParanoia, PARANOIA_FREE );
+ cdda_paranoia_modeset = (void (*)(cdrom_paranoia *p, int mode))dlsym( s_libParanoia, PARANOIA_MODESET );
+ cdda_paranoia_read_limited = (int16_t* (*)(cdrom_paranoia *p, void(*callback)(long,int), int))dlsym( s_libParanoia, PARANOIA_READ_LIMITED );
+ cdda_paranoia_seek = (long (*)(cdrom_paranoia *p,long seek,int mode))dlsym( s_libParanoia, PARANOIA_SEEK );
+
+ // check if all symbols could be resoled
+ if( cdda_cdda_identify == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_identify'" << endl;
+ return false;
+ }
+ if( cdda_cdda_open == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_open'" << endl;
+ return false;
+ }
+ if( cdda_cdda_close == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_close'" << endl;
+ return false;
+ }
+ if( cdda_cdda_track_firstsector == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_track_firstsector'" << endl;
+ return false;
+ }
+ if( cdda_cdda_track_lastsector == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_track_lastsector'" << endl;
+ return false;
+ }
+ if( cdda_cdda_disc_firstsector == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_disc_firstsector'" << endl;
+ return false;
+ }
+ if( cdda_cdda_verbose_set == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'cdda_verbose_set'" << endl;
+ return false;
+ }
+
+ if( cdda_paranoia_init == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_init'" << endl;
+ return false;
+ }
+ if( cdda_paranoia_free == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_free'" << endl;
+ return false;
+ }
+ if( cdda_paranoia_modeset == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_modeset'" << endl;
+ return false;
+ }
+ if( cdda_paranoia_read_limited == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_read_limited'" << endl;
+ return false;
+ }
+ if( cdda_paranoia_seek == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve 'paranoia_seek'" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+
+K3bCdparanoiaLib* K3bCdparanoiaLib::create()
+{
+ // check if libcdda_interface is avalilable
+ if( s_libInterface == 0 ) {
+ s_haveLibCdio = false;
+
+ s_libInterface = dlopen( "libcdda_interface.so.0", RTLD_NOW|RTLD_GLOBAL );
+
+ // try the redhat & Co. location
+ if( s_libInterface == 0 )
+ s_libInterface = dlopen( "cdda/libcdda_interface.so.0", RTLD_NOW|RTLD_GLOBAL );
+
+ // try the new cdio lib
+ if( s_libInterface == 0 ) {
+ s_libInterface = dlopen( "libcdio_cdda.so", RTLD_NOW|RTLD_GLOBAL );
+ s_haveLibCdio = true;
+ }
+
+ if( s_libInterface == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error while loading libcdda_interface. " << endl;
+ return 0;
+ }
+
+
+ s_libParanoia = dlopen( "libcdda_paranoia.so.0", RTLD_NOW );
+
+ // try the redhat & Co. location
+ if( s_libParanoia == 0 )
+ s_libParanoia = dlopen( "cdda/libcdda_paranoia.so.0", RTLD_NOW );
+
+ // try the new cdio lib
+ if( s_haveLibCdio && s_libParanoia == 0 )
+ s_libParanoia = dlopen( "libcdio_paranoia.so.0", RTLD_NOW );
+
+ if( s_libParanoia == 0 ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error while loading libcdda_paranoia. " << endl;
+ dlclose( s_libInterface );
+ s_libInterface = 0;
+ return 0;
+ }
+ }
+
+ K3bCdparanoiaLib* lib = new K3bCdparanoiaLib();
+ if( !lib->load() ) {
+ kdDebug() << "(K3bCdparanoiaLib) Error: could not resolve all symbols!" << endl;
+ delete lib;
+ return 0;
+ }
+ return lib;
+}
+
+
+bool K3bCdparanoiaLib::initParanoia( K3bDevice::Device* dev, const K3bDevice::Toc& toc )
+{
+ if( !dev ) {
+ kdError() << "(K3bCdparanoiaLib::initParanoia) dev = 0!" << endl;
+ return false;
+ }
+
+ close();
+
+ d->device = dev;
+ d->toc = toc;
+ if( d->toc.isEmpty() ) {
+ kdDebug() << "(K3bCdparanoiaLib) empty toc." << endl;
+ cleanup();
+ return false;
+ }
+
+ if( d->toc.contentType() == K3bDevice::DATA ) {
+ kdDebug() << "(K3bCdparanoiaLib) No audio tracks found." << endl;
+ cleanup();
+ return false;
+ }
+
+ //
+ // Get the appropriate data instance for this device
+ //
+ d->data = K3bCdparanoiaLibData::data( dev );
+
+ if( d->data->paranoiaInit() ) {
+ d->startSector = d->currentSector = d->lastSector = 0;
+
+ return true;
+ }
+ else {
+ cleanup();
+ return false;
+ }
+}
+
+
+bool K3bCdparanoiaLib::initParanoia( K3bDevice::Device* dev )
+{
+ return initParanoia( dev, dev->readToc() );
+}
+
+
+void K3bCdparanoiaLib::close()
+{
+ cleanup();
+}
+
+
+void K3bCdparanoiaLib::cleanup()
+{
+ if( d->data )
+ d->data->paranoiaFree();
+ d->device = 0;
+ d->currentSector = 0;
+}
+
+
+bool K3bCdparanoiaLib::initReading()
+{
+ if( d->device ) {
+ // find first audio track
+ K3bDevice::Toc::const_iterator trackIt = d->toc.begin();
+ while( (*trackIt).type() != K3bDevice::Track::AUDIO ) {
+ ++trackIt;
+ }
+
+ long start = (*trackIt).firstSector().lba();
+
+ // find last audio track
+ while( trackIt != d->toc.end() && (*trackIt).type() == K3bDevice::Track::AUDIO )
+ ++trackIt;
+ --trackIt;
+
+ long end = (*trackIt).lastSector().lba();
+
+ return initReading( start, end );
+ }
+ else {
+ kdDebug() << "(K3bCdparanoiaLib) initReading without initParanoia." << endl;
+ return false;
+ }
+}
+
+
+bool K3bCdparanoiaLib::initReading( unsigned int track )
+{
+ if( d->device ) {
+ if( track <= d->toc.count() ) {
+ const K3bDevice::Track& k3bTrack = d->toc[track-1];
+ if( k3bTrack.type() == K3bDevice::Track::AUDIO ) {
+ return initReading( k3bTrack.firstSector().lba(), k3bTrack.lastSector().lba() );
+ }
+ else {
+ kdDebug() << "(K3bCdparanoiaLib) Track " << track << " no audio track." << endl;
+ return false;
+ }
+ }
+ else {
+ kdDebug() << "(K3bCdparanoiaLib) Track " << track << " too high." << endl;
+ return false;
+ }
+ }
+ else {
+ kdDebug() << "(K3bCdparanoiaLib) initReading without initParanoia." << endl;
+ return false;
+ }
+}
+
+
+bool K3bCdparanoiaLib::initReading( long start, long end )
+{
+ kdDebug() << "(K3bCdparanoiaLib) initReading( " << start << ", " << end << " )" << endl;
+
+ if( d->device ) {
+ if( d->toc.firstSector().lba() <= start &&
+ d->toc.lastSector().lba() >= end ) {
+ d->startSector = d->currentSector = start;
+ d->lastSector = end;
+
+ // determine track number
+ d->currentTrack = 1;
+ while( d->toc[d->currentTrack-1].lastSector() < start )
+ d->currentTrack++;
+
+ // let the paranoia stuff point to the startSector
+ d->data->paranoiaSeek( start, SEEK_SET );
+ return true;
+ }
+ else {
+ kdDebug() << "(K3bCdparanoiaLib) " << start << " and " << end << " out of range." << endl;
+ return false;
+ }
+ }
+ else {
+ kdDebug() << "(K3bCdparanoiaLib) initReading without initParanoia." << endl;
+ return false;
+ }
+}
+
+
+char* K3bCdparanoiaLib::read( int* statusCode, unsigned int* track, bool littleEndian )
+{
+ if( d->currentSector > d->lastSector ) {
+ kdDebug() << "(K3bCdparanoiaLib) finished ripping. read "
+ << (d->currentSector - d->startSector) << " sectors." << endl
+ << " current sector: " << d->currentSector << endl;
+ d->status = S_OK;
+ if( statusCode )
+ *statusCode = d->status;
+ return 0;
+ }
+
+ if( d->currentSector != d->data->sector() ) {
+ kdDebug() << "(K3bCdparanoiaLib) need to seek before read. Looks as if we are reusing the paranoia instance." << endl;
+ if( !d->data->paranoiaSeek( d->currentSector, SEEK_SET ) )
+ return 0;
+ }
+
+ //
+ // The paranoia data could have been used by someone else before
+ // and setting the paranoia mode is fast
+ //
+ d->updateParanoiaMode();
+
+ Q_INT16* data = d->data->paranoiaRead( paranoiaCallback, d->maxRetries );
+
+ char* charData = reinterpret_cast<char*>(data);
+
+#ifdef WORDS_BIGENDIAN // __BYTE_ORDER == __BIG_ENDIAN
+ if( littleEndian ) {
+#else
+ if( !littleEndian ) {
+#endif
+ for( int i = 0; i < CD_FRAMESIZE_RAW-1; i+=2 ) {
+ char b = charData[i];
+ charData[i] = charData[i+1];
+ charData[i+1] = b;
+ }
+ }
+
+
+ if( data )
+ d->status = S_OK;
+ else
+ d->status = S_ERROR; // We may skip this sector if we'd like...
+
+ if( statusCode )
+ *statusCode = d->status;
+
+ if( track )
+ *track = d->currentTrack;
+
+ d->currentSector++;
+
+ if( d->toc[d->currentTrack-1].lastSector() < d->currentSector )
+ d->currentTrack++;
+
+ return charData;
+}
+
+
+int K3bCdparanoiaLib::status() const
+{
+ return d->status;
+}
+
+
+const K3bDevice::Toc& K3bCdparanoiaLib::toc() const
+{
+ return d->toc;
+}
+
+
+long K3bCdparanoiaLib::rippedDataLength() const
+{
+ return d->lastSector - d->startSector + 1;
+}
+
+
+void K3bCdparanoiaLib::setParanoiaMode( int m )
+{
+ d->paranoiaLevel = m;
+}
+
+
+void K3bCdparanoiaLib::setNeverSkip( bool b )
+{
+ d->neverSkip = b;
+}
+
+
+void K3bCdparanoiaLib::setMaxRetries( int r )
+{
+ d->maxRetries = r;
+}