|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* $Id: sourceheader 380067 2005-01-19 13:03:46Z trueg $
|
|
|
|
* Copyright (C) 2005 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This file is part of the K3b project.
|
|
|
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
* See the file "COPYING" for the exact licensing terms.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "k3bmedium.h"
|
|
|
|
|
|
|
|
#include <k3bdeviceglobals.h>
|
|
|
|
#include <k3bglobals.h>
|
|
|
|
#include <k3biso9660.h>
|
|
|
|
#include <k3biso9660backend.h>
|
|
|
|
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <tdeio/global.h>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal class used by K3bMedium
|
|
|
|
*/
|
|
|
|
class K3bMedium::Data : public TDEShared
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Data();
|
|
|
|
|
|
|
|
K3bDevice::Device* device;
|
|
|
|
K3bDevice::DiskInfo diskInfo;
|
|
|
|
K3bDevice::Toc toc;
|
|
|
|
K3bDevice::CdText cdText;
|
|
|
|
TQValueList<int> writingSpeeds;
|
|
|
|
K3bIso9660SimplePrimaryDescriptor isoDesc;
|
|
|
|
int content;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
K3bMedium::Data::Data()
|
|
|
|
: device( 0 ),
|
|
|
|
content( K3bMedium::CONTENT_NONE )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bMedium::K3bMedium()
|
|
|
|
{
|
|
|
|
d = new Data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
K3bMedium::K3bMedium( const K3bMedium& other )
|
|
|
|
{
|
|
|
|
d = other.d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bMedium::K3bMedium( K3bDevice::Device* dev )
|
|
|
|
{
|
|
|
|
d = new Data;
|
|
|
|
d->device = dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bMedium::~K3bMedium()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bMedium& K3bMedium::operator=( const K3bMedium& other )
|
|
|
|
{
|
|
|
|
if( this != &other )
|
|
|
|
d = other.d;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bMedium::detach()
|
|
|
|
{
|
|
|
|
if( d.count() > 1 )
|
|
|
|
d = new Data( *d );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bMedium::setDevice( K3bDevice::Device* dev )
|
|
|
|
{
|
|
|
|
if( d->device != dev ) {
|
|
|
|
reset();
|
|
|
|
d->device = dev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
K3bDevice::Device* K3bMedium::device() const
|
|
|
|
{
|
|
|
|
return d->device;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const K3bDevice::DiskInfo& K3bMedium::diskInfo() const
|
|
|
|
{
|
|
|
|
return d->diskInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const K3bDevice::Toc& K3bMedium::toc() const
|
|
|
|
{
|
|
|
|
return d->toc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const K3bDevice::CdText& K3bMedium::cdText() const
|
|
|
|
{
|
|
|
|
return d->cdText;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TQValueList<int>& K3bMedium::writingSpeeds() const
|
|
|
|
{
|
|
|
|
return d->writingSpeeds;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int K3bMedium::content() const
|
|
|
|
{
|
|
|
|
return d->content;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const K3bIso9660SimplePrimaryDescriptor& K3bMedium::iso9660Descriptor() const
|
|
|
|
{
|
|
|
|
return d->isoDesc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bMedium::reset()
|
|
|
|
{
|
|
|
|
detach();
|
|
|
|
|
|
|
|
d->diskInfo = K3bDevice::DiskInfo();
|
|
|
|
d->toc.clear();
|
|
|
|
d->cdText.clear();
|
|
|
|
d->writingSpeeds.clear();
|
|
|
|
d->content = CONTENT_NONE;
|
|
|
|
|
|
|
|
// clear the desc
|
|
|
|
d->isoDesc = K3bIso9660SimplePrimaryDescriptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bMedium::update()
|
|
|
|
{
|
|
|
|
if( d->device ) {
|
|
|
|
reset();
|
|
|
|
|
|
|
|
d->diskInfo = d->device->diskInfo();
|
|
|
|
|
|
|
|
if( d->diskInfo.diskState() != K3bDevice::STATE_NO_MEDIA ) {
|
|
|
|
kdDebug() << "(K3bMedium) found medium:" << endl
|
|
|
|
<< "=====================================================" << endl;
|
|
|
|
d->diskInfo.debug();
|
|
|
|
kdDebug() << "=====================================================" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( diskInfo().diskState() == K3bDevice::STATE_COMPLETE ||
|
|
|
|
diskInfo().diskState() == K3bDevice::STATE_INCOMPLETE ) {
|
|
|
|
d->toc = d->device->readToc();
|
|
|
|
if( d->toc.contentType() == K3bDevice::AUDIO ||
|
|
|
|
d->toc.contentType() == K3bDevice::MIXED )
|
|
|
|
d->cdText = d->device->readCdText();
|
|
|
|
}
|
|
|
|
|
|
|
|
if( diskInfo().mediaType() & K3bDevice::MEDIA_WRITABLE ) {
|
|
|
|
d->writingSpeeds = d->device->determineSupportedWriteSpeeds();
|
|
|
|
}
|
|
|
|
|
|
|
|
analyseContent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bMedium::analyseContent()
|
|
|
|
{
|
|
|
|
// set basic content types
|
|
|
|
switch( toc().contentType() ) {
|
|
|
|
case K3bDevice::AUDIO:
|
|
|
|
d->content = CONTENT_AUDIO;
|
|
|
|
break;
|
|
|
|
case K3bDevice::DATA:
|
|
|
|
case K3bDevice::DVD:
|
|
|
|
d->content = CONTENT_DATA;
|
|
|
|
break;
|
|
|
|
case K3bDevice::MIXED:
|
|
|
|
d->content = CONTENT_AUDIO|CONTENT_DATA;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
d->content = CONTENT_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// analyze filesystem
|
|
|
|
if( d->content & CONTENT_DATA ) {
|
|
|
|
//kdDebug() << "(K3bMedium) Checking file system." << endl;
|
|
|
|
|
|
|
|
unsigned long startSec = 0;
|
|
|
|
|
|
|
|
if( diskInfo().numSessions() > 1 ) {
|
|
|
|
// We use the last data track
|
|
|
|
// this way we get the latest session on a ms cd
|
|
|
|
K3bDevice::Toc::const_iterator it = toc().end();
|
|
|
|
--it; // this is valid since there is at least one data track
|
|
|
|
while( it != toc().begin() && (*it).type() != K3bDevice::Track::DATA )
|
|
|
|
--it;
|
|
|
|
startSec = (*it).firstSector().lba();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// use first data track
|
|
|
|
K3bDevice::Toc::const_iterator it = toc().begin();
|
|
|
|
while( it != toc().end() && (*it).type() != K3bDevice::Track::DATA )
|
|
|
|
++it;
|
|
|
|
startSec = (*it).firstSector().lba();
|
|
|
|
}
|
|
|
|
|
|
|
|
//kdDebug() << "(K3bMedium) Checking file system at " << startSec << endl;
|
|
|
|
|
|
|
|
// force the backend since we don't need decryption
|
|
|
|
// which just slows down the whole process
|
|
|
|
K3bIso9660 iso( new K3bIso9660DeviceBackend( d->device ) );
|
|
|
|
iso.setStartSector( startSec );
|
|
|
|
iso.setPlainIso9660( true );
|
|
|
|
if( iso.open() ) {
|
|
|
|
d->isoDesc = iso.primaryDescriptor();
|
|
|
|
kdDebug() << "(K3bMedium) found volume id from start sector " << startSec
|
|
|
|
<< ": '" << d->isoDesc.volumeId << "'" << endl;
|
|
|
|
|
|
|
|
if( diskInfo().isDvdMedia() ) {
|
|
|
|
// Every VideoDVD needs to have a VIDEO_TS.IFO file
|
|
|
|
if( iso.firstIsoDirEntry()->entry( "VIDEO_TS/VIDEO_TS.IFO" ) != 0 )
|
|
|
|
d->content |= CONTENT_VIDEO_DVD;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
kdDebug() << "(K3bMedium) checking for VCD." << endl;
|
|
|
|
|
|
|
|
// check for VCD
|
|
|
|
const K3bIso9660Entry* vcdEntry = iso.firstIsoDirEntry()->entry( "VCD/INFO.VCD" );
|
|
|
|
const K3bIso9660Entry* svcdEntry = iso.firstIsoDirEntry()->entry( "SVCD/INFO.SVD" );
|
|
|
|
const K3bIso9660File* vcdInfoFile = 0;
|
|
|
|
if( vcdEntry ) {
|
|
|
|
kdDebug() << "(K3bMedium) found vcd entry." << endl;
|
|
|
|
if( vcdEntry->isFile() )
|
|
|
|
vcdInfoFile = static_cast<const K3bIso9660File*>(vcdEntry);
|
|
|
|
}
|
|
|
|
if( svcdEntry && !vcdInfoFile ) {
|
|
|
|
kdDebug() << "(K3bMedium) found svcd entry." << endl;
|
|
|
|
if( svcdEntry->isFile() )
|
|
|
|
vcdInfoFile = static_cast<const K3bIso9660File*>(svcdEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( vcdInfoFile ) {
|
|
|
|
char buffer[8];
|
|
|
|
|
|
|
|
if ( vcdInfoFile->read( 0, buffer, 8 ) == 8 &&
|
|
|
|
( !tqstrncmp( buffer, "VIDEO_CD", 8 ) ||
|
|
|
|
!tqstrncmp( buffer, "SUPERVCD", 8 ) ||
|
|
|
|
!tqstrncmp( buffer, "HQ-VCD ", 8 ) ) )
|
|
|
|
d->content |= CONTENT_VIDEO_CD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // opened iso9660
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString K3bMedium::shortString( bool useContent ) const
|
|
|
|
{
|
|
|
|
TQString mediaTypeString = K3bDevice::mediaTypeString( diskInfo().mediaType(), true );
|
|
|
|
|
|
|
|
if( diskInfo().diskState() == K3bDevice::STATE_UNKNOWN ) {
|
|
|
|
return i18n("No medium information");
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( diskInfo().diskState() == K3bDevice::STATE_NO_MEDIA ) {
|
|
|
|
return i18n("No medium present");
|
|
|
|
}
|
|
|
|
|
|
|
|
else if( diskInfo().diskState() == K3bDevice::STATE_EMPTY ) {
|
|
|
|
return i18n("Empty %1 medium").arg( mediaTypeString );
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
if( useContent ) {
|
|
|
|
// AUDIO + MIXED
|
|
|
|
if( toc().contentType() == K3bDevice::AUDIO ||
|
|
|
|
toc().contentType() == K3bDevice::MIXED ) {
|
|
|
|
if( !cdText().performer().isEmpty() || !cdText().title().isEmpty() ) {
|
|
|
|
return TQString("%1 - %2 (%3)")
|
|
|
|
.arg( cdText().performer() )
|
|
|
|
.arg( cdText().title() )
|
|
|
|
.arg( toc().contentType() == K3bDevice::AUDIO ? i18n("Audio CD") : i18n("Mixed CD") );
|
|
|
|
}
|
|
|
|
else if( toc().contentType() == K3bDevice::AUDIO ) {
|
|
|
|
return i18n("Audio CD");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return i18n("%1 (Mixed CD)").arg( beautifiedVolumeId() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// DATA CD and DVD
|
|
|
|
else if( !volumeId().isEmpty() ) {
|
|
|
|
if( content() & CONTENT_VIDEO_DVD ) {
|
|
|
|
return TQString("%1 (%2)").arg( beautifiedVolumeId() ).arg( i18n("Video DVD") );
|
|
|
|
}
|
|
|
|
else if( content() & CONTENT_VIDEO_CD ) {
|
|
|
|
return TQString("%1 (%2)").arg( beautifiedVolumeId() ).arg( i18n("Video CD") );
|
|
|
|
}
|
|
|
|
else if( diskInfo().diskState() == K3bDevice::STATE_INCOMPLETE ) {
|
|
|
|
return i18n("%1 (Appendable Data %2)").arg( beautifiedVolumeId(), mediaTypeString );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return i18n("%1 (Complete Data %2)").arg( beautifiedVolumeId(), mediaTypeString );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if( diskInfo().diskState() == K3bDevice::STATE_INCOMPLETE ) {
|
|
|
|
return i18n("Appendable Data %1").arg( mediaTypeString );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return i18n("Complete Data %1").arg( mediaTypeString );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// without content
|
|
|
|
else {
|
|
|
|
if( diskInfo().diskState() == K3bDevice::STATE_INCOMPLETE ) {
|
|
|
|
return i18n("Appendable %1 medium").arg( mediaTypeString );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return i18n("Complete %1 medium").arg( mediaTypeString );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString K3bMedium::longString() const
|
|
|
|
{
|
|
|
|
TQString s = TQString("<p><nobr><b>%1 %2</b> (%3)</nobr>"
|
|
|
|
"<p>")
|
|
|
|
.arg( d->device->vendor() )
|
|
|
|
.arg( d->device->description() )
|
|
|
|
.arg( d->device->blockDeviceName() )
|
|
|
|
+ shortString( true );
|
|
|
|
|
|
|
|
if( diskInfo().diskState() == K3bDevice::STATE_COMPLETE ||
|
|
|
|
diskInfo().diskState() == K3bDevice::STATE_INCOMPLETE ) {
|
|
|
|
s += "<br>" + i18n("%1 in %n track", "%1 in %n tracks", toc().count() )
|
|
|
|
.arg( TDEIO::convertSize(diskInfo().size().mode1Bytes() ) );
|
|
|
|
if( diskInfo().numSessions() > 1 )
|
|
|
|
s += i18n(" and %n session", " and %n sessions", diskInfo().numSessions() );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( diskInfo().diskState() == K3bDevice::STATE_EMPTY ||
|
|
|
|
diskInfo().diskState() == K3bDevice::STATE_INCOMPLETE )
|
|
|
|
s += "<br>" + i18n("Free space: %1")
|
|
|
|
.arg( TDEIO::convertSize( diskInfo().remainingSize().mode1Bytes() ) );
|
|
|
|
|
|
|
|
if( !diskInfo().empty() && diskInfo().rewritable() )
|
|
|
|
s += "<br>" + i18n("Capacity: %1")
|
|
|
|
.arg( TDEIO::convertSize( diskInfo().capacity().mode1Bytes() ) );
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TQString& K3bMedium::volumeId() const
|
|
|
|
{
|
|
|
|
return iso9660Descriptor().volumeId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString K3bMedium::beautifiedVolumeId() const
|
|
|
|
{
|
|
|
|
const TQString& oldId = volumeId();
|
|
|
|
TQString newId;
|
|
|
|
|
|
|
|
bool newWord = true;
|
|
|
|
for( unsigned int i = 0; i < oldId.length(); ++i ) {
|
|
|
|
TQChar c = oldId[i];
|
|
|
|
//
|
|
|
|
// first let's handle the cases where we do not change
|
|
|
|
// the id anyway
|
|
|
|
//
|
|
|
|
// In case the id already contains spaces or lower case chars
|
|
|
|
// it is likely that it already looks good and does ignore
|
|
|
|
// the restricted iso9660 charset (like almost every project
|
|
|
|
// created with K3b)
|
|
|
|
//
|
|
|
|
if( c.isLetter() && c.lower() == c )
|
|
|
|
return oldId;
|
|
|
|
else if( c.isSpace() )
|
|
|
|
return oldId;
|
|
|
|
|
|
|
|
// replace underscore with space
|
|
|
|
else if( c.unicode() == 95 ) {
|
|
|
|
newId.append( ' ' );
|
|
|
|
newWord = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// from here on only upper case chars and numbers and stuff
|
|
|
|
else if( c.isLetter() ) {
|
|
|
|
if( newWord ) {
|
|
|
|
newId.append( c );
|
|
|
|
newWord = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
newId.append( c.lower() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
newId.append( c );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return newId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bMedium::operator==( const K3bMedium& other )
|
|
|
|
{
|
|
|
|
if( this->d == other.d )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return( this->device() == other.device() &&
|
|
|
|
this->diskInfo() == other.diskInfo() &&
|
|
|
|
this->toc() == other.toc() &&
|
|
|
|
this->cdText() == other.cdText() &&
|
|
|
|
this->content() == other.content() &&
|
|
|
|
this->iso9660Descriptor() == other.iso9660Descriptor() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bMedium::operator!=( const K3bMedium& other )
|
|
|
|
{
|
|
|
|
if( this->d == other.d )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return( this->device() != other.device() ||
|
|
|
|
this->diskInfo() != other.diskInfo() ||
|
|
|
|
this->toc() != other.toc() ||
|
|
|
|
this->cdText() != other.cdText() ||
|
|
|
|
this->content() != other.content() ||
|
|
|
|
this->iso9660Descriptor() != other.iso9660Descriptor() );
|
|
|
|
}
|