diff --git a/libk3b/core/k3bprogressinfoevent.h b/libk3b/core/k3bprogressinfoevent.h index aceae7640..8499eec6e 100644 --- a/libk3b/core/k3bprogressinfoevent.h +++ b/libk3b/core/k3bprogressinfoevent.h @@ -1,86 +1,86 @@ /* * * Copyright (C) 2003-2009 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2009 Sebastian Trueg * * 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. */ #ifndef K3B_PROGRESS_INFO_EVENT_H #define K3B_PROGRESS_INFO_EVENT_H #include #include namespace K3b { /** * Custom event class for posting events corresponding to the * Job signals. This is useful for a threaded job since * in that case it's not possible to emit signals that directly * change the GUI (see QThread docu). */ class ProgressInfoEvent : public QEvent { public: - ProgressInfoEvent( int type ) + explicit ProgressInfoEvent( int type ) : QEvent( QEvent::User ), m_type(type) {} ProgressInfoEvent( int type, const QString& v1, const QString& v2 = QString(), int value1 = 0, int value2 = 0 ) : QEvent( QEvent::User ), m_type( type), m_firstValue(value1), m_secondValue(value2), m_firstString(v1), m_secondString(v2) {} ProgressInfoEvent( int type, int value1, int value2 = 0 ) : QEvent( QEvent::User ), m_type( type), m_firstValue(value1), m_secondValue(value2) {} int type() const { return m_type; } const QString& firstString() const { return m_firstString; } const QString& secondString() const { return m_secondString; } int firstValue() const { return m_firstValue; } int secondValue() const { return m_secondValue; } enum ProgressInfoEventType { Progress = QEvent::User + 1, SubProgress, ProcessedSize, ProcessedSubSize, InfoMessage, Started, Canceled, Finished, NewTask, NewSubTask, DebuggingOutput, BufferStatus, WriteSpeed, NextTrack }; private: int m_type; int m_firstValue; int m_secondValue; QString m_firstString; QString m_secondString; }; } #endif diff --git a/libk3b/core/k3bthreadjob.h b/libk3b/core/k3bthreadjob.h index e481239c2..7d99b2998 100644 --- a/libk3b/core/k3bthreadjob.h +++ b/libk3b/core/k3bthreadjob.h @@ -1,135 +1,135 @@ /* * * Copyright (C) 2003-2010 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2010 Sebastian Trueg * * 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. */ #ifndef _K3B_THREAD_JOB_H_ #define _K3B_THREAD_JOB_H_ #include "k3bjob.h" #include "k3b_export.h" #include namespace K3b { class Thread; /** * A Job that runs in a different thread. Instead of reimplementing * start() reimplement run() to perform all operations in a different * thread. Otherwise usage is the same as Job. */ class LIBK3B_EXPORT ThreadJob : public Job { Q_OBJECT public: - ThreadJob( JobHandler*, QObject* parent = 0 ); + explicit ThreadJob( JobHandler*, QObject* parent = 0 ); virtual ~ThreadJob(); /** * \reimplemented from Job * * \return true if the job has been started and has not yet * emitted the finished signal */ virtual bool active() const; /** * reimplemented from JobHandler */ virtual Device::MediaType waitForMedium( Device::Device*, Device::MediaStates mediaState = Device::STATE_EMPTY, Device::MediaTypes mediaType = Device::MEDIA_WRITABLE_CD, const K3b::Msf& minMediaSize = K3b::Msf(), const QString& message = QString() ); /** * reimplemented from JobHandler */ virtual bool questionYesNo( const QString& text, const QString& caption = QString(), const KGuiItem& buttonYes = KStandardGuiItem::yes(), const KGuiItem& buttonNo = KStandardGuiItem::no() ); /** * reimplemented from JobHandler */ virtual void blockingInformation( const QString& text, const QString& caption = QString() ); /** * Call QThread::wait() on job's thread * \see QThread::wait() */ bool wait( unsigned long time = ULONG_MAX ); public Q_SLOTS: /** * Starts the job in a different thread. Emits the started() * signal. * * When reimplementing this method to perform housekeeping * operations in the GUI thread make sure to call the * parent implementation. * * \sa run() */ virtual void start(); /** * Cancel the job. The method will give the thread a certain * time to actually cancel. After that the thread is terminated. * * \sa canceled() */ virtual void cancel(); protected: /** * Implement this method to do the actual work in the thread. * Do not emit started(), finished(), and canceled() signals * in this method. ThreadJob will do that automatically. * * \return \p true on success. */ virtual bool run() = 0; /** * Use to check if the job has been canceled. * \sa cancel() */ bool canceled() const; private Q_SLOTS: /** * Called in the GUi thread once the job is done. * Emits the finished signal and performs some * housekeeping. */ void slotThreadFinished(); private: void customEvent( QEvent* ); class Private; Private* const d; friend class Thread; }; } #endif diff --git a/libk3b/projects/audiocd/k3baudiotrackreader.h b/libk3b/projects/audiocd/k3baudiotrackreader.h index 3776c5208..4e8e0896a 100644 --- a/libk3b/projects/audiocd/k3baudiotrackreader.h +++ b/libk3b/projects/audiocd/k3baudiotrackreader.h @@ -1,61 +1,61 @@ /* * * Copyright (C) 2010 Michal Malek * * This file is part of the K3b project. * Copyright (C) 1998-2010 Sebastian Trueg * * 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. */ #ifndef _K3B_AUDIO_TRACK_READER_H_ #define _K3B_AUDIO_TRACK_READER_H_ #include "k3b_export.h" #include #include namespace K3b { class AudioTrack; class LIBK3B_EXPORT AudioTrackReader : public QIODevice { Q_OBJECT public: - AudioTrackReader( AudioTrack& track, QObject* parent = 0 ); + explicit AudioTrackReader( AudioTrack& track, QObject* parent = 0 ); ~AudioTrackReader(); const AudioTrack& track() const; AudioTrack& track(); virtual bool open( OpenMode mode = QIODevice::ReadOnly ); virtual void close(); virtual bool isSequential() const; virtual qint64 size() const; virtual bool seek( qint64 pos ); protected: virtual qint64 writeData( const char* data, qint64 len ); virtual qint64 readData( char* data, qint64 maxlen ); private Q_SLOTS: void slotTrackChanged(); private: class Private; QScopedPointer d; Q_DISABLE_COPY(AudioTrackReader) Q_PRIVATE_SLOT( d, void slotSourceAdded( int position ) ) Q_PRIVATE_SLOT( d, void slotSourceAboutToBeRemoved( int position ) ) }; } // namespace K3b #endif // _K3B_AUDIO_TRACK_READER_H_ diff --git a/libk3b/projects/audiocd/k3baudiozerodatareader.h b/libk3b/projects/audiocd/k3baudiozerodatareader.h index 1cef3556a..b8c9e77e4 100644 --- a/libk3b/projects/audiocd/k3baudiozerodatareader.h +++ b/libk3b/projects/audiocd/k3baudiozerodatareader.h @@ -1,50 +1,50 @@ /* * * Copyright (C) 2010 Michal Malek * * This file is part of the K3b project. * Copyright (C) 1998-2010 Sebastian Trueg * * 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. */ #ifndef K3B_AUDIOZERODATAREADER_H #define K3B_AUDIOZERODATAREADER_H #include "k3b_export.h" #include #include namespace K3b { class AudioZeroData; class LIBK3B_EXPORT AudioZeroDataReader : public QIODevice { public: - AudioZeroDataReader( AudioZeroData& source, QObject* parent = 0 ); + explicit AudioZeroDataReader( AudioZeroData& source, QObject* parent = 0 ); ~AudioZeroDataReader(); virtual bool open( OpenMode mode ); virtual bool isSequential() const; virtual qint64 size() const; protected: virtual qint64 writeData(const char* data, qint64 len); virtual qint64 readData(char* data, qint64 maxlen); private: class Private; QScopedPointer d; Q_DISABLE_COPY(AudioZeroDataReader) }; } #endif // K3B_AUDIOZERODATAREADER_H diff --git a/libk3b/tools/k3bintvalidator.h b/libk3b/tools/k3bintvalidator.h index 7829e8a3a..c314deb7e 100644 --- a/libk3b/tools/k3bintvalidator.h +++ b/libk3b/tools/k3bintvalidator.h @@ -1,85 +1,85 @@ /* * * Copyright (C) 2004 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2007 Sebastian Trueg * * 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. */ #ifndef _K3B_INT_VALIDATOR_H_ #define _K3B_INT_VALIDATOR_H_ #include "k3b_export.h" #include class QWidget; class QString; namespace K3b { /** * QValidator for integers. * * It differs from QIntValidator and KIntValidator in the fact that * it also accepts hex numbers prefixed with 0x. */ class LIBK3B_EXPORT IntValidator : public QValidator { public: /** * Constructor. Also sets the base value. */ - IntValidator ( QWidget * parent ); + explicit IntValidator ( QWidget * parent ); /** * Constructor. Also sets the minimum, maximum, and numeric base values. */ IntValidator ( int bottom, int top, QWidget * parent ); /** * Destructs the validator. */ virtual ~IntValidator (); /** * Validates the text, and return the result. Does not modify the parameters. */ virtual State validate ( QString &, int & ) const; /** * Fixes the text if possible, providing a valid string. The parameter may be modified. */ virtual void fixup ( QString & ) const; /** * Sets the minimum and maximum values allowed. */ virtual void setRange ( int bottom, int top ); /** * Returns the current minimum value allowed. */ virtual int bottom () const; /** * Returns the current maximum value allowed. */ virtual int top () const; /** * If the string starts with 0x it's assumed to be a hex number. */ static int toInt( const QString&, bool* ok = 0 ); private: int m_min; int m_max; }; } #endif diff --git a/libk3b/tools/k3biso9660.cpp b/libk3b/tools/k3biso9660.cpp index d6f085c7c..772becb13 100644 --- a/libk3b/tools/k3biso9660.cpp +++ b/libk3b/tools/k3biso9660.cpp @@ -1,914 +1,914 @@ /* * * Copyright (C) 2003-2008 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2008 Sebastian Trueg * * 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 #include "k3bglobals.h" #include "k3biso9660.h" #include "k3biso9660backend.h" #include "k3bdevice.h" #include "libisofs/isofs.h" #include #include #include /* callback function for libisofs */ int K3b::Iso9660::read_callback( char* buf, sector_t start, int len, void* udata ) { K3b::Iso9660* isoF = static_cast(udata); return isoF->read( start, buf, len ); } /* callback function for libisofs */ int K3b::Iso9660::isofs_callback( struct iso_directory_record *idr, void *udata ) { K3b::Iso9660 *iso = static_cast (udata); QString path, isoPath,user,group,symlink; int i; int access; int time,cdate,adate; rr_entry rr; bool special=false; K3b::Iso9660Entry *entry=0; //K3b::Iso9660Entry *oldentry=0; char z_algo[2],z_params[2]; int z_size=0; if (isonum_711(idr->name_len)==1) { switch (idr->name[0]) { case 0: path+=("."); special=true; break; case 1: path+=(".."); special=true; break; } } // // First extract the raw iso9660 name // if( !special ) { - for( i = 0; i < isonum_711( idr->name_len ); i++ ) { + for( i = 0; i < isonum_711( idr->name_len ); ++i ) { if( idr->name[i] ) isoPath += idr->name[i]; } } else isoPath = path; // // Now see if we have RockRidge // if( !iso->plainIso9660() && ParseRR(idr,&rr) > 0 ) { iso->m_rr = true; if (!special) path = QString::fromLocal8Bit( rr.name ); symlink=rr.sl; access=rr.mode; time=0;//rr.st_mtime; adate=0;//rr.st_atime; cdate=0;//rr.st_ctime; user.setNum(rr.uid); group.setNum(rr.gid); z_algo[0]=rr.z_algo[0];z_algo[1]=rr.z_algo[1]; z_params[0]=rr.z_params[0];z_params[1]=rr.z_params[1]; z_size=rr.z_size; } else { access=iso->dirent->permissions() & ~S_IFMT; adate=cdate=time=isodate_915(idr->date,0); user=iso->dirent->user(); group=iso->dirent->group(); if (idr->flags[0] & 2) access |= S_IFDIR; else access |= S_IFREG; if (!special) { if( !iso->plainIso9660() && iso->jolietLevel() ) { for (i=0;i<(isonum_711(idr->name_len)-1);i+=2) { QChar ch( be2me_16(*((ushort*)&(idr->name[i]))) ); if (ch==';') break; path+=ch; } } else { // no RR, no Joliet, just plain iso9660 path = isoPath; // remove the version field int pos = path.indexOf( ';' ); if( pos > 0 ) path.truncate( pos ); } if (path.endsWith('.')) path.truncate(path.length()-1); } } if( !iso->plainIso9660() ) FreeRR(&rr); if (idr->flags[0] & 2) { entry = new K3b::Iso9660Directory( iso, isoPath, path, access | S_IFDIR, time, adate, cdate, user, group, symlink, special ? 0 : isonum_733(idr->extent), special ? 0 : isonum_733(idr->size) ); } else { entry = new K3b::Iso9660File( iso, isoPath, path, access, time, adate, cdate, user, group, symlink, isonum_733(idr->extent), isonum_733(idr->size) ); if (z_size) (static_cast(entry))->setZF( z_algo, z_params, z_size ); } iso->dirent->addEntry(entry); return 0; } K3b::Iso9660Entry::Iso9660Entry( K3b::Iso9660* archive, const QString& isoName, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink ) : m_adate( adate ), m_cdate( cdate ), m_name( name ), m_isoName( isoName ), m_date( date ), m_access( access ), m_user( user ), m_group( group ), m_symlink( symlink ), m_archive( archive ) { } K3b::Iso9660Entry::~Iso9660Entry() { } K3b::Iso9660File::Iso9660File( K3b::Iso9660* archive, const QString& isoName, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink, unsigned int pos, unsigned int size ) : K3b::Iso9660Entry( archive, isoName, name, access, date, adate, cdate, user, group, symlink ), m_startSector(pos), m_size(size) { m_algo[0] = 0; m_algo[1] = 0; m_parms[0] = 0; m_parms[1] = 0; m_realsize = 0; } K3b::Iso9660File::~Iso9660File() { } void K3b::Iso9660File::setZF(char algo[2],char parms[2],int realsize) { m_algo[0]=algo[0];m_algo[1]=algo[1]; m_parms[0]=parms[0];m_parms[1]=parms[1]; m_realsize=realsize; } int K3b::Iso9660File::read( unsigned int pos, char* data, int maxlen ) const { if( pos >= size() ) return 0; unsigned long startSec = m_startSector + pos/2048; int startSecOffset = pos%2048; char* buffer = data; bool buffered = false; unsigned long bufferLen = maxlen+startSecOffset; // cut to size if( pos + maxlen > size() ) bufferLen = size() - pos + startSecOffset; // pad to 2048 if( bufferLen%2048 ) bufferLen += (2048-(bufferLen%2048)); // we need to buffer if we changed the startSec or need a bigger buffer if( startSecOffset || bufferLen > (unsigned int)maxlen ) { buffered = true; buffer = new char[bufferLen]; } int read = archive()->read( startSec, buffer, bufferLen/2048 )*2048; if( buffered ) { if( read > 0 ) { // cut to requested data read -= startSecOffset; if( read + pos > size() ) read = size() - pos; if( read > maxlen ) read = maxlen; ::memcpy( data, buffer+startSecOffset, read ); } delete [] buffer; return read; } else { // cut read data if( read + pos > size() ) read = size() - pos; return read; } } bool K3b::Iso9660File::copyTo( const QString& url ) const { QFile of( url ); if( of.open( QIODevice::WriteOnly ) ) { char buffer[2048*10]; unsigned int pos = 0; int r = 0; while( ( r = read( pos, buffer, 2048*10 ) ) > 0 ) { of.write( buffer, r ); pos += r; } return !r; } else { qDebug() << "(K3b::Iso9660File) could not open " << url << " for writing."; return false; } } K3b::Iso9660Directory::Iso9660Directory( K3b::Iso9660* archive, const QString& isoName, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink, unsigned int pos, unsigned int size ) : K3b::Iso9660Entry( archive, isoName, name, access, date, adate, cdate, user, group, symlink ), m_bExpanded( size == 0 ), // we can only expand entries that represent an actual directory m_startSector(pos), m_size(size) { } K3b::Iso9660Directory::~Iso9660Directory() { qDeleteAll( m_entries ); } void K3b::Iso9660Directory::expand() { if( !m_bExpanded ) { archive()->dirent = this; if( ProcessDir( &K3b::Iso9660::read_callback, m_startSector, m_size, &K3b::Iso9660::isofs_callback, archive() ) ) qDebug() << "(K3b::Iso9660) failed to expand dir: " << name() << " with size: " << m_size; m_bExpanded = true; } } QStringList K3b::Iso9660Directory::entries() const { // create a fake const method to fool the user ;) const_cast(this)->expand(); QStringList l; QHashIterator it( m_entries ); while ( it.hasNext() ) { it.next(); l.append( it.key() ); } return l; } QStringList K3b::Iso9660Directory::iso9660Entries() const { // create a fake const method to fool the user ;) const_cast(this)->expand(); QStringList l; QHashIterator it( m_iso9660Entries ); while ( it.hasNext() ) { it.next(); l.append( it.key() ); } return l; } K3b::Iso9660Entry* K3b::Iso9660Directory::entry( const QString& n ) { if( n.isEmpty() ) return 0; expand(); QString name(n); // trailing slash ? -> remove if( name.length() > 1 && name[name.length()-1] == '/' ) { name.truncate( name.length()-1 ); } int pos = name.indexOf( '/' ); while( pos == 0 ) { if( name.length() > 1 ) { name = name.mid( 1 ); // remove leading slash pos = name.indexOf( '/' ); // look again } else // "/" return this; } if ( pos != -1 ) { QString left = name.left( pos ); QString right = name.mid( pos + 1 ); K3b::Iso9660Entry* e = m_entries[ left ]; if ( !e || !e->isDirectory() ) return 0; return static_cast(e)->entry( right ); } return m_entries[ name ]; } K3b::Iso9660Entry* K3b::Iso9660Directory::iso9660Entry( const QString& n ) { if( n.isEmpty() ) return 0; expand(); QString name(n); // trailing slash ? -> remove if( name.length() > 1 && name[name.length()-1] == '/' ) { name.truncate( name.length()-1 ); } int pos = name.indexOf( '/' ); while( pos == 0 ) { if( name.length() > 1 ) { name = name.mid( 1 ); // remove leading slash pos = name.indexOf( '/' ); // look again } else // "/" return this; } if ( pos != -1 ) { QString left = name.left( pos ); QString right = name.mid( pos + 1 ); K3b::Iso9660Entry* e = m_iso9660Entries[ left ]; if ( !e || !e->isDirectory() ) return 0; return static_cast(e)->iso9660Entry( right ); } return m_iso9660Entries[ name ]; } const K3b::Iso9660Entry* K3b::Iso9660Directory::entry( const QString& name ) const { return const_cast(this)->entry( name ); } const K3b::Iso9660Entry* K3b::Iso9660Directory::iso9660Entry( const QString& name ) const { return const_cast(this)->iso9660Entry( name ); } void K3b::Iso9660Directory::addEntry( K3b::Iso9660Entry* entry ) { m_entries.insert( entry->name(), entry ); m_iso9660Entries.insert( entry->isoName(), entry ); } class K3b::Iso9660::Private { public: Private() : cdDevice(0), fd(-1), isOpen(false), startSector(0), plainIso9660(false), backend(0) { } QList elToritoDirs; QList jolietDirs; QList isoDirs; QList rrDirs; // RockRidge K3b::Iso9660SimplePrimaryDescriptor primaryDesc; K3b::Device::Device* cdDevice; int fd; bool isOpen; // only used for direkt K3b::Device::Device access unsigned int startSector; bool plainIso9660; K3b::Iso9660Backend* backend; }; K3b::Iso9660::Iso9660( const QString& filename ) : m_filename( filename ) { d = new Private(); } K3b::Iso9660::Iso9660( int fd ) { d = new Private(); d->fd = fd; } K3b::Iso9660::Iso9660( K3b::Iso9660Backend* backend ) { d = new Private(); d->backend = backend; } K3b::Iso9660::Iso9660( K3b::Device::Device* dev, unsigned int startSector ) { d = new Private(); d->cdDevice = dev; d->startSector = startSector; } K3b::Iso9660::~Iso9660() { close(); delete d->backend; delete d; } void K3b::Iso9660::setStartSector( unsigned int startSector ) { d->startSector = startSector; } void K3b::Iso9660::setPlainIso9660( bool b ) { d->plainIso9660 = b; } bool K3b::Iso9660::plainIso9660() const { return d->plainIso9660; } int K3b::Iso9660::read( unsigned int sector, char* data, int count ) { if( count == 0 ) return 0; else return d->backend->read( sector, data, count ); } void K3b::Iso9660::addBoot(struct el_torito_boot_descriptor* bootdesc) { int i,size; boot_head boot; boot_entry *be; QString path; K3b::Iso9660File *entry; entry=new K3b::Iso9660File( this, "Catalog", "Catalog", dirent->permissions() & ~S_IFDIR, dirent->date(), dirent->adate(), dirent->cdate(), dirent->user(), dirent->group(), QString(), isonum_731(bootdesc->boot_catalog), 2048 ); dirent->addEntry(entry); if (!ReadBootTable(&K3b::Iso9660::read_callback,isonum_731(bootdesc->boot_catalog),&boot,this)) { i=1; be=boot.defentry; while (be) { size=BootImageSize(&K3b::Iso9660::read_callback, isonum_711(((struct default_entry*) be->data)->media), isonum_731(((struct default_entry*) be->data)->start), isonum_721(((struct default_entry*) be->data)->seccount), this); path="Default Image"; if (i>1) path += " (" + QString::number(i) + ')'; entry=new K3b::Iso9660File( this, path, path, dirent->permissions() & ~S_IFDIR, dirent->date(), dirent->adate(), dirent->cdate(), dirent->user(), dirent->group(), QString(), isonum_731(((struct default_entry*) be->data)->start), size<<9 ); dirent->addEntry(entry); be=be->next; i++; } FreeBootTable(&boot); } } bool K3b::Iso9660::open() { if( d->isOpen ) return true; if( !d->backend ) { // create a backend if( !m_filename.isEmpty() ) d->backend = new K3b::Iso9660FileBackend( m_filename ); else if( d->fd > 0 ) d->backend = new K3b::Iso9660FileBackend( d->fd ); else if( d->cdDevice ) { // now check if we have a scrambled video dvd if( d->cdDevice->copyrightProtectionSystemType() == K3b::Device::COPYRIGHT_PROTECTION_CSS ) { qDebug() << "(K3b::Iso9660) found encrypted dvd. using libdvdcss."; // open the libdvdcss stuff d->backend = new K3b::Iso9660LibDvdCssBackend( d->cdDevice ); if( !d->backend->open() ) { // fallback to devicebackend delete d->backend; d->backend = new K3b::Iso9660DeviceBackend( d->cdDevice ); } } else d->backend = new K3b::Iso9660DeviceBackend( d->cdDevice ); } else return false; } d->isOpen = d->backend->open(); if( !d->isOpen ) return false; iso_vol_desc *desc; QString path,tmp,uid,gid; k3b_struct_stat buf; int access,c_i,c_j; struct el_torito_boot_descriptor* bootdesc; // TODO implement win32 support /* We'll use the permission and user/group of the 'host' file except * in Rock Ridge, where the permissions are stored on the file system */ if ( k3b_stat( QFile::encodeName(m_filename), &buf ) < 0 ) { /* defaults, if stat fails */ memset(&buf,0,sizeof(k3b_struct_stat)); buf.st_mode=0777; } uid.setNum(buf.st_uid); gid.setNum(buf.st_gid); access = buf.st_mode & ~S_IFMT; int c_b=1; c_i=1;c_j=1; desc = ReadISO9660( &K3b::Iso9660::read_callback, d->startSector, this ); if (!desc) { qDebug() << "K3b::Iso9660::openArchive no volume descriptors"; close(); return false; } while (desc) { m_rr = false; switch (isonum_711(desc->data.type)) { case ISO_VD_BOOT: bootdesc=(struct el_torito_boot_descriptor*) &(desc->data); if( !memcmp( EL_TORITO_ID, bootdesc->system_id, ISODCL(8,39) ) ) { path="El Torito Boot"; if( c_b > 1 ) path += " (" + QString::number(c_b) + ')'; dirent = new K3b::Iso9660Directory( this, path, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString() ); d->elToritoDirs.append( dirent ); addBoot(bootdesc); c_b++; } break; case ISO_VD_PRIMARY: createSimplePrimaryDesc( (struct iso_primary_descriptor*)&desc->data ); // fall through case ISO_VD_SUPPLEMENTARY: { struct iso_primary_descriptor* primaryDesc = (struct iso_primary_descriptor*)&desc->data; struct iso_directory_record* idr = (struct iso_directory_record*)&primaryDesc->root_directory_record; m_joliet = JolietLevel(&desc->data); // skip joliet in plain iso mode if( m_joliet && plainIso9660() ) break; if (m_joliet) { path = "Joliet level " + QString::number(m_joliet); if( c_j > 1 ) path += " (" + QString::number(c_j) + ')'; } else { path = QString::fromLocal8Bit( primaryDesc->volume_id, 32 ); if( c_i > 1 ) path += " (" + QString::number(c_i) + ')'; } dirent = new K3b::Iso9660Directory( this, path, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString() ); // expand the root entry ProcessDir( &K3b::Iso9660::read_callback, isonum_733(idr->extent),isonum_733(idr->size),&K3b::Iso9660::isofs_callback,this); if (m_joliet) c_j++; else c_i++; if( m_joliet ) d->jolietDirs.append( dirent ); else { if( m_rr ) d->rrDirs.append( dirent ); d->isoDirs.append( dirent ); } break; } } desc = desc->next; } FreeISO9660(desc); return true; } bool K3b::Iso9660::isOpen() const { return d->isOpen; } void K3b::Iso9660::createSimplePrimaryDesc( struct iso_primary_descriptor* desc ) { d->primaryDesc.volumeId = QString::fromLocal8Bit( desc->volume_id, 32 ).trimmed(); d->primaryDesc.systemId = QString::fromLocal8Bit( desc->system_id, 32 ).trimmed(); d->primaryDesc.volumeSetId = QString::fromLocal8Bit( desc->volume_set_id, 128 ).trimmed(); d->primaryDesc.publisherId = QString::fromLocal8Bit( desc->publisher_id, 128 ).trimmed(); d->primaryDesc.preparerId = QString::fromLocal8Bit( desc->preparer_id, 128 ).trimmed(); d->primaryDesc.applicationId = QString::fromLocal8Bit( desc->application_id, 128 ).trimmed(); d->primaryDesc.volumeSetSize = isonum_723(desc->volume_set_size); d->primaryDesc.volumeSetNumber = isonum_723(desc->volume_set_size); d->primaryDesc.logicalBlockSize = isonum_723(desc->logical_block_size); d->primaryDesc.volumeSpaceSize = isonum_733(desc->volume_space_size); } void K3b::Iso9660::close() { if( d->isOpen ) { d->backend->close(); // Since the first isoDir is the KArchive // root we must not delete it but all the // others. qDeleteAll( d->elToritoDirs ); qDeleteAll( d->jolietDirs ); qDeleteAll( d->isoDirs ); d->elToritoDirs.clear(); d->jolietDirs.clear(); d->isoDirs.clear(); d->isOpen = false; } } const K3b::Iso9660Directory* K3b::Iso9660::firstJolietDirEntry() const { if ( !d->jolietDirs.isEmpty() ) return d->jolietDirs.first(); else return 0; } const K3b::Iso9660Directory* K3b::Iso9660::firstIsoDirEntry() const { if ( !d->isoDirs.isEmpty() ) return d->isoDirs.first(); else return 0; } const K3b::Iso9660Directory* K3b::Iso9660::firstElToritoEntry() const { if ( !d->elToritoDirs.isEmpty() ) return d->elToritoDirs.first(); else return 0; } const K3b::Iso9660Directory* K3b::Iso9660::firstRRDirEntry() const { if ( !d->rrDirs.isEmpty() ) return d->rrDirs.first(); else return 0; } const K3b::Iso9660SimplePrimaryDescriptor& K3b::Iso9660::primaryDescriptor() const { return d->primaryDesc; } void K3b::Iso9660::debug() const { if( isOpen() ) { qDebug() << "System Id: " << primaryDescriptor().systemId; qDebug() << "Volume Id: " << primaryDescriptor().volumeId; qDebug() << "Volume Set Id: " << primaryDescriptor().volumeSetId; qDebug() << "Preparer Id: " << primaryDescriptor().preparerId; qDebug() << "Publisher Id: " << primaryDescriptor().publisherId; qDebug() << "Application Id: " << primaryDescriptor().applicationId; qDebug() << "Volume Set Size: " << primaryDescriptor().volumeSetSize; qDebug() << "Volume Set Number: " << primaryDescriptor().volumeSetNumber; if( firstIsoDirEntry() ) { qDebug() << "First ISO Dir entry:"; qDebug() << "----------------------------------------------"; debugEntry( firstIsoDirEntry(), 0 ); qDebug() << "----------------------------------------------"; } if( firstRRDirEntry() ) { qDebug() << "First RR Dir entry:"; qDebug() << "----------------------------------------------"; debugEntry( firstRRDirEntry(), 0 ); qDebug() << "----------------------------------------------"; } if( firstJolietDirEntry() ) { qDebug() << "First Joliet Dir entry:"; qDebug() << "----------------------------------------------"; debugEntry( firstJolietDirEntry(), 0 ); qDebug() << "----------------------------------------------"; } } } void K3b::Iso9660::debugEntry( const K3b::Iso9660Entry* entry, int depth ) const { if( !entry ) { qDebug() << "(K3b::Iso9660::debugEntry) null entry."; return; } QString spacer; spacer.fill( ' ', depth*3 ); qDebug() << spacer << "- " << entry->name() << " (" << entry->isoName() << ")"; if( entry->isDirectory() ) { const K3b::Iso9660Directory* dir = dynamic_cast(entry); const QStringList entries = dir->entries(); for( QStringList::const_iterator it = entries.constBegin(); it != entries.constEnd(); ++it ) { debugEntry( dir->entry( *it ), depth+1 ); } } } K3b::Iso9660SimplePrimaryDescriptor::Iso9660SimplePrimaryDescriptor() : volumeSetSize(0), volumeSetNumber(0), logicalBlockSize(0), volumeSpaceSize(0) { } bool K3b::operator==( const K3b::Iso9660SimplePrimaryDescriptor& d1, const K3b::Iso9660SimplePrimaryDescriptor& d2 ) { return( d1.volumeId == d2.volumeId && d1.systemId == d2.systemId && d1.volumeSetId == d2.volumeSetId && d1.publisherId == d2.publisherId && d1.preparerId == d2.preparerId && d1.applicationId == d2.applicationId && d1.volumeSetSize == d2.volumeSetSize && d1.volumeSetNumber == d2.volumeSetNumber && d1.logicalBlockSize == d2.logicalBlockSize && d1.volumeSpaceSize == d2.volumeSpaceSize ); } bool K3b::operator!=( const K3b::Iso9660SimplePrimaryDescriptor& d1, const K3b::Iso9660SimplePrimaryDescriptor& d2 ) { return( d1.volumeId != d2.volumeId || d1.systemId != d2.systemId || d1.volumeSetId != d2.volumeSetId || d1.publisherId != d2.publisherId || d1.preparerId != d2.preparerId || d1.applicationId != d2.applicationId || d1.volumeSetSize != d2.volumeSetSize || d1.volumeSetNumber != d2.volumeSetNumber || d1.logicalBlockSize != d2.logicalBlockSize || d1.volumeSpaceSize != d2.volumeSpaceSize ); } diff --git a/libk3b/tools/k3biso9660.h b/libk3b/tools/k3biso9660.h index 1b784d4d0..ac804d791 100644 --- a/libk3b/tools/k3biso9660.h +++ b/libk3b/tools/k3biso9660.h @@ -1,453 +1,453 @@ /* * * Copyright (C) 2003-2009 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2009 Sebastian Trueg * * 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. */ #ifndef _K3B_ISO9660_H_ #define _K3B_ISO9660_H_ #include "k3b_export.h" #include #include #include #include #include #include struct iso_directory_record; struct el_torito_boot_descriptor; struct iso_primary_descriptor; typedef long sector_t; namespace K3b { namespace Device { class Device; } class Iso9660; class Iso9660Backend; /** * Simplyfied primary descriptor which just contains the fields * used by K3b. */ class LIBK3B_EXPORT Iso9660SimplePrimaryDescriptor { public: /** * Creates an empty descriptor */ Iso9660SimplePrimaryDescriptor(); QString volumeId; QString systemId; QString volumeSetId; QString publisherId; QString preparerId; QString applicationId; int volumeSetSize; int volumeSetNumber; long logicalBlockSize; long long volumeSpaceSize; }; LIBK3B_EXPORT bool operator==( const Iso9660SimplePrimaryDescriptor& d1, const Iso9660SimplePrimaryDescriptor& d2 ); LIBK3B_EXPORT bool operator!=( const Iso9660SimplePrimaryDescriptor& d1, const Iso9660SimplePrimaryDescriptor& d2 ); /** * Base class for all entries in a Iso9660 archive. A lot has been copied * from KArchive. */ class LIBK3B_EXPORT Iso9660Entry { public: Iso9660Entry( Iso9660* archive, const QString& isoName, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink ); virtual ~Iso9660Entry(); int adate() const { return m_adate; } int cdate() const { return m_cdate; } /** * Creation date of the file. * @return the creation date */ QDateTime datetime() const; /** * Creation date of the file. * @return the creation date in seconds since 1970 */ int date() const { return m_date; } /** * Name of the file without path. * @return The file name without path. */ QString name() const { return m_name; } /** * \return The raw name as saved in the ISO9660 tree */ QString isoName() const { return m_isoName; } /** * The permissions and mode flags as returned by the stat() function * in st_mode. * @return the permissions */ mode_t permissions() const { return m_access; } /** * User who created the file. * @return the owner of the file */ QString user() const { return m_user; } /** * Group of the user who created the file. * @return the group of the file */ QString group() const { return m_group; } /** * Symlink if there is one. * @return the symlink, or QString() */ QString symlink() const { return m_symlink; } /** * Checks whether the entry is a file. * @return true if this entry is a file */ virtual bool isFile() const { return false; } /** * Checks whether the entry is a directory. * @return true if this entry is a directory */ virtual bool isDirectory() const { return false; } Iso9660* archive() const { return m_archive; } private: int m_adate; int m_cdate; QString m_name; QString m_isoName; int m_date; mode_t m_access; QString m_user; QString m_group; QString m_symlink; Iso9660* m_archive; }; class LIBK3B_EXPORT Iso9660Directory : public Iso9660Entry { public: Iso9660Directory( Iso9660* archive, const QString& isoName, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink, unsigned int pos = 0, unsigned int size = 0 ); ~Iso9660Directory(); /** * Returns a list of sub-entries. * @return the names of all entries in this directory (filenames, no path). */ QStringList entries() const; /** * Returns the entry with the given name. * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. * @return a pointer to the entry in the directory. */ Iso9660Entry* entry( const QString& name ); /** * Returns the entry with the given name. * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. * @return a pointer to the entry in the directory. */ const Iso9660Entry* entry( const QString& name ) const; /** * Returns a list of sub-entries. * Searches for Iso9660 names. * @return the names of all entries in this directory (filenames, no path). */ QStringList iso9660Entries() const; /** * Returns the entry with the given name. * Searches for Iso9660 names. * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. * @return a pointer to the entry in the directory. */ Iso9660Entry* iso9660Entry( const QString& name ); /** * Returns the entry with the given name. * Searches for Iso9660 names. * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. * @return a pointer to the entry in the directory. */ const Iso9660Entry* iso9660Entry( const QString& name ) const; /** * @internal * Adds a new entry to the directory. */ void addEntry( Iso9660Entry* ); /** * Checks whether this entry is a directory. * @return true, since this entry is a directory */ bool isDirectory() const { return true; } private: void expand(); QHash m_entries; QHash m_iso9660Entries; bool m_bExpanded; unsigned int m_startSector; unsigned int m_size; }; class LIBK3B_EXPORT Iso9660File : public Iso9660Entry { public: /** * @param pos start sector */ Iso9660File( Iso9660* archive, const QString& isoName, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink, unsigned int pos, unsigned int size ); ~Iso9660File(); bool isFile() const { return true; } void setZF( char algo[2], char parms[2], int realsize ); int realsize() const { return m_realsize; } /** * @return size in bytes. */ unsigned int size() const { return m_size; } /** * Returnes the startSector of the file. */ unsigned int startSector() const { return m_startSector; } /** * Returnes the startOffset of the file in bytes. */ unsigned long long startPostion() const { return (unsigned long long)m_startSector * 2048; } /** * @param pos offset in bytes * @param len max number of bytes to read */ int read( unsigned int pos, char* data, int len ) const; /** * Copy this file to a url. */ bool copyTo( const QString& url ) const; private: char m_algo[2]; char m_parms[2]; int m_realsize; unsigned int m_curpos; unsigned int m_startSector; unsigned int m_size; }; /** * This class is based on the KIso class by * Gy�gy Szombathelyi . * A lot has been changed and bugfixed. * The API has been improved to be useful. * * Due to the stupid Qt which does not support large files as default * we cannot use QIODevice with DVDs! That's why we have our own * reading code which is not allowed by KArchive (which is limited to int * by the way... who the hell designed this?) * I also removed the KArchive inheritance because of the named reasons. * So this stuff contains a lot KArchive code which has been made usable. * * That does not mean that this class is well designed. No, it's not. :) * * Opening a Iso9660 object should be fast since creation of the directory * and file entries is not done until a call to Iso9660Directory::entries. */ class LIBK3B_EXPORT Iso9660 { public: /** * Creates an instance that operates on the given filename. * using the compression filter associated to given mimetype. * * @param filename is a local path (e.g. "/home/weis/myfile.tgz") */ explicit Iso9660( const QString& filename ); /** * Special case which always reads the TOC from the specified sector * thus supporting multisession CDs. */ - Iso9660( Device::Device* dev, unsigned int startSector = 0 ); + explicit Iso9660( Device::Device* dev, unsigned int startSector = 0 ); /** * @param fd open file descriptor */ explicit Iso9660( int fd ); /** * Directly specify the backend to read from. * Iso9660 will take ownership of the backend and delete it. */ explicit Iso9660( Iso9660Backend* ); /** * If the .iso is still opened, then it will be * closed automatically by the destructor. */ virtual ~Iso9660(); /** * Set where to start reading in the source. */ void setStartSector( unsigned int startSector ); /** * If set to true before opening Iso9660 will ignore RR and joliet extensions * and only create plain iso9660 names. */ void setPlainIso9660( bool ); bool plainIso9660() const; /** * Opens the archive for reading. * Parses the directory listing of the archive * and creates the Iso9660Directory/Iso9660File entries. */ bool open(); bool isOpen() const; /** * Closes everything. * This is also called in the destructor */ void close(); /** * @param sector startsector * @param len number of sectors * @return number of sectors read or -1 on error */ int read( unsigned int sector, char* data, int len ); /** * The name of the os file, as passed to the constructor * Null if you did not use the QString constructor. */ QString fileName() { return m_filename; } const Iso9660Directory* firstJolietDirEntry() const; const Iso9660Directory* firstRRDirEntry() const; const Iso9660Directory* firstIsoDirEntry() const; const Iso9660Directory* firstElToritoEntry() const; /** * @returns 0 if no joliet desc could be found * the joliet level (1-3) otherwise */ int jolietLevel() const { return m_joliet; } const Iso9660SimplePrimaryDescriptor& primaryDescriptor() const; void debug() const; private: /** * @internal */ void addBoot( struct el_torito_boot_descriptor* bootdesc ); void createSimplePrimaryDesc( struct iso_primary_descriptor* desc ); void debugEntry( const Iso9660Entry*, int depth ) const; int m_joliet; // only used for creation static int read_callback( char* buf, sector_t start, int len, void* udata ); static int isofs_callback( struct iso_directory_record* idr, void *udata ); Iso9660Directory *dirent; bool m_rr; friend class Iso9660Directory; private: QString m_filename; class Private; Private * d; }; } #endif diff --git a/libk3b/tools/libisofs/isofs.cpp b/libk3b/tools/libisofs/isofs.cpp index 66ce3ba8b..6d75d17c2 100644 --- a/libk3b/tools/libisofs/isofs.cpp +++ b/libk3b/tools/libisofs/isofs.cpp @@ -1,875 +1,875 @@ /*************************************************************************** isofs.c - libisofs implementation ------------------- begin : Oct 25 2002 copyright : (C) 2002 by Szombathelyi György email : gyurco@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include "isofs.h" #include "rock.h" /* internal function from the linux kernel (isofs fs) */ static time_t getisotime(int year,int month,int day,int hour, int minute,int second,int tz) { int days, i; time_t crtime; year-=1970; if (year < 0) { crtime = 0; } else { int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; days = year * 365; if (year > 2) days += (year+1) / 4; - for (i = 1; i < month; i++) + for (i = 1; i < month; ++i) days += monlen[i-1]; if (((year+2) % 4) == 0 && month > 2) days++; days += day - 1; crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; /* sign extend */ if (tz & 0x80) tz |= (-1 << 8); /* * The timezone offset is unreliable on some disks, * so we make a sanity check. In no case is it ever * more than 13 hours from GMT, which is 52*15min. * The time is always stored in localtime with the * timezone offset being what get added to GMT to * get to localtime. Thus we need to subtract the offset * to get to true GMT, which is what we store the time * as internally. On the local system, the user may set * their timezone any way they wish, of course, so GMT * gets converted back to localtime on the receiving * system. * * NOTE: mkisofs in versions prior to mkisofs-1.10 had * the sign wrong on the timezone offset. This has now * been corrected there too, but if you are getting screwy * results this may be the explanation. If enough people * complain, a user configuration option could be added * to add the timezone offset in with the wrong sign * for 'compatibility' with older discs, but I cannot see how * it will matter that much. * * Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann) * for pointing out the sign error. */ if (-52 <= tz && tz <= 52) crtime -= tz * 15 * 60; } return crtime; } /** * Returns the Unix from the ISO9660 9.1.5 time format */ time_t isodate_915(char * p, int hs) { return getisotime(1900+p[0],p[1],p[2],p[3],p[4],p[5],hs==0 ? p[6] : 0); } /** * Returns the Unix from the ISO9660 8.4.26.1 time format * BUG: hundredth of seconds are ignored, because Unix time_t has one second * resolution (I think it's no problem at all) */ time_t isodate_84261(char * p, int hs) { int year,month,day,hour,minute,second; year=(p[0]-'0')*1000 + (p[1]-'0')*100 + (p[2]-'0')*10 + p[3]-'0'; month=(p[4]-'0')*10 + (p[5]-'0'); day=(p[6]-'0')*10 + (p[7]-'0'); hour=(p[8]-'0')*10 + (p[9]-'0'); minute=(p[10]-'0')*10 + (p[11]-'0'); second=(p[12]-'0')*10 + (p[13]-'0'); return getisotime(year,month,day,hour,minute,second,hs==0 ? p[16] : 0); } void FreeBootTable(boot_head *boot) { boot_entry *be,*next; be=boot->defentry; while (be) { next=be->next; free(be); be=next; } boot->defentry=NULL; } int BootImageSize(readfunc*,int media,sector_t,int len,void*) { int ret; switch(media & 0xf) { case 0: ret=len; /* No emulation */ break; case 1: ret=80*2*15; /* 1.2 MB */ break; case 2: ret=80*2*18; /* 1.44 MB */ break; case 3: ret=80*2*36; /* 2.88 MB */ break; case 4: /* FIXME!!! */ ret=len; /* Hard Disk */ break; default: ret=len; } return ret; } static boot_entry *CreateBootEntry(char *be) { boot_entry *entry; entry = (boot_entry*) malloc(sizeof(boot_entry)); if (!entry) return NULL; memset(entry, 0, sizeof(boot_entry)); memcpy(entry->data,be,0x20); return entry; } int ReadBootTable(readfunc *read,sector_t sector, boot_head *head, void *udata) { char buf[2048], *c, *be; int i,end=0; unsigned short sum; boot_entry *defcur=NULL,*deflast=NULL; register struct validation_entry *ventry=NULL; head->sections=NULL; head->defentry=NULL; while (1) { be = (char*) &buf; if ( read(be, sector, 1, udata) != 1 ) goto err; /* first entry needs to be a validation entry */ if (!ventry) { ventry=(struct validation_entry *) be; if ( isonum_711(ventry->type) !=1 ) goto err; sum=0; c = (char*) ventry; - for (i=0;i<16;i++) { sum += isonum_721(c); c+=2; } + for (i=0;i<16;++i) { sum += isonum_721(c); c+=2; } if (sum) goto err; memcpy(&head->ventry,be,0x20); be += 0x20; } while (!end && (be < (buf+1))) { switch (isonum_711(be)) { case 0x88: defcur=CreateBootEntry(be); if (!defcur) goto err; if (deflast) deflast->next=defcur; else head->defentry=defcur; defcur->prev=deflast; deflast=defcur; break; case 0x90: case 0x91: break; default: end=1; break; } be += 0x20; } if (end) break; sector ++; } return 0; err: FreeBootTable(head); return -1; } /** * Creates the linked list of the volume descriptors */ iso_vol_desc *ReadISO9660(readfunc *read,sector_t sector,void *udata) { int i; struct iso_volume_descriptor buf; iso_vol_desc *first=NULL,*current=NULL,*prev=NULL; for (i=0;i<100;i++) { if (read( (char*) &buf, sector+i+16, 1, udata) != 1 ) { FreeISO9660(first); return NULL; } if (!memcmp(ISO_STANDARD_ID,&buf.id,5)) { switch ( isonum_711(&buf.type[0]) ) { case ISO_VD_BOOT: case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: current=(iso_vol_desc*) malloc(sizeof(iso_vol_desc)); if (!current) { FreeISO9660(first); return NULL; } current->prev=prev; current->next=NULL; if (prev) prev->next=current; memcpy(&(current->data),&buf,2048); if (!first) first=current; prev=current; break; case ISO_VD_END: return first; break; } } else if (!memcmp(HS_STANDARD_ID,(struct hs_volume_descriptor*) &buf,5)) { /* High Sierra format not supported (yet) */ } } return first; } /** * Frees the linked list of volume descriptors */ void FreeISO9660(iso_vol_desc *data) { iso_vol_desc *current; while (data) { current=data; data=current->next; free(current); } } /** * Frees the strings in 'rrentry' */ void FreeRR(rr_entry *rrentry) { if (rrentry->name) { free(rrentry->name); rrentry->name=NULL; } if (rrentry->sl) { free(rrentry->sl); rrentry->sl = NULL; } } int str_nappend(char **d, char *s, int n) { int i = 0; char *c; while (i < n && s[i]) i++; i++; if (*d) i += (strlen(*d) + 1); c = (char*) malloc(i); if (!c) return -ENOMEM; if (*d) { strcpy(c, *d); strncat(c, s, n); free(*d); } else strncpy(c, s, n); c[i - 1] = 0; *d = c; return 0; } static int str_append(char **d, const char *s) { int i; char *c; i=strlen(s)+1; if (*d) i+=(strlen(*d)+1); c=(char*) malloc(i); if (!c) return -ENOMEM; if (*d) { strcpy(c,*d); strcat(c,s); free(*d); } else strcpy(c,s); c[i-1]=0; *d=c; return 0; } #define rrtlen(c) (((unsigned char) c & 0x80) ? 17 : 7) #define rrctime(f,c) ((unsigned char) f & 0x80) ? isodate_84261(c,0) : isodate_915(c,0) /** * Parses the System Use area and fills rr_entry with values */ int ParseRR(struct iso_directory_record *idr, rr_entry *rrentry) { int suspoffs,susplen,i,f,ret=0; char *r, *c; struct rock_ridge *rr; suspoffs=33+isonum_711(idr->name_len); if (!(isonum_711(idr->name_len) & 1)) suspoffs++; susplen=isonum_711(idr->length)-suspoffs; r= & (((char*) idr)[suspoffs]); rr = (struct rock_ridge*) r; memset(rrentry,0,sizeof(rr_entry)); rrentry->len = sizeof(rr_entry); while (susplen > 0) { if (isonum_711(&rr->len) > susplen || rr->len == 0) break; if (rr->signature[0]=='N' && rr->signature[1]=='M') { if (!(rr->u.NM.flags & 0x26) && rr->len>5 && !rrentry->name) { if (str_nappend(&rrentry->name,rr->u.NM.name,isonum_711(&rr->len)-5)) { FreeRR(rrentry); return -ENOMEM; } ret++; } } else if (rr->signature[0]=='P' && rr->signature[1]=='X' && (isonum_711(&rr->len)==44 || isonum_711(&rr->len)==36)) { rrentry->mode=isonum_733(rr->u.PX.mode); rrentry->nlink=isonum_733(rr->u.PX.n_links); rrentry->uid=isonum_733(rr->u.PX.uid); rrentry->gid=isonum_733(rr->u.PX.gid); if (isonum_711(&rr->len)==44) rrentry->serno=isonum_733(rr->u.PX.serno); ret++; } else if (rr->signature[0]=='P' && rr->signature[1]=='N' && isonum_711(&rr->len)==20) { rrentry->dev_major=isonum_733(rr->u.PN.dev_high); rrentry->dev_minor=isonum_733(rr->u.PN.dev_low); ret++; } else if (rr->signature[0]=='P' && rr->signature[1]=='L' && isonum_711(&rr->len)==12) { rrentry->pl=isonum_733(rr->u.PL.location); ret++; } else if (rr->signature[0]=='C' && rr->signature[1]=='L' && isonum_711(&rr->len)==12) { rrentry->cl=isonum_733(rr->u.CL.location); ret++; } else if (rr->signature[0]=='R' && rr->signature[1]=='E' && isonum_711(&rr->len)==4) { rrentry->re=1; ret++; } else if (rr->signature[0]=='S' && rr->signature[1]=='L' && isonum_711(&rr->len)>7) { i = isonum_711(&rr->len)-5; c = (char*) rr; c += 5; while (i>0) { switch(c[0] & ~1) { case 0x2: if (str_append(&rrentry->sl,".")) { FreeRR(rrentry); return -ENOMEM; } break; case 0x4: if (str_append(&rrentry->sl,"..")) { FreeRR(rrentry); return -ENOMEM; } break; } if ( (c[0] & 0x08) == 0x08 || (c[1] && rrentry->sl && strlen(rrentry->sl)>1) ) { if (str_append(&rrentry->sl,"/")) { FreeRR(rrentry); return -ENOMEM; } } if ((unsigned char)c[1]>0) { if (str_nappend(&rrentry->sl,c+2,(unsigned char)c[1])) { FreeRR(rrentry); return -ENOMEM; } } i -= ((unsigned char)c[1] + 2); c += ((unsigned char)c[1] + 2); } ret++; } else if (rr->signature[0]=='T' && rr->signature[1]=='F' && isonum_711(&rr->len)>5) { i = isonum_711(&rr->len)-5; f = rr->u.TF.flags; c = (char*) rr; c += 5; while (i >= rrtlen(f)) { if (f & 1) { rrentry->t_creat=rrctime(f,c); f &= ~1; } else if (f & 2) { rrentry->rr_st_mtime=rrctime(f,c); f &= ~2; } else if (f & 4) { rrentry->rr_st_atime=rrctime(f,c); f &= ~4; } else if (f & 8) { rrentry->rr_st_ctime=rrctime(f,c); f &= ~8; } else if (f & 16) { rrentry->t_backup=rrctime(f,c); f &= ~16; } else if (f & 32) { rrentry->t_expire=rrctime(f,c); f &= ~32; } else if (f & 64) { rrentry->t_effect=rrctime(f,c); f &= ~64; } i -= rrtlen(f); c += rrtlen(f); } ret++; } else if (rr->signature[0]=='Z' && rr->signature[1]=='F' && isonum_711(&rr->len)==16) { /* Linux-specific extension: transparent decompression */ rrentry->z_algo[0]=rr->u.ZF.algorithm[0]; rrentry->z_algo[1]=rr->u.ZF.algorithm[1]; rrentry->z_params[0]=rr->u.ZF.parms[0]; rrentry->z_params[1]=rr->u.ZF.parms[1]; rrentry->z_size=isonum_733(rr->u.ZF.real_size); ret++; } else { /* printf("SUSP sign: %c%c\n",rr->signature[0],rr->signature[1]); */ } susplen -= isonum_711(&rr->len); r += isonum_711(&rr->len); rr = (struct rock_ridge*) r; } return ret; } /** * Iterates over the directory entries. The directory is in 'buf', * the size of the directory is 'size'. 'callback' is called for each * directory entry with the parameter 'udata'. */ int ProcessDir(readfunc *read,int extent,int size,dircallback *callback,void *udata) { int pos=0,ret=0,siz; char *buf; struct iso_directory_record *idr; if (size & 2047) { siz=((size>>11)+1)<<11; } else { siz=size; } buf=(char*) malloc(siz); if (!buf) return -ENOMEM; if (read(buf,extent,siz>>11,udata)!=siz>>11) { free(buf); return -EIO; } while (size>0) { idr=(struct iso_directory_record*) &buf[pos]; if (isonum_711(idr->length)==0) { size-=(2048 - (pos & 0x7ff)); if (size<=2) break; pos+=0x800; pos&=0xfffff800; idr=(struct iso_directory_record*) &buf[pos]; } pos+=isonum_711(idr->length); pos+=isonum_711(idr->ext_attr_length); size-=isonum_711(idr->length); size-=isonum_711(idr->ext_attr_length); if (size<0) break; if (isonum_711(idr->length) <33 || isonum_711(idr->length)<33+isonum_711(idr->name_len)) { /* Invalid directory entry */ continue; } if ((ret=callback(idr,udata))) break; } free(buf); return ret; } /** * returns the joliet level from the volume descriptor */ int JolietLevel(struct iso_volume_descriptor *ivd) { int ret=0; register struct iso_supplementary_descriptor *isd; isd = (struct iso_supplementary_descriptor *) ivd; if (isonum_711(ivd->type)==ISO_VD_SUPPLEMENTARY) { if (isd->escape[0]==0x25 && isd->escape[1]==0x2f) { switch (isd->escape[2]) { case 0x40: ret=1; break; case 0x43: ret=2; break; case 0x45: ret=3; break; } } } return ret; } /********************************************************************/ #if 0 #include #include #include #include #include #include #include int level=0,joliet=0,dirs,files; iconv_t iconv_d; int fd; int readf(char *buf, int start, int len,void *udata) { int ret; if ((ret=lseek(fd, start << 11, SEEK_SET))<0) return ret; ret=read(fd, buf, len << 11); if (ret<0) return ret; return (ret >> 11); } void dumpchars(char *c,int len) { while (len>0) { printf("%c",*c); len--; c++; } } void sp(int num) { int i; for (i=0;iname_len)==1) { switch (dir->name[0]) { case 0: printf("."); break; case 1: printf(".."); break; default: printf("%c",dir->name[0]); break; } } dumpchardesc(dir->name,isonum_711(dir->name_len)); printf(" size=%d",isonum_733(dir->size)); printf(" extent=%d ",isonum_733(dir->extent)); dumpflags(isonum_711(dir->flags)); dumpiso915time((char*) &(dir->date),0); } void dumprrentry(rr_entry *rr) { printf(" NM=[%s] uid=%d gid=%d nlink=%d mode=%o ", rr->name,rr->uid,rr->gid,rr->nlink,rr->mode); if (S_ISCHR(rr->mode) || S_ISBLK(rr->mode)) printf("major=%d minor=%d ",rr->dev_major,rr->dev_minor); if (rr->mode & S_IFLNK && rr->sl) printf("slink=%s ",rr->sl); /* printf("\n"); if (rr->t_creat) printf("t_creat: %s",ctime(&rr->t_creat)); if (rr->rr_st_mtime) printf("rr_st_mtime: %s",ctime(&rr->rr_st_mtime)); if (rr->rr_st_atime) printf("rr_st_atime: %s",ctime(&rr->rr_st_atime)); if (rr->rr_st_ctime) printf("rr_st_ctime: %s",ctime(&rr->rr_st_ctime)); if (rr->t_backup) printf("t_backup: %s",ctime(&rr->t_backup)); if (rr->t_expire) printf("t_expire: %s",ctime(&rr->t_expire)); if (rr->t_effect) printf("t_effect: %s",ctime(&rr->t_effect)); */ } void dumpsusp(char *c, int len) { dumpchars(c,len); } void dumpboot(struct el_torito_boot_descriptor *ebd) { printf("version: %d\n",isonum_711(ebd->version)); printf("system id: ");dumpchars(ebd->system_id,ISODCL(8,39));printf("\n"); printf("boot catalog start: %d\n",isonum_731(ebd->boot_catalog)); } void dumpdefentry(struct default_entry *de) { printf("Default entry: \n"); printf(" bootid=%x\n",isonum_711(de->bootid)); printf(" media emulation=%d (",isonum_711(de->media)); switch(isonum_711(de->media) & 0xf) { case 0: printf("No emulation"); break; case 1: printf("1.2 Mb floppy"); break; case 2: printf("1.44 Mb floppy"); break; case 3: printf("2.88 Mb floppy"); break; case 4: printf("Hard Disk"); break; default: printf("Unknown/Invalid"); break; } printf(")\n"); printf(" loadseg=%d\n",isonum_721(de->loadseg)); printf(" systype=%d\n",isonum_711(de->systype)); printf(" start lba=%d count=%d\n",isonum_731(de->start), isonum_721(de->seccount)); } void dumpbootcat(boot_head *bh) { boot_entry *be; printf("System id: ");dumpchars(bh->ventry.id,ISODCL(28,5));printf("\n"); be=bh->defentry; while (be) { dumpdefentry(be->data); be=be->next; } } void dumpdesc(struct iso_primary_descriptor *ipd) { printf("system id: ");dumpchardesc(ipd->system_id,ISODCL(9,40));printf("\n"); printf("volume id: ");dumpchardesc(ipd->volume_id,ISODCL(41,72));printf("\n"); printf("volume space size: %d\n",isonum_733(ipd->volume_space_size)); printf("volume set size: %d\n",isonum_723(ipd->volume_set_size)); printf("volume seq num: %d\n",isonum_723(ipd->volume_set_size)); printf("logical block size: %d\n",isonum_723(ipd->logical_block_size)); printf("path table size: %d\n",isonum_733(ipd->path_table_size)); printf("location of type_l path table: %d\n",isonum_731(ipd->type_l_path_table)); printf("location of optional type_l path table: %d\n",isonum_731(ipd->opt_type_l_path_table)); printf("location of type_m path table: %d\n",isonum_732(ipd->type_m_path_table)); printf("location of optional type_m path table: %d\n",isonum_732(ipd->opt_type_m_path_table)); /* printf("Root dir record:\n");dumpdirrec((struct iso_directory_record*) &ipd->root_directory_record); */ printf("Volume set id: ");dumpchardesc(ipd->volume_set_id,ISODCL(191,318));printf("\n"); printf("Publisher id: ");dumpchardesc(ipd->publisher_id,ISODCL(319,446));printf("\n"); printf("Preparer id: ");dumpchardesc(ipd->preparer_id,ISODCL(447,574));printf("\n"); printf("Application id: ");dumpchardesc(ipd->application_id,ISODCL(575,702));printf("\n"); printf("Copyright id: ");dumpchardesc(ipd->copyright_file_id,ISODCL(703,739));printf("\n"); printf("Abstract file id: ");dumpchardesc(ipd->abstract_file_id,ISODCL(740,776));printf("\n"); printf("Bibliographic file id: ");dumpchardesc(ipd->bibliographic_file_id,ISODCL(777,813));printf("\n"); printf("Volume creation date: ");dumpiso84261time(ipd->creation_date,0);printf("\n"); printf("Volume modification date: ");dumpiso84261time(ipd->modification_date,0);printf("\n"); printf("Volume expiration date: ");dumpiso84261time(ipd->expiration_date,0);printf("\n"); printf("Volume effective date: ");dumpiso84261time(ipd->effective_date,0);printf("\n"); printf("File structure version: %d\n",isonum_711(ipd->file_structure_version)); } int mycallb(struct iso_directory_record *idr,void *udata) { rr_entry rrentry; sp(level);dumpdirrec(idr); if (level==0) printf(" (Root directory) "); printf("\n"); if (ParseRR(idr,&rrentry)>0) { sp(level);printf(" ");dumprrentry(&rrentry);printf("\n"); } FreeRR(&rrentry); if ( !(idr->flags[0] & 2) ) files++; if ( (idr->flags[0] & 2) && (level==0 || isonum_711(idr->name_len)>1) ) { level++; dirs++; ProcessDir(&readf,isonum_733(idr->extent),isonum_733(idr->size),&mycallb,udata); level--; } return 0; } /************************************************/ int main(int argc, char *argv[]) { int i=1,sector=0; iso_vol_desc *desc; boot_head boot; if (argc<2) { fprintf(stderr,"\nUsage: %s iso-file-name or device [starting sector]\n\n",argv[0]); return 0; } if (argc>=3) { sector=atoi(argv[2]); printf("Using starting sector number %d\n",sector); } fd=open(argv[1],O_RDONLY); if (fd<0) { fprintf(stderr,"open error\n"); return -1; } iconv_d=iconv_open("ISO8859-2","UTF16BE"); if (iconv_d==0) { fprintf(stderr,"iconv open error\n"); return -1; } desc=ReadISO9660(&readf,sector,NULL); if (!desc) { printf("No volume descriptors\n"); return -1; } while (desc) { printf("\n\n--------------- Volume descriptor (%d.) type %d: ---------------\n\n", i,isonum_711(desc->data.type)); switch (isonum_711(desc->data.type)) { case ISO_VD_BOOT: { struct el_torito_boot_descriptor* bootdesc; bootdesc=&(desc->data); dumpboot(bootdesc); if ( !memcmp(EL_TORITO_ID,bootdesc->system_id,ISODCL(8,39)) ) { if (ReadBootTable(&readf,isonum_731(bootdesc->boot_catalog),&boot,NULL)) { printf("Boot Catalog Error\n"); } else { dumpbootcat(&boot); FreeBootTable(&boot); } } } break; case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: joliet=0; joliet = JolietLevel(&desc->data); printf("Joliet level: %d\n",joliet); dumpdesc((struct iso_primary_descriptor*) &desc->data); printf("\n\n--------------- Directory structure: -------------------\n\n"); dirs=0;files=0; mycallb( &( ((struct iso_primary_descriptor*) &desc->data)->root_directory_record), NULL ); printf("\nnumber of directories: %d\n",dirs); printf("\nnumber of files: %d\n",files); break; } desc=desc->next; i++; } iconv_close(iconv_d); close(fd); FreeISO9660(desc); return 0; } #endif diff --git a/libk3b/tools/qprocess/k3bqprocess_win.cpp b/libk3b/tools/qprocess/k3bqprocess_win.cpp index c19d7fe3e..102175148 100644 --- a/libk3b/tools/qprocess/k3bqprocess_win.cpp +++ b/libk3b/tools/qprocess/k3bqprocess_win.cpp @@ -1,910 +1,910 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "k3bqprocess.h" #include "k3bqprocess_p.h" #include "qwindowspipewriter_p.h" #include #include #include #include #include #include #include #include #include #include #include "private/qfsfileengine.h" // for longFileName and win95FileName #include "private/qfsfileengine_p.h" // for longFileName and win95FileName #ifndef QT_NO_PROCESS QT_BEGIN_NAMESPACE //#define QPROCESS_DEBUG #define NOTIFYTIMEOUT 100 static void qt_create_pipe(Q_PIPE *pipe, bool in) { // Open the pipes. Make non-inheritable copies of input write and output // read handles to avoid non-closable handles (this is done by the // DuplicateHandle() call). #if !defined(Q_OS_WINCE) SECURITY_ATTRIBUTES secAtt = { sizeof( SECURITY_ATTRIBUTES ), NULL, TRUE }; HANDLE tmpHandle; if (in) { // stdin if (!CreatePipe(&pipe[0], &tmpHandle, &secAtt, 1024 * 1024)) return; if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) return; } else { // stdout or stderr if (!CreatePipe(&tmpHandle, &pipe[1], &secAtt, 1024 * 1024)) return; if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &pipe[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) return; } CloseHandle(tmpHandle); #else Q_UNUSED(pipe); Q_UNUSED(in); #endif } /* Create the pipes to a K3bQProcessPrivate::Channel. This function must be called in order: stdin, stdout, stderr */ bool K3bQProcessPrivate::createChannel(Channel &channel) { Q_Q(K3bQProcess); if (&channel == &stderrChannel && processChannelMode == QProcess::MergedChannels) { return DuplicateHandle(GetCurrentProcess(), stdoutChannel.pipe[1], GetCurrentProcess(), &stderrChannel.pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS); } if (channel.type == Channel::Normal) { // we're piping this channel to our own process qt_create_pipe(channel.pipe, &channel == &stdinChannel); return true; } else if (channel.type == Channel::Redirect) { // we're redirecting the channel to/from a file SECURITY_ATTRIBUTES secAtt = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; if (&channel == &stdinChannel) { // try to open in read-only mode channel.pipe[1] = INVALID_Q_PIPE; QT_WA({ channel.pipe[0] = CreateFileW((TCHAR*)QFSFileEnginePrivate::longFileName(channel.file).utf16(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &secAtt, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); }, { channel.pipe[0] = CreateFileA(QFSFileEnginePrivate::win95Name(channel.file), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &secAtt, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); }); if (channel.pipe[0] != INVALID_Q_PIPE) return true; q->setErrorString(K3bQProcess::tr("Could not open input redirection for reading")); } else { // open in write mode channel.pipe[0] = INVALID_Q_PIPE; DWORD creation; if (channel.append) creation = OPEN_ALWAYS; else creation = CREATE_ALWAYS; QT_WA({ channel.pipe[1] = CreateFileW((TCHAR*)QFSFileEnginePrivate::longFileName(channel.file).utf16(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &secAtt, creation, FILE_ATTRIBUTE_NORMAL, NULL); }, { channel.pipe[1] = CreateFileA(QFSFileEnginePrivate::win95Name(channel.file), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &secAtt, creation, FILE_ATTRIBUTE_NORMAL, NULL); }); if (channel.pipe[1] != INVALID_Q_PIPE) { if (channel.append) { SetFilePointer(channel.pipe[1], 0, NULL, FILE_END); } return true; } q->setErrorString(K3bQProcess::tr("Could not open output redirection for writing")); } // could not open file processError = QProcess::FailedToStart; emit q->error(processError); cleanup(); return false; } else { Q_ASSERT_X(channel.process, "K3bQProcess::start", "Internal error"); Channel *source; Channel *sink; if (channel.type == Channel::PipeSource) { // we are the source source = &channel; sink = &channel.process->stdinChannel; if (source->pipe[1] != INVALID_Q_PIPE) { // already constructed by the sink // make it inheritable HANDLE tmpHandle = source->pipe[1]; if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &source->pipe[1], 0, TRUE, DUPLICATE_SAME_ACCESS)) return false; CloseHandle(tmpHandle); return true; } Q_ASSERT(source == &stdoutChannel); Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink); qt_create_pipe(source->pipe, /* in = */ false); // source is stdout sink->pipe[0] = source->pipe[0]; source->pipe[0] = INVALID_Q_PIPE; return true; } else { // we are the sink; source = &channel.process->stdoutChannel; sink = &channel; if (sink->pipe[0] != INVALID_Q_PIPE) { // already constructed by the source // make it inheritable HANDLE tmpHandle = sink->pipe[0]; if (!DuplicateHandle(GetCurrentProcess(), tmpHandle, GetCurrentProcess(), &sink->pipe[0], 0, TRUE, DUPLICATE_SAME_ACCESS)) return false; CloseHandle(tmpHandle); return true; } Q_ASSERT(sink == &stdinChannel); Q_ASSERT(source->process == this && source->type == Channel::PipeSource); qt_create_pipe(sink->pipe, /* in = */ true); // sink is stdin source->pipe[1] = sink->pipe[1]; sink->pipe[1] = INVALID_Q_PIPE; return true; } } } void K3bQProcessPrivate::destroyPipe(Q_PIPE pipe[2]) { if (pipe[0] == stdinChannel.pipe[0] && pipe[1] == stdinChannel.pipe[1] && pipeWriter) { delete pipeWriter; pipeWriter = 0; } if (pipe[0] != INVALID_Q_PIPE) { CloseHandle(pipe[0]); pipe[0] = INVALID_Q_PIPE; } if (pipe[1] != INVALID_Q_PIPE) { CloseHandle(pipe[1]); pipe[1] = INVALID_Q_PIPE; } } static QString qt_create_commandline(const QString &program, const QStringList &arguments) { QString args; if (!program.isEmpty()) { QString programName = program; if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1String(" "))) programName = QLatin1String("\"") + programName + QLatin1String("\""); programName.replace(QLatin1String("/"), QLatin1String("\\")); // add the prgram as the first arg ... it works better args = programName + QLatin1String(" "); } for (int i=0; i0 && tmp.at(i-1) == QLatin1Char('\\')) { --i; endQuote += QLatin1String("\\"); } args += QLatin1String(" \"") + tmp.left(i) + endQuote; } else { args += QLatin1Char(' ') + tmp; } } return args; } static QByteArray qt_create_environment(const QStringList &environment) { QByteArray envlist; if (!environment.isEmpty()) { QStringList envStrings = environment; int pos = 0; // add PATH if necessary (for DLL loading) if (envStrings.filter(QRegExp(QLatin1String("^PATH="),Qt::CaseInsensitive)).isEmpty()) { QByteArray path = qgetenv("PATH"); if (!path.isEmpty()) envStrings.prepend(QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path))); } // add systemroot if needed if (envStrings.filter(QRegExp(QLatin1String("^SystemRoot="),Qt::CaseInsensitive)).isEmpty()) { QByteArray systemRoot = qgetenv("SystemRoot"); if (!systemRoot.isEmpty()) envStrings.prepend(QString(QLatin1String("SystemRoot=%1")).arg(QString::fromLocal8Bit(systemRoot))); } #ifdef UNICODE if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) { QString tmp = *it; uint tmpSize = sizeof(TCHAR) * (tmp.length()+1); envlist.resize(envlist.size() + tmpSize); memcpy(envlist.data()+pos, tmp.utf16(), tmpSize); pos += tmpSize; } // add the 2 terminating 0 (actually 4, just to be on the safe side) envlist.resize( envlist.size()+4 ); envlist[pos++] = 0; envlist[pos++] = 0; envlist[pos++] = 0; envlist[pos++] = 0; } else #endif // UNICODE { - for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); it++) { + for (QStringList::ConstIterator it = envStrings.constBegin(); it != envStrings.constEnd(); ++it) { QByteArray tmp = (*it).toLocal8Bit(); uint tmpSize = tmp.length() + 1; envlist.resize(envlist.size() + tmpSize); memcpy(envlist.data()+pos, tmp.data(), tmpSize); pos += tmpSize; } // add the terminating 0 (actually 2, just to be on the safe side) envlist.resize(envlist.size()+2); envlist[pos++] = 0; envlist[pos++] = 0; } } return envlist; } void K3bQProcessPrivate::startProcess() { Q_Q(K3bQProcess); bool success = false; if (pid) { CloseHandle(pid->hThread); CloseHandle(pid->hProcess); delete pid; pid = 0; } pid = new PROCESS_INFORMATION; memset(pid, 0, sizeof(PROCESS_INFORMATION)); q->setProcessState(QProcess::Starting); if (!createChannel(stdinChannel) || !createChannel(stdoutChannel) || !createChannel(stderrChannel)) return; #if defined(Q_OS_WINCE) QString args = qt_create_commandline(QString(), arguments); #else QString args = qt_create_commandline(program, arguments); QByteArray envlist = qt_create_environment(environment); #endif #if defined QPROCESS_DEBUG qDebug("Creating process"); qDebug(" program : [%s]", program.toLatin1().constData()); qDebug(" args : %s", args.toLatin1().constData()); qDebug(" pass environment : %s", environment.isEmpty() ? "no" : "yes"); #endif DWORD dwCreationFlags = 0; if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) dwCreationFlags |= CREATE_NO_WINDOW; #ifdef UNICODE if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { #if defined(Q_OS_WINCE) QString fullPathProgram = program; if (!QDir::isAbsolutePath(fullPathProgram)) fullPathProgram = QFileInfo(fullPathProgram).absoluteFilePath(); fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\")); success = CreateProcessW((WCHAR*)fullPathProgram.utf16(), (WCHAR*)args.utf16(), 0, 0, false, 0, 0, 0, 0, pid); #else dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0, 0, 0, STARTF_USESTDHANDLES, 0, 0, 0, stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1] }; success = CreateProcessW(0, (WCHAR*)args.utf16(), 0, 0, TRUE, dwCreationFlags, environment.isEmpty() ? 0 : envlist.data(), workingDirectory.isEmpty() ? 0 : (WCHAR*)QDir::toNativeSeparators(workingDirectory).utf16(), &startupInfo, pid); #endif } else #endif // UNICODE { #ifndef Q_OS_WINCE STARTUPINFOA startupInfo = { sizeof( STARTUPINFOA ), 0, 0, 0, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0, 0, 0, STARTF_USESTDHANDLES, 0, 0, 0, stdinChannel.pipe[0], stdoutChannel.pipe[1], stderrChannel.pipe[1] }; success = CreateProcessA(0, args.toLocal8Bit().data(), 0, 0, TRUE, dwCreationFlags, environment.isEmpty() ? 0 : envlist.data(), workingDirectory.isEmpty() ? 0 : QDir::toNativeSeparators(workingDirectory).toLocal8Bit().data(), &startupInfo, pid); #endif // Q_OS_WINCE } #ifndef Q_OS_WINCE if (stdinChannel.pipe[0] != INVALID_Q_PIPE) { CloseHandle(stdinChannel.pipe[0]); stdinChannel.pipe[0] = INVALID_Q_PIPE; } if (stdoutChannel.pipe[1] != INVALID_Q_PIPE) { CloseHandle(stdoutChannel.pipe[1]); stdoutChannel.pipe[1] = INVALID_Q_PIPE; } if (stderrChannel.pipe[1] != INVALID_Q_PIPE) { CloseHandle(stderrChannel.pipe[1]); stderrChannel.pipe[1] = INVALID_Q_PIPE; } #endif // Q_OS_WINCE if (!success) { cleanup(); processError = QProcess::FailedToStart; q->setErrorString(K3bQProcess::tr("Process failed to start")); emit q->error(processError); q->setProcessState(QProcess::NotRunning); return; } q->setProcessState(QProcess::Running); // User can call kill()/terminate() from the stateChanged() slot // so check before proceeding if (!pid) return; // if (threadData->eventDispatcher) { processFinishedNotifier = new QWinEventNotifier(pid->hProcess, q); QObject::connect(processFinishedNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_processDied())); processFinishedNotifier->setEnabled(true); notifier = new QTimer(q); QObject::connect(notifier, SIGNAL(timeout()), q, SLOT(_q_notified())); notifier->start(NOTIFYTIMEOUT); // } // give the process a chance to start ... Sleep(SLEEPMIN*2); _q_startupNotification(); } bool K3bQProcessPrivate::processStarted() { return processState == QProcess::Running; } qint64 K3bQProcessPrivate::bytesAvailableFromStdout() const { if (stdoutChannel.pipe[0] == INVALID_Q_PIPE) return 0; DWORD bytesAvail = 0; #if !defined(Q_OS_WINCE) PeekNamedPipe(stdoutChannel.pipe[0], 0, 0, 0, &bytesAvail, 0); #if defined QPROCESS_DEBUG qDebug("K3bQProcessPrivate::bytesAvailableFromStdout() == %d", bytesAvail); #endif if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) { QByteArray buf(bytesAvail, 0); DWORD bytesRead = 0; if (ReadFile(stdoutChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) { HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdout) { DWORD bytesWritten = 0; WriteFile(hStdout, buf.data(), bytesRead, &bytesWritten, 0); } } bytesAvail = 0; } #endif return bytesAvail; } qint64 K3bQProcessPrivate::bytesAvailableFromStderr() const { if (stderrChannel.pipe[0] == INVALID_Q_PIPE) return 0; DWORD bytesAvail = 0; #if !defined(Q_OS_WINCE) PeekNamedPipe(stderrChannel.pipe[0], 0, 0, 0, &bytesAvail, 0); #if defined QPROCESS_DEBUG qDebug("K3bQProcessPrivate::bytesAvailableFromStderr() == %d", bytesAvail); #endif if (processChannelMode == QProcess::ForwardedChannels && bytesAvail > 0) { QByteArray buf(bytesAvail, 0); DWORD bytesRead = 0; if (ReadFile(stderrChannel.pipe[0], buf.data(), buf.size(), &bytesRead, 0) && bytesRead > 0) { HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE); if (hStderr) { DWORD bytesWritten = 0; WriteFile(hStderr, buf.data(), bytesRead, &bytesWritten, 0); } } bytesAvail = 0; } #endif return bytesAvail; } qint64 K3bQProcessPrivate::readFromStdout(char *data, qint64 maxlen) { DWORD read = qMin(maxlen, bytesAvailableFromStdout()); DWORD bytesRead = 0; if (read > 0 && !ReadFile(stdoutChannel.pipe[0], data, read, &bytesRead, 0)) return -1; return bytesRead; } qint64 K3bQProcessPrivate::readFromStderr(char *data, qint64 maxlen) { DWORD read = qMin(maxlen, bytesAvailableFromStderr()); DWORD bytesRead = 0; if (read > 0 && !ReadFile(stderrChannel.pipe[0], data, read, &bytesRead, 0)) return -1; return bytesRead; } static BOOL CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId) { DWORD currentProcId = 0; GetWindowThreadProcessId(hwnd, ¤tProcId); if (currentProcId == (DWORD)procId) PostMessage(hwnd, WM_CLOSE, 0, 0); return TRUE; } void K3bQProcessPrivate::terminateProcess() { if (pid) { EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId); PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0); } } void K3bQProcessPrivate::killProcess() { if (pid) TerminateProcess(pid->hProcess, 0xf291); } bool K3bQProcessPrivate::waitForStarted(int) { Q_Q(K3bQProcess); if (processStarted()) return true; if (processError == QProcess::FailedToStart) return false; processError = QProcess::Timedout; q->setErrorString(K3bQProcess::tr("Process operation timed out")); return false; } bool K3bQProcessPrivate::waitForReadyRead(int msecs) { Q_Q(K3bQProcess); #if defined(Q_OS_WINCE) processError = K3bQProcess::ReadError; q->setErrorString(K3bQProcess::tr("Error reading from process")); emit q->error(processError); return false; #endif QIncrementalSleepTimer timer(msecs); forever { if (!writeBuffer.isEmpty() && !_q_canWrite()) return false; if (pipeWriter && pipeWriter->waitForWrite(0)) timer.resetIncrements(); bool readyReadEmitted = false; if (bytesAvailableFromStdout() != 0) { readyReadEmitted = _q_canReadStandardOutput() ? true : readyReadEmitted; timer.resetIncrements(); } if (bytesAvailableFromStderr() != 0) { readyReadEmitted = _q_canReadStandardError() ? true : readyReadEmitted; timer.resetIncrements(); } if (readyReadEmitted) return true; if (!pid) return false; if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { // find the return value if there is noew data to read _q_processDied(); return false; } Sleep(timer.nextSleepTime()); if (timer.hasTimedOut()) break; } processError = QProcess::Timedout; q->setErrorString(K3bQProcess::tr("Process operation timed out")); return false; } bool K3bQProcessPrivate::waitForBytesWritten(int msecs) { Q_Q(K3bQProcess); #if defined(Q_OS_WINCE) processError = K3bQProcess::ReadError; q->setErrorString(K3bQProcess::tr("Error reading from process")); emit q->error(processError); return false; #endif QIncrementalSleepTimer timer(msecs); forever { // Check if we have any data pending: the pipe writer has // bytes waiting to written, or it has written data since the // last time we called pipeWriter->waitForWrite(). bool pendingDataInPipe = pipeWriter && (pipeWriter->bytesToWrite() || pipeWriter->hadWritten()); // If we don't have pending data, and our write buffer is // empty, we fail. if (!pendingDataInPipe && writeBuffer.isEmpty()) return false; // If we don't have pending data and we do have data in our // write buffer, try to flush that data over to the pipe // writer. Fail on error. if (!pendingDataInPipe) { if (!_q_canWrite()) return false; } // Wait for the pipe writer to acknowledge that it has // written. This will succeed if either the pipe writer has // already written the data, or if it manages to write data // within the given timeout. If the write buffer was non-empty // and the pipeWriter is now dead, that means _q_canWrite() // destroyed the writer after it successfully wrote the last // batch. if (!pipeWriter || pipeWriter->waitForWrite(0)) return true; // If we wouldn't write anything, check if we can read stdout. if (bytesAvailableFromStdout() != 0) { _q_canReadStandardOutput(); timer.resetIncrements(); } // Check if we can read stderr. if (bytesAvailableFromStderr() != 0) { _q_canReadStandardError(); timer.resetIncrements(); } // Check if the process died while reading. if (!pid) return false; // Wait for the process to signal any change in its state, // such as incoming data, or if the process died. if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) { _q_processDied(); return false; } // Only wait for as long as we've been asked. if (timer.hasTimedOut()) break; } processError = QProcess::Timedout; q->setErrorString(K3bQProcess::tr("Process operation timed out")); return false; } bool K3bQProcessPrivate::waitForFinished(int msecs) { Q_Q(K3bQProcess); #if defined QPROCESS_DEBUG qDebug("K3bQProcessPrivate::waitForFinished(%d)", msecs); #endif QIncrementalSleepTimer timer(msecs); forever { if (!writeBuffer.isEmpty() && !_q_canWrite()) return false; if (pipeWriter && pipeWriter->waitForWrite(0)) timer.resetIncrements(); if (bytesAvailableFromStdout() != 0) { _q_canReadStandardOutput(); timer.resetIncrements(); } if (bytesAvailableFromStderr() != 0) { _q_canReadStandardError(); timer.resetIncrements(); } if (!pid) return true; if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) { _q_processDied(); return true; } if (timer.hasTimedOut()) break; } processError = QProcess::Timedout; q->setErrorString(K3bQProcess::tr("Process operation timed out")); return false; } void K3bQProcessPrivate::findExitCode() { DWORD theExitCode; if (GetExitCodeProcess(pid->hProcess, &theExitCode)) { exitCode = theExitCode; //### for now we assume a crash if exit code is less than -1 or the magic number crashed = (exitCode == 0xf291 || (int)exitCode < 0); } } void K3bQProcessPrivate::flushPipeWriter() { if (pipeWriter && pipeWriter->bytesToWrite() > 0) { pipeWriter->waitForWrite(ULONG_MAX); } } qint64 K3bQProcessPrivate::pipeWriterBytesToWrite() const { return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0); } qint64 K3bQProcessPrivate::writeToStdin(const char *data, qint64 maxlen) { Q_Q(K3bQProcess); #if defined(Q_OS_WINCE) processError = K3bQProcess::WriteError; q->setErrorString(K3bQProcess::tr("Error writing to process")); emit q->error(processError); return -1; #endif if (!pipeWriter) { pipeWriter = new QWindowsPipeWriter(stdinChannel.pipe[1], q); pipeWriter->start(); } return pipeWriter->write(data, maxlen); } bool K3bQProcessPrivate::waitForWrite(int msecs) { Q_Q(K3bQProcess); if (!pipeWriter || pipeWriter->waitForWrite(msecs)) return true; processError = QProcess::Timedout; q->setErrorString(K3bQProcess::tr("Process operation timed out")); return false; } void K3bQProcessPrivate::_q_notified() { notifier->stop(); if (!writeBuffer.isEmpty() && (!pipeWriter || pipeWriter->waitForWrite(0))) _q_canWrite(); if (bytesAvailableFromStdout()) _q_canReadStandardOutput(); if (bytesAvailableFromStderr()) _q_canReadStandardError(); if (processState != QProcess::NotRunning) notifier->start(NOTIFYTIMEOUT); } bool K3bQProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid) { #if defined(Q_OS_WINCE) Q_UNUSED(workingDir); QString args = qt_create_commandline(QString(), arguments); #else QString args = qt_create_commandline(program, arguments); #endif bool success = false; PROCESS_INFORMATION pinfo; #ifdef UNICODE if (!(QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based)) { #if defined(Q_OS_WINCE) QString fullPathProgram = program; if (!QDir::isAbsolutePath(fullPathProgram)) fullPathProgram.prepend(QDir::currentPath().append(QLatin1String("/"))); fullPathProgram.replace(QLatin1String("/"), QLatin1String("\\")); success = CreateProcessW((WCHAR*)fullPathProgram.utf16(), (WCHAR*)args.utf16(), 0, 0, false, CREATE_NEW_CONSOLE, 0, 0, 0, &pinfo); #else STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; success = CreateProcessW(0, (WCHAR*)args.utf16(), 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0, workingDir.isEmpty() ? (const WCHAR *)0 : (const WCHAR *)workingDir.utf16(), &startupInfo, &pinfo); #endif } else #endif // UNICODE { #ifndef Q_OS_WINCE STARTUPINFOA startupInfo = { sizeof( STARTUPINFOA ), 0, 0, 0, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; success = CreateProcessA(0, args.toLocal8Bit().data(), 0, 0, FALSE, CREATE_NEW_CONSOLE, 0, workingDir.isEmpty() ? (LPCSTR)0 : workingDir.toLocal8Bit().constData(), &startupInfo, &pinfo); #endif // Q_OS_WINCE } if (success) { CloseHandle(pinfo.hThread); CloseHandle(pinfo.hProcess); if (pid) *pid = pinfo.dwProcessId; } return success; } QT_END_NAMESPACE #endif // QT_NO_PROCESS diff --git a/libk3bdevice/k3bcdtext.h b/libk3bdevice/k3bcdtext.h index 27425b581..7255d20a9 100644 --- a/libk3bdevice/k3bcdtext.h +++ b/libk3bdevice/k3bcdtext.h @@ -1,151 +1,151 @@ /* * * Copyright (C) 2003-2009 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2009 Sebastian Trueg * * 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. */ #ifndef _K3B_CDTEXT_H_ #define _K3B_CDTEXT_H_ #include "k3bdevice_export.h" #include #include namespace K3b { namespace Device { class LIBK3BDEVICE_EXPORT TrackCdText { public: TrackCdText(); TrackCdText( const TrackCdText& ); ~TrackCdText(); TrackCdText& operator=( const TrackCdText& ); void clear(); QString title() const; QString performer() const; QString songwriter() const; QString composer() const; QString arranger() const; QString message() const; QString isrc() const; // TODO: use the real CD-TEXT charset (a modified ISO8859-1) void setTitle( const QString& s ); void setPerformer( const QString& s ); void setSongwriter( const QString& s ); void setComposer( const QString& s ); void setArranger( const QString& s ); void setMessage( const QString& s ); void setIsrc( const QString& s ); bool isEmpty() const; bool operator==( const TrackCdText& ) const; bool operator!=( const TrackCdText& ) const; private: class Private; QSharedDataPointer d; friend class CdText; }; class LIBK3BDEVICE_EXPORT CdText { public: CdText(); CdText( const unsigned char* data, int len ); /** * \sa setRawPackData */ - CdText( const QByteArray& ); + explicit CdText( const QByteArray& ); CdText( const CdText& ); ~CdText(); CdText& operator=( const CdText& ); int count() const; /** * If i < count() behaviour is undefined. */ TrackCdText operator[]( int i ) const; TrackCdText& operator[]( int i ); void insert( int index, const TrackCdText& ); /** * Will create a new empty TrackCdText if i is out of scope. */ TrackCdText& track( int i ); TrackCdText track( int i ) const; /** * Parse cd text from raw pack data. The data as provided is cached * and will be returned by rawPackData until the cdtext object is changed. */ void setRawPackData( const unsigned char*, int ); void setRawPackData( const QByteArray& ); QByteArray rawPackData() const; bool empty() const; bool isEmpty() const; void clear(); QString title() const; QString performer() const; QString songwriter() const; QString composer() const; QString arranger() const; QString message() const; QString discId() const; QString upcEan() const; // TODO: use the real CD-TEXT charset (a modified ISO8859-1) void setTitle( const QString& s ); void setPerformer( const QString& s ); void setSongwriter( const QString& s ); void setComposer( const QString& s ); void setArranger( const QString& s ); void setMessage( const QString& s ); void setDiscId( const QString& s ); void setUpcEan( const QString& s ); void debug() const; /** * Returns false if found a crc error in the raw cdtext block or it has a * wrong length. */ static bool checkCrc( const unsigned char*, int ); static bool checkCrc( const QByteArray& ); bool operator==( const CdText& ) const; bool operator!=( const CdText& ) const; private: class Private; QSharedDataPointer d; }; } } #endif diff --git a/plugins/decoder/flac/k3bflacdecoder.cpp b/plugins/decoder/flac/k3bflacdecoder.cpp index 2d86c8d9f..1cec5d870 100644 --- a/plugins/decoder/flac/k3bflacdecoder.cpp +++ b/plugins/decoder/flac/k3bflacdecoder.cpp @@ -1,489 +1,489 @@ /* * FLAC decoder module for K3b. * Based on the Ogg Vorbis module for same. * Copyright (C) 1998-2008 Sebastian Trueg * Copyright (C) 2003-2004 John Steele Scott * * 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 "k3bflacdecoder.h" #include "k3bplugin_i18n.h" #include #include #include #include #include #include #include #include #include #include K3B_EXPORT_PLUGIN(k3bflacdecoder, K3bFLACDecoderFactory) #ifdef ENABLE_TAGLIB #include #include #endif #if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6 #define LEGACY_FLAC #else #undef LEGACY_FLAC #endif class K3bFLACDecoder::Private #ifdef LEGACY_FLAC : public FLAC::Decoder::SeekableStream #else : public FLAC::Decoder::Stream #endif { public: void open(QFile* f) { file = f; file->open(QIODevice::ReadOnly); //TODO port me to kde4 //internalBuffer->flush(); set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO); set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT); init(); process_until_end_of_metadata(); } void cleanup() { file->close(); finish(); delete comments; comments = 0; } Private(QFile* f) #ifdef LEGACY_FLAC : FLAC::Decoder::SeekableStream(), #else : FLAC::Decoder::Stream(), #endif comments(0) { internalBuffer = new QBuffer(); internalBuffer->open(QIODevice::ReadWrite); open(f); } ~Private() { cleanup(); delete internalBuffer; } bool seekToFrame(int frame); QFile* file; QBuffer* internalBuffer; FLAC::Metadata::VorbisComment* comments; unsigned rate; unsigned channels; unsigned bitsPerSample; unsigned maxFramesize; unsigned maxBlocksize; unsigned minFramesize; unsigned minBlocksize; FLAC__uint64 samples; protected: #ifdef LEGACY_FLAC virtual FLAC__SeekableStreamDecoderReadStatus read_callback(FLAC__byte buffer[], unsigned *bytes); virtual FLAC__SeekableStreamDecoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset); virtual FLAC__SeekableStreamDecoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset); virtual FLAC__SeekableStreamDecoderLengthStatus length_callback(FLAC__uint64 *stream_length); #else virtual FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes); virtual FLAC__StreamDecoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset); virtual FLAC__StreamDecoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset); virtual FLAC__StreamDecoderLengthStatus length_callback(FLAC__uint64 *stream_length); #endif virtual bool eof_callback(); virtual void error_callback(FLAC__StreamDecoderErrorStatus){}; virtual void metadata_callback(const ::FLAC__StreamMetadata *metadata); virtual ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]); }; bool K3bFLACDecoder::Private::seekToFrame(int frame) { FLAC__uint64 sample = static_cast(frame) * rate / 75; return seek_absolute(sample); } bool K3bFLACDecoder::Private::eof_callback() { return file->atEnd(); } #ifdef LEGACY_FLAC FLAC__SeekableStreamDecoderReadStatus K3bFLACDecoder::Private::read_callback(FLAC__byte buffer[], unsigned *bytes) { long retval = file->read((char *)buffer, (*bytes)); if(-1 == retval) { return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; } else { (*bytes) = retval; return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK; } } #else FLAC__StreamDecoderReadStatus K3bFLACDecoder::Private::read_callback(FLAC__byte buffer[], size_t *bytes) { long retval = file->read((char *)buffer, (*bytes)); if(-1 == retval) { return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } else { (*bytes) = retval; return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } } #endif #ifdef LEGACY_FLAC FLAC__SeekableStreamDecoderSeekStatus K3bFLACDecoder::Private::seek_callback(FLAC__uint64 absolute_byte_offset) { if(!file->seek(absolute_byte_offset)) return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR; else return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK; } #else FLAC__StreamDecoderSeekStatus K3bFLACDecoder::Private::seek_callback(FLAC__uint64 absolute_byte_offset) { if(file->seek(absolute_byte_offset) == false) return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; else return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } #endif #ifdef LEGACY_FLAC FLAC__SeekableStreamDecoderTellStatus K3bFLACDecoder::Private::tell_callback(FLAC__uint64 *absolute_byte_offset) { (*absolute_byte_offset) = file->pos(); return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK; } #else FLAC__StreamDecoderTellStatus K3bFLACDecoder::Private::tell_callback(FLAC__uint64 *absolute_byte_offset) { (*absolute_byte_offset) = file->pos(); return FLAC__STREAM_DECODER_TELL_STATUS_OK; } #endif #ifdef LEGACY_FLAC FLAC__SeekableStreamDecoderLengthStatus K3bFLACDecoder::Private::length_callback(FLAC__uint64 *stream_length) { (*stream_length) = file->size(); return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK; } #else FLAC__StreamDecoderLengthStatus K3bFLACDecoder::Private::length_callback(FLAC__uint64 *stream_length) { (*stream_length) = file->size(); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } #endif void K3bFLACDecoder::Private::metadata_callback(const FLAC__StreamMetadata *metadata) { switch (metadata->type) { case FLAC__METADATA_TYPE_STREAMINFO: channels = metadata->data.stream_info.channels; rate = metadata->data.stream_info.sample_rate; bitsPerSample = metadata->data.stream_info.bits_per_sample; samples = metadata->data.stream_info.total_samples; maxFramesize = metadata->data.stream_info.max_framesize; minFramesize = metadata->data.stream_info.min_framesize; maxBlocksize = metadata->data.stream_info.max_blocksize; minBlocksize = metadata->data.stream_info.min_blocksize; break; case FLAC__METADATA_TYPE_VORBIS_COMMENT: comments = new FLAC::Metadata::VorbisComment((FLAC__StreamMetadata *)metadata, true); break; default: break; } } FLAC__StreamDecoderWriteStatus K3bFLACDecoder::Private::write_callback(const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) { unsigned i, j; // Note that in canDecode we made sure that the input is 1-16 bit stereo or mono. unsigned samples = frame->header.blocksize; - for(i=0; i < samples; i++) { + for(i=0; i < samples; ++i) { // in FLAC channel 0 is left, 1 is right - for(j=0; j < this->channels; j++) { + for(j=0; j < this->channels; ++j) { FLAC__int32 value = (buffer[j][i])<<(16 - frame->header.bits_per_sample); internalBuffer->putChar(value >> 8); // msb internalBuffer->putChar(value & 0xFF); // lsb } } // Rewind the buffer so the decode method will take data from the beginning. internalBuffer->seek(0); return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } K3bFLACDecoder::K3bFLACDecoder( QObject* parent ) : K3b::AudioDecoder( parent) { d = 0; } K3bFLACDecoder::~K3bFLACDecoder() { delete d; } void K3bFLACDecoder::cleanup() { if (d) { d->cleanup(); d->open(new QFile(filename())); } else d = new Private(new QFile(filename())); } bool K3bFLACDecoder::analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ) { cleanup(); frames = (unsigned long)ceil((d->samples * 75.0)/d->rate); samplerate = d->rate; ch = d->channels; // add meta info if( d->comments != 0 ) { qDebug() << "(K3bFLACDecoder) unpacking Vorbis tags"; for( unsigned int i = 0; i < d->comments->get_num_comments(); ++i ) { QString key = QString::fromUtf8( d->comments->get_comment(i).get_field_name(), d->comments->get_comment(i).get_field_name_length() ); QString value = QString::fromUtf8( d->comments->get_comment(i).get_field_value(), d->comments->get_comment(i).get_field_value_length() ); if( key.toUpper() == "TITLE" ) addMetaInfo( META_TITLE, value ); else if( key.toUpper() == "ARTIST" ) addMetaInfo( META_ARTIST, value ); else if( key.toUpper() == "DESCRIPTION" ) addMetaInfo( META_COMMENT, value ); } } #ifdef ENABLE_TAGLIB if ((d->comments == 0) || (d->comments->get_num_comments() == 0)) { // no Vorbis comments, check for ID3 tags qDebug() << "(K3bFLACDecoder) using taglib to read tag"; TagLib::FLAC::File f( QFile::encodeName(filename()) ); if( f.isOpen() ) { addMetaInfo( META_TITLE, TStringToQString( f.tag()->title() ) ); addMetaInfo( META_ARTIST, TStringToQString( f.tag()->artist() ) ); addMetaInfo( META_COMMENT, TStringToQString( f.tag()->comment() ) ); } } #endif return true; } bool K3bFLACDecoder::initDecoderInternal() { cleanup(); return true; } int K3bFLACDecoder::decodeInternal( char* _data, int maxLen ) { int bytesToCopy; int bytesCopied; int bytesAvailable; #ifdef LEGACY_FLAC if(d->internalBuffer->size() == 0) { // want more data switch(d->get_state()) { case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM: d->finish(); break; case FLAC__SEEKABLE_STREAM_DECODER_OK: if(! d->process_single()) return -1; break; default: return -1; } } #else if(d->internalBuffer->size() == 0) { // want more data if(d->get_state() == FLAC__STREAM_DECODER_END_OF_STREAM) d->finish(); else if(d->get_state() < FLAC__STREAM_DECODER_END_OF_STREAM) { if(! d->process_single()) return -1; } else return -1; } #endif bytesAvailable = d->internalBuffer->size() - d->internalBuffer->pos(); bytesToCopy = qMin(maxLen, bytesAvailable); bytesCopied = (int)d->internalBuffer->read(_data, bytesToCopy); if(bytesCopied == bytesAvailable) { // reset the buffer d->internalBuffer->close(); d->internalBuffer->open(QIODevice::ReadWrite|QIODevice::Truncate); } return bytesCopied; } bool K3bFLACDecoder::seekInternal( const K3b::Msf& pos ) { return d->seekToFrame(pos.totalFrames()); } QString K3bFLACDecoder::fileType() const { return i18n("FLAC"); } QStringList K3bFLACDecoder::supportedTechnicalInfos() const { return QString( i18n("Channels") + ';' + i18n("Sampling Rate") + ';' + i18n("Sample Size") ).split( ';' ); } QString K3bFLACDecoder::technicalInfo( const QString& info ) const { if( d->comments != 0 ) { if( info == i18n("Vendor") ) #ifdef FLAC_NEWER_THAN_1_1_1 return QString::fromUtf8((char*)d->comments->get_vendor_string()); #else return QString::fromUtf8(d->comments->get_vendor_string().get_field()); #endif else if( info == i18n("Channels") ) return QString::number(d->channels); else if( info == i18n("Sampling Rate") ) return i18n("%1 Hz",d->rate); else if( info == i18n("Sample Size") ) return i18np("1 bit","%1 bits",d->bitsPerSample); } return QString(); } K3bFLACDecoderFactory::K3bFLACDecoderFactory( QObject* parent, const QVariantList& ) : K3b::AudioDecoderFactory( parent ) { } K3bFLACDecoderFactory::~K3bFLACDecoderFactory() { } K3b::AudioDecoder* K3bFLACDecoderFactory::createDecoder( QObject* parent) const { return new K3bFLACDecoder( parent ); } bool K3bFLACDecoderFactory::canDecode( const QUrl& url ) { // buffer large enough to read an ID3 tag header char buf[10]; // Note: since file is created on the stack it will be closed automatically // by its destructor when this method (i.e. canDecode) returns. QFile file(url.toLocalFile()); if(!file.open(QIODevice::ReadOnly)) { qDebug() << "(K3bFLACDecoder) Could not open file " << url.toLocalFile(); return false; } // look for a fLaC magic number or ID3 tag header if(10 != file.read(buf, 10)) { qDebug() << "(K3bFLACDecorder) File " << url.toLocalFile() << " is too small to be a FLAC file" << endl; return false; } if(0 == memcmp(buf, "ID3", 3)) { // Found ID3 tag, try and seek past it. qDebug() << "(K3bFLACDecorder) File " << url.toLocalFile() << ": found ID3 tag"; // See www.id3.org for details of the header, note that the size field // unpacks to 7-bit bytes, then the +10 is for the header itself. int pos; pos = ((buf[6]<<21)|(buf[7]<<14)|(buf[8]<<7)|buf[9]) + 10; qDebug() << "(K3bFLACDecoder) " << url.toLocalFile() << ": seeking to " << pos << endl; if(!file.seek(pos)) { qDebug() << "(K3bFLACDecoder) " << url.toLocalFile() << ": couldn't seek to " << pos << endl; return false; }else{ // seek was okay, try and read magic number into buf if(4 != file.read(buf, 4)) { qDebug() << "(K3bFLACDecorder) File " << url.toLocalFile() << " has ID3 tag but naught else!" << endl; return false; } } } if(memcmp(buf, "fLaC", 4) != 0) { qDebug() << "(K3bFLACDecoder) " << url.toLocalFile() << ": not a FLAC file"; return false; } FLAC::Metadata::StreamInfo info = FLAC::Metadata::StreamInfo(); FLAC::Metadata::get_streaminfo(url.toLocalFile().toLatin1(), info); if((info.get_channels() <= 2) && (info.get_bits_per_sample() <= 16)) { return true; } else { qDebug() << "(K3bFLACDecoder) " << url.toLocalFile() << ": wrong format:" << endl << " channels: " << QString::number(info.get_channels()) << endl << " samplerate: " << QString::number(info.get_sample_rate()) << endl << " bits/sample: " << QString::number(info.get_bits_per_sample()) << endl; return false; } } #include "k3bflacdecoder.moc" diff --git a/plugins/decoder/mp3/k3bmaddecoder.h b/plugins/decoder/mp3/k3bmaddecoder.h index 0dd97aed8..d35c6c40a 100644 --- a/plugins/decoder/mp3/k3bmaddecoder.h +++ b/plugins/decoder/mp3/k3bmaddecoder.h @@ -1,78 +1,78 @@ /* * * $Id$ * Copyright (C) 2003-2008 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2008 Sebastian Trueg * * 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. */ #ifndef _K3B_MAD_DECODER_H_ #define _K3B_MAD_DECODER_H_ #include "k3baudiodecoder.h" extern "C" { #include } class K3bMadDecoderFactory : public K3b::AudioDecoderFactory { Q_OBJECT public: - K3bMadDecoderFactory( QObject* parent = 0, const QVariantList& args = QVariantList() ); + explicit K3bMadDecoderFactory( QObject* parent = 0, const QVariantList& args = QVariantList() ); ~K3bMadDecoderFactory(); bool canDecode( const QUrl& filename ); int pluginSystemVersion() const { return K3B_PLUGIN_SYSTEM_VERSION; } K3b::AudioDecoder* createDecoder( QObject* parent = 0 ) const; }; class K3bMadDecoder : public K3b::AudioDecoder { Q_OBJECT public: - K3bMadDecoder( QObject* parent = 0 ); + explicit K3bMadDecoder( QObject* parent = 0 ); ~K3bMadDecoder(); QString metaInfo( MetaDataField ); void cleanup(); bool seekInternal( const K3b::Msf& ); QString fileType() const; QStringList supportedTechnicalInfos() const; QString technicalInfo( const QString& ) const; protected: bool analyseFileInternal( K3b::Msf& frames, int& samplerate, int& ch ); bool initDecoderInternal(); int decodeInternal( char* _data, int maxLen ); private: unsigned long countFrames(); inline unsigned short linearRound( mad_fixed_t fixed ); bool createPcmSamples( mad_synth* ); static int MaxAllowedRecoverableErrors; class MadDecoderPrivate; MadDecoderPrivate* d; }; #endif diff --git a/plugins/encoder/ogg/k3boggvorbisencoder.cpp b/plugins/encoder/ogg/k3boggvorbisencoder.cpp index 8618e0fa0..8bf29eae6 100644 --- a/plugins/encoder/ogg/k3boggvorbisencoder.cpp +++ b/plugins/encoder/ogg/k3boggvorbisencoder.cpp @@ -1,445 +1,445 @@ /* * * Copyright (C) 2003-2008 Sebastian Trueg * Copyright (C) 2010 Michal Malek * * This file is part of the K3b project. * Copyright (C) 1998-2008 Sebastian Trueg * * 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 "k3boggvorbisencoder.h" #include "k3boggvorbisencoderdefaults.h" #include "k3bcore.h" #include "k3bplugin_i18n.h" #include #include #include #include #include // for the random generator #include #include K3B_EXPORT_PLUGIN(k3boggvorbisdecoder, K3bOggVorbisEncoder) // quality levels -1 to 10 map to 0 to 11 static const int s_rough_average_quality_level_bitrates[] = { 45, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 400 }; // quality levels -1 to 10 map to 0 to 11 // static const char* s_ogg_quality_level_strings[] = { // I18N_NOOP("Low quality"), // I18N_NOOP(""), // I18N_NOOP(""), // I18N_NOOP(""), // I18N_NOOP(""), // I18N_NOOP("targetted %1 kbps"), // I18N_NOOP("targetted %1 kbps"), // I18N_NOOP("targetted %1 kbps"), // I18N_NOOP(""), // I18N_NOOP(""), // I18N_NOOP(""), // I18N_NOOP(""), // }; // THIS IS BASED ON THE OGG VORBIS LIB EXAMPLE // BECAUSE OF THE LACK OF DOCUMENTATION class K3bOggVorbisEncoder::Private { public: Private() : manualBitrate(false), qualityLevel(4), bitrateUpper(-1), bitrateNominal(-1), bitrateLower(-1), // sampleRate(44100), oggStream(0), oggPage(0), oggPacket(0), vorbisInfo(0), vorbisComment(0), vorbisDspState(0), vorbisBlock(0), headersWritten(false) { } // encoding settings bool manualBitrate; // 0 to 10 -> 0.0 - 1.0 int qualityLevel; int bitrateUpper; int bitrateNominal; int bitrateLower; // int sampleRate; // encoding structures ogg_stream_state *oggStream; // take physical pages, weld into a logical stream of packets ogg_page *oggPage; // one Ogg bitstream page. Vorbis packets are inside ogg_packet *oggPacket; // one raw packet of data for decode vorbis_info *vorbisInfo; // struct that stores all the static vorbis bitstream settings vorbis_comment *vorbisComment; // struct that stores all the user comments vorbis_dsp_state *vorbisDspState; // central working state for the packet->PCM decoder vorbis_block *vorbisBlock; // local working space for packet->PCM decode bool headersWritten; }; K3bOggVorbisEncoder::K3bOggVorbisEncoder( QObject* parent, const QVariantList& ) : K3b::AudioEncoder( parent ) { d = new Private(); } K3bOggVorbisEncoder::~K3bOggVorbisEncoder() { cleanup(); delete d; } bool K3bOggVorbisEncoder::initEncoderInternal( const QString&, const K3b::Msf& /*length*/, const MetaData& metaData ) { cleanup(); // load user settings loadConfig(); d->oggPage = new ogg_page; d->oggPacket = new ogg_packet; d->vorbisInfo = new vorbis_info; vorbis_info_init( d->vorbisInfo ); int ret = 0; if( d->manualBitrate ) { qDebug() << "(K3bOggVorbisEncoder) calling: " << "vorbis_encode_init( d->vorbisInfo, 2, 44100, " << (d->bitrateUpper != -1 ? d->bitrateUpper*1000 : -1) << ", " << (d->bitrateNominal != -1 ? d->bitrateNominal*1000 : -1) << ", " << (d->bitrateLower != -1 ? d->bitrateLower*1000 : -1) << " );" << endl; ret = vorbis_encode_init( d->vorbisInfo, 2, // 2 channels: stereo 44100, d->bitrateUpper != -1 ? d->bitrateUpper*1000 : -1, d->bitrateNominal != -1 ? d->bitrateNominal*1000 : -1, d->bitrateLower != -1 ? d->bitrateLower*1000 : -1 ); } else { if( d->qualityLevel < -1 ) d->qualityLevel = -1; else if( d->qualityLevel > 10 ) d->qualityLevel = 10; qDebug() << "(K3bOggVorbisEncoder) calling: " << "vorbis_encode_init_vbr( d->vorbisInfo, 2, 44100, " << (float)d->qualityLevel/10.0 << ");" << endl; ret = vorbis_encode_init_vbr( d->vorbisInfo, 2, // 2 channels: stereo 44100, (float)d->qualityLevel/10.0 ); } if( ret ) { qDebug() << "(K3bOggVorbisEncoder) vorbis_encode_init failed: " << ret; cleanup(); return false; } // init the comment stuff d->vorbisComment = new vorbis_comment; vorbis_comment_init( d->vorbisComment ); // add the encoder tag (so everybody knows we did it! ;) vorbis_comment_add_tag( d->vorbisComment, QByteArray("ENCODER").data(), QByteArray("K3bOggVorbisEncoderPlugin").data() ); // set up the analysis state and auxiliary encoding storage d->vorbisDspState = new vorbis_dsp_state; d->vorbisBlock = new vorbis_block; vorbis_analysis_init( d->vorbisDspState, d->vorbisInfo ); vorbis_block_init( d->vorbisDspState, d->vorbisBlock ); // set up our packet->stream encoder // pick a random serial number; that way we can more likely build // chained streams just by concatenation d->oggStream = new ogg_stream_state; srand( time(0) ); ogg_stream_init( d->oggStream, rand() ); // Set meta data for( MetaData::const_iterator it = metaData.constBegin(); it != metaData.constEnd(); ++it ) { QByteArray key; switch( it.key() ) { case META_TRACK_TITLE: key = "TITLE"; break; case META_TRACK_ARTIST: key = "ARTIST"; break; case META_ALBUM_TITLE: key = "ALBUM"; break; case META_ALBUM_COMMENT: key = "DESCRIPTION"; break; case META_YEAR: key = "DATE"; break; case META_TRACK_NUMBER: key = "TRACKNUMBER"; break; case META_GENRE: key = "GENRE"; break; default: break; } if( !key.isEmpty() ) { vorbis_comment_add_tag( d->vorbisComment, key.data(), it.value().toString().toUtf8().data() ); } } return true; } bool K3bOggVorbisEncoder::writeOggHeaders() { if( !d->oggStream ) { qDebug() << "(K3bOggVorbisEncoder) call to writeOggHeaders without init."; return false; } if( d->headersWritten ) { qDebug() << "(K3bOggVorbisEncoder) headers already written."; return true; } // // Vorbis streams begin with three headers; the initial header (with // most of the codec setup parameters) which is mandated by the Ogg // bitstream spec. The second header holds any comment fields. The // third header holds the bitstream codebook. We merely need to // make the headers, then pass them to libvorbis one at a time; // libvorbis handles the additional Ogg bitstream constraints // ogg_packet header; ogg_packet header_comm; ogg_packet header_code; vorbis_analysis_headerout( d->vorbisDspState, d->vorbisComment, &header, &header_comm, &header_code); // automatically placed in its own page ogg_stream_packetin( d->oggStream, &header ); ogg_stream_packetin( d->oggStream, &header_comm ); ogg_stream_packetin( d->oggStream, &header_code ); // // This ensures the actual // audio data will start on a new page, as per spec // QByteArray data; while( ogg_stream_flush( d->oggStream, d->oggPage ) ) { writeData( (char*)d->oggPage->header, d->oggPage->header_len ); writeData( (char*)d->oggPage->body, d->oggPage->body_len ); } d->headersWritten = true; return true; } qint64 K3bOggVorbisEncoder::encodeInternal( const char* data, qint64 len ) { if( !d->headersWritten ) if( !writeOggHeaders() ) return -1; // expose the buffer to submit data float** buffer = vorbis_analysis_buffer( d->vorbisDspState, len/4 ); // uninterleave samples qint64 i = 0; - for( i = 0; i < len/4; i++ ) { + for( i = 0; i < len/4; ++i ) { buffer[0][i]=( (data[i*4+1]<<8) | (0x00ff&(int)data[i*4]) ) / 32768.f; buffer[1][i]=( (data[i*4+3]<<8) | (0x00ff&(int)data[i*4+2]) ) / 32768.f; } // tell the library how much we actually submitted vorbis_analysis_wrote( d->vorbisDspState, i ); return flushVorbis(); } long K3bOggVorbisEncoder::flushVorbis() { // vorbis does some data preanalysis, then divvies up blocks for // more involved (potentially parallel) processing. Get a single // block for encoding now long writtenData = 0; while( vorbis_analysis_blockout( d->vorbisDspState, d->vorbisBlock ) == 1 ) { // analysis vorbis_analysis( d->vorbisBlock, 0 ); vorbis_bitrate_addblock( d->vorbisBlock ); while( vorbis_bitrate_flushpacket( d->vorbisDspState, d->oggPacket ) ) { // weld the packet into the bitstream ogg_stream_packetin( d->oggStream, d->oggPacket ); // write out pages (if any) while( ogg_stream_pageout( d->oggStream, d->oggPage ) ) { writeData( (char*)d->oggPage->header, d->oggPage->header_len ); writeData( (char*)d->oggPage->body, d->oggPage->body_len ); writtenData += ( d->oggPage->header_len + d->oggPage->body_len ); } } } return writtenData; } void K3bOggVorbisEncoder::finishEncoderInternal() { if( d->vorbisDspState ) { vorbis_analysis_wrote( d->vorbisDspState, 0 ); flushVorbis(); } else qDebug() << "(K3bOggVorbisEncoder) call to finishEncoderInternal without init."; } void K3bOggVorbisEncoder::cleanup() { if( d->oggStream ) { ogg_stream_clear( d->oggStream ); delete d->oggStream; d->oggStream = 0; } if( d->vorbisBlock ) { vorbis_block_clear( d->vorbisBlock ); delete d->vorbisBlock; d->vorbisBlock = 0; } if( d->vorbisDspState ) { vorbis_dsp_clear( d->vorbisDspState ); delete d->vorbisDspState; d->vorbisDspState = 0; } if( d->vorbisComment ) { vorbis_comment_clear( d->vorbisComment ); delete d->vorbisComment; d->vorbisComment = 0; } if( d->vorbisInfo ) { vorbis_info_clear( d->vorbisInfo ); delete d->vorbisInfo; d->vorbisInfo = 0; } // ogg_page and ogg_packet structs always point to storage in // libvorbis. They're never freed or manipulated directly if( d->oggPage ) { delete d->oggPage; d->oggPage = 0; } if( d->oggPacket ) { delete d->oggPacket; d->oggPacket = 0; } d->headersWritten = false; } void K3bOggVorbisEncoder::loadConfig() { KSharedConfig::Ptr c = KSharedConfig::openConfig(); KConfigGroup grp(c, "K3bOggVorbisEncoderPlugin" ); d->manualBitrate = grp.readEntry( "manual bitrate", DEFAULT_MANUAL_BITRATE ); d->qualityLevel = grp.readEntry( "quality level", DEFAULT_QUALITY_LEVEL ); d->bitrateUpper = grp.readEntry( "bitrate upper", DEFAULT_BITRATE_UPPER ); d->bitrateNominal = grp.readEntry( "bitrate nominal", DEFAULT_BITRATE_NOMINAL ); d->bitrateLower = grp.readEntry( "bitrate lower", DEFAULT_BITRATE_LOWER ); // d->sampleRate = c->readEntry( "samplerate", DEFAULT_SAMPLERATE ); } QString K3bOggVorbisEncoder::fileTypeComment( const QString& ) const { return i18n("Ogg-Vorbis"); } long long K3bOggVorbisEncoder::fileSize( const QString&, const K3b::Msf& msf ) const { KSharedConfig::Ptr c = KSharedConfig::openConfig(); KConfigGroup grp(c, "K3bOggVorbisEncoderPlugin" ); // the following code is based on the size estimation from the audiocd kioslave // TODO: reimplement. if( !grp.readEntry( "manual bitrate", DEFAULT_MANUAL_BITRATE ) ) { // Estimated numbers based on the Vorbis FAQ: // http://www.xiph.org/archives/vorbis-faq/200203/0030.html // static long vorbis_q_bitrate[] = { 45, 60, 74, 86, 106, 120, 152, // 183, 207, 239, 309, 440 }; int qualityLevel = grp.readEntry( "quality level", DEFAULT_QUALITY_LEVEL ); if( qualityLevel < -1 ) qualityLevel = -1; else if( qualityLevel > 10 ) qualityLevel = 10; return ( (msf.totalFrames()/75) * s_rough_average_quality_level_bitrates[qualityLevel+1] * 1000 ) / 8; } else { return (msf.totalFrames()/75) * grp.readEntry( "bitrate nominal", 160 ) * 1000 / 8; } } #include "k3boggvorbisencoder.moc"