You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/translators/freedb_util.cpp

377 lines
11 KiB

/***************************************************************************
* *
* Modified from cd-discid.c, found at http://lly.org/~rcw/cd-discid/ *
* *
* Copyright (c) 1999-2003 Robert Woodcock <rcw@debian.org> *
* *
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of version 2 of the GNU General Public License as *
* published by the Free Software Foundation; *
* *
***************************************************************************/
#include "freedbimporter.h"
#include "../tellico_debug.h"
using Tellico::Import::FreeDBImporter;
extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
/* Porting credits:
* Solaris: David Champion <dgc@uchicago.edu>
* FreeBSD: Niels Bakker <niels@bakker.net>
* OpenBSD: Marcus Daniel <danielm@uni-muenster.de>
* NetBSD: Chris Gilbert <chris@NetBSD.org>
* MacOSX: Evan Jones <ejones@uwaterloo.ca> http://www.eng.uwaterloo.ca/~ejones/
*/
#if defined(__linux__)
// see http://bugs.kde.org/show_bug.cgi?id=86188
#ifdef __STRICT_ANSI__
#undef __STRICT_ANSI__
#define _ANSI_WAS_HERE_
#endif
#include <linux/types.h>
#include <linux/cdrom.h>
#ifdef _ANSI_WAS_HERE_
#define __STRICT_ANSI__
#undef _ANSI_WAS_HERE_
#endif
#define cdte_track_address cdte_addr.lba
#elif defined(sun) && defined(unix) && defined(__SVR4)
#include <sys/cdio.h>
#define CD_MSF_OFFSET 150
#define CD_FRAMES 75
/* According to David Schweikert <dws@ee.ethz.ch>, cd-discid needs this
* to compile on Solaris */
#define cdte_track_address cdte_addr.lba
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <netinet/in.h>
#include <sys/cdio.h>
#define CDROM_LBA CD_LBA_FORMAT /* first frame is 0 */
#define CD_MSF_OFFSET 150 /* MSF offset of first frame */
#define CD_FRAMES 75 /* per second */
#define CDROM_LEADOUT 0xAA /* leadout track */
#define CDROMREADTOCHDR CDIOREADTOCHEADER
#define CDROMREADTOCENTRY CDIOREADTOCENTRY
#define cdrom_tochdr ioc_toc_header
#define cdth_trk0 starting_track
#define cdth_trk1 ending_track
#define cdrom_tocentry ioc_read_toc_single_entry
#define cdte_track track
#define cdte_format address_format
#define cdte_track_address entry.addr.lba
#elif defined(__OpenBSD__) || defined(__NetBSD__)
#include <netinet/in.h>
#include <sys/cdio.h>
#define CDROM_LBA CD_LBA_FORMAT /* first frame is 0 */
#define CD_MSF_OFFSET 150 /* MSF offset of first frame */
#define CD_FRAMES 75 /* per second */
#define CDROM_LEADOUT 0xAA /* leadout track */
#define CDROMREADTOCHDR CDIOREADTOCHEADER
#define cdrom_tochdr ioc_toc_header
#define cdth_trk0 starting_track
#define cdth_trk1 ending_track
#define cdrom_tocentry cd_toc_entry
#define cdte_track track
#define cdte_track_address addr.lba
#elif defined(__APPLE__)
#include <sys/types.h>
#include <IOKit/storage/IOCDTypes.h>
#include <IOKit/storage/IOCDMediaBSDClient.h>
#define CD_FRAMES 75 /* per second */
#define CD_MSF_OFFSET 150 /* MSF offset of first frame */
#define cdrom_tochdr CDDiscInfo
#define cdth_trk0 numberOfFirstTrack
/* NOTE: Judging by the name here, we might have to do this:
* hdr.lastTrackNumberInLastSessionMSB << 8 *
* sizeof(hdr.lastTrackNumberInLastSessionLSB)
* | hdr.lastTrackNumberInLastSessionLSB; */
#define cdth_trk1 lastTrackNumberInLastSessionLSB
#define cdrom_tocentry CDTrackInfo
#define cdte_track_address trackStartAddress
#else
# warning "Your OS isn't supported yet for CDDB lookup."
#endif /* os selection */
}
#include <config.h>
namespace {
class CloseDrive {
public:
CloseDrive(int d) : drive(d) {}
~CloseDrive() { ::close(drive); }
private:
int drive;
};
}
TQValueList<uint> FreeDBImporter::offsetList(const TQCString& drive_, TQValueList<uint>& trackLengths_) {
TQValueList<uint> list;
int drive = ::open(drive_.data(), O_RDONLY | O_NONBLOCK);
CloseDrive closer(drive);
if(drive < 0) {
return list;
}
cdrom_tochdr hdr;
#if defined(__APPLE__)
dk_cd_read_disc_info_t discInfoParams;
::memset(&discInfoParams, 0, sizeof(discInfoParams));
discInfoParams.buffer = &hdr;
discInfoParams.bufferLength = sizeof(hdr);
if(ioctl(drive, DKIOCCDREADDISCINFO, &discInfoParams) < 0
|| discInfoParams.bufferLength != sizeof(hdr)) {
return list;
}
#else
if(ioctl(drive, CDROMREADTOCHDR, &hdr) < 0) {
return list;
}
#endif
// uchar first = hdr.cdth_trk0;
uchar last = hdr.cdth_trk1;
cdrom_tocentry* TocEntry = new cdrom_tocentry[last+1];
#if defined(__OpenBSD__)
ioc_read_toc_entry t;
t.starting_track = 0;
#elif defined(__NetBSD__)
ioc_read_toc_entry t;
t.starting_track = 1;
#endif
#if defined(__OpenBSD__) || defined(__NetBSD__)
t.address_format = CDROM_LBA;
t.data_len = (last + 1) * sizeof(cdrom_tocentry);
t.data = TocEntry;
if (::ioctl(drive, CDIOREADTOCENTRYS, (char *) &t) < 0)
return list;
#elif defined(__APPLE__)
dk_cd_read_track_info_t trackInfoParams;
::memset(&trackInfoParams, 0, sizeof(trackInfoParams));
trackInfoParams.addressType = kCDTrackInfoAddressTypeTrackNumber;
trackInfoParams.bufferLength = sizeof(*TocEntry);
for(int i = 0; i < last; ++i) {
trackInfoParams.address = i + 1;
trackInfoParams.buffer = &TocEntry[i];
::ioctl(drive, DKIOCCDREADTRACKINFO, &trackInfoParams);
}
/* MacOS X on G5-based systems does not report valid info for
* TocEntry[last-1].lastRecordedAddress + 1, so we compute the start
* of leadout from the start+length of the last track instead
*/
TocEntry[last].cdte_track_address = TocEntry[last-1].trackSize + TocEntry[last-1].trackStartAddress;
#else /* FreeBSD, Linux, Solaris */
for(uint i = 0; i < last; ++i) {
/* tracks start with 1, but I must start with 0 on OpenBSD */
TocEntry[i].cdte_track = i + 1;
TocEntry[i].cdte_format = CDROM_LBA;
::ioctl(drive, CDROMREADTOCENTRY, &TocEntry[i]);
}
TocEntry[last].cdte_track = CDROM_LEADOUT;
TocEntry[last].cdte_format = CDROM_LBA;
::ioctl(drive, CDROMREADTOCENTRY, &TocEntry[last]);
#endif
#if defined(__FreeBSD__)
TocEntry[last].cdte_track_address = ntohl(TocEntry[last].cdte_track_address);
#endif
for(uint i = 0; i < last; ++i) {
#if defined(__FreeBSD__)
TocEntry[i].cdte_track_address = ntohl(TocEntry[i].cdte_track_address);
#endif
list.append(TocEntry[i].cdte_track_address + CD_MSF_OFFSET);
}
list.append(TocEntry[0].cdte_track_address + CD_MSF_OFFSET);
list.append(TocEntry[last].cdte_track_address + CD_MSF_OFFSET);
// hey, these are track lengths! :P
trackLengths_.clear();
for(uint i = 0; i < last; ++i) {
trackLengths_.append((TocEntry[i+1].cdte_track_address - TocEntry[i].cdte_track_address) / CD_FRAMES);
}
delete[] TocEntry;
return list;
}
inline
ushort from2Byte(uchar* d) {
return (d[0] << 8 & 0xFF00) | (d[1] & 0xFF);
}
#define SIZE 61
// mostly taken from kover and k3b
// licensed under GPL
FreeDBImporter::CDText FreeDBImporter::getCDText(const TQCString& drive_) {
CDText cdtext;
#ifdef USE_CDTEXT
// only works for linux ATM
#if defined(__linux__)
int drive = ::open(drive_.data(), O_RDONLY | O_NONBLOCK);
CloseDrive closer(drive);
if(drive < 0) {
return cdtext;
}
cdrom_generic_command m_cmd;
::memset(&m_cmd, 0, sizeof(cdrom_generic_command));
int dataLen;
int format = 5;
uint track = 0;
uchar buffer[2048];
m_cmd.cmd[0] = 0x43;
m_cmd.cmd[1] = 0x0;
m_cmd.cmd[2] = format & 0x0F;
m_cmd.cmd[6] = track;
m_cmd.cmd[8] = 2; // we only read the length first
m_cmd.buffer = buffer;
m_cmd.buflen = 2;
m_cmd.data_direction = CGC_DATA_READ;
if(ioctl(drive, CDROM_SEND_PACKET, &m_cmd) != 0) {
myDebug() << "FreeDBImporter::getCDText() - access error" << endl;
return cdtext;
}
dataLen = from2Byte(buffer) + 2;
m_cmd.cmd[7] = 2048 >> 8;
m_cmd.cmd[8] = 2048 & 0xFF;
m_cmd.buflen = 2048;
::ioctl(drive, CDROM_SEND_PACKET, &m_cmd);
dataLen = from2Byte(buffer) + 2;
::memset(buffer, 0, dataLen);
m_cmd.cmd[7] = dataLen >> 8;
m_cmd.cmd[8] = dataLen;
m_cmd.buffer = buffer;
m_cmd.buflen = dataLen;
::ioctl(drive, CDROM_SEND_PACKET, &m_cmd);
bool rc = false;
int buffer_size = (buffer[0] << 8) | buffer[1];
buffer_size -= 2;
char data[SIZE];
short pos_data = 0;
char old_block_no = 0xff;
for(uchar* bufptr = buffer + 4; buffer_size >= 18; bufptr += 18, buffer_size -= 18) {
char code = *bufptr;
if((code & 0x80) != 0x80) {
continue;
}
char block_no = *(bufptr + 3);
if(block_no & 0x80) {
myDebug() << "FreeDBImporter::readCDText() - double byte code not supported" << endl;
continue;
}
block_no &= 0x70;
if(block_no != old_block_no) {
if(rc) {
break;
}
pos_data = 0;
old_block_no = block_no;
}
track = *(bufptr + 1);
if(track & 0x80) {
continue;
}
uchar* txtstr = bufptr + 4;
int length = 11;
while(length >= 0 && *(txtstr + length) == '\0') {
--length;
}
++length;
if(length < 12) {
++length;
}
for(int j = 0; j < length; ++j) {
char c = *(txtstr + j);
if(c == '\0') {
data[pos_data] = c;
if(track == 0) {
if(code == (char)0xFFFFFF80) {
cdtext.title = TQString::fromUtf8(data);
} else if(code == (char)0xFFFFFF81) {
cdtext.artist = TQString::fromUtf8(data);
} else if (code == (char)0xFFFFFF85) {
cdtext.message = TQString::fromUtf8(data);
}
} else {
if(code == (char)0xFFFFFF80) {
if(cdtext.trackTitles.size() < track) {
cdtext.trackTitles.resize(track);
}
cdtext.trackTitles[track-1] = TQString::fromUtf8(data);
} else if(code == (char)0xFFFFFF81) {
if(cdtext.trackArtists.size() < track) {
cdtext.trackArtists.resize(track);
}
cdtext.trackArtists[track-1] = TQString::fromUtf8(data);
}
}
rc = true;
pos_data = 0;
++track;
} else if(pos_data < (SIZE - 1)) {
data[pos_data++] = c;
}
}
}
if(cdtext.trackTitles.size() != cdtext.trackArtists.size()) {
size_t size = TQMAX(cdtext.trackTitles.size(), cdtext.trackArtists.size());
cdtext.trackTitles.resize(size);
cdtext.trackArtists.resize(size);
}
#endif
#endif
return cdtext;
}
#undef SIZE