#include "CDDBSupport.h"
#include <Alert.h>
#include <Debug.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <FindDirectory.h>
#include <fs_attr.h>
#include <fs_index.h>
#include <NetAddress.h>
#include <Path.h>
#include <Query.h>
#include <UTF8.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#ifdef DEBUG_CDDB
#define STRACE(x) printf x
#else
#define STRACE(x)
#endif
const int kTerminatingSignal = SIGINT;
struct TrackRecord {
int8 unused[5];
int8 min;
int8 sec;
int8 frame;
};
static void
DoNothing(int)
{
}
static int32
cddb_sum(int n)
{
char buf[12];
int32 ret = 0;
sprintf(buf, "%u", n);
for (const char *p = buf; *p != '\0'; p++)
ret += (*p - '0');
return ret;
}
BString
GetLineFromString(const char *string)
{
if (!string)
return NULL;
BString out(string);
int32 lineFeed = out.FindFirst("\n");
if (lineFeed > 0)
out.Truncate(lineFeed);
return out;
}
CDDBData::CDDBData(int32 discId)
:
fDiscID(discId),
fYear(-1)
{
}
CDDBData::CDDBData(const CDDBData &from)
:
fDiscID(from.fDiscID),
fArtist(from.fArtist),
fAlbum(from.fAlbum),
fGenre(from.fGenre),
fDiscTime(from.fDiscTime),
fYear(from.fYear)
{
STRACE(("CDDBData::Copy Constructor\n"));
for (int32 i = 0; i < from.fTrackList.CountItems(); i++) {
BString *string = (BString*)from.fTrackList.ItemAt(i);
CDAudioTime *time = (CDAudioTime*)from.fTimeList.ItemAt(i);
if (!string || !time)
continue;
AddTrack(string->String(), *time);
}
}
CDDBData::~CDDBData()
{
STRACE(("CDDBData::~CDDBData\n"));
_EmptyLists();
}
CDDBData &
CDDBData::operator=(const CDDBData &from)
{
_EmptyLists();
fDiscID = from.fDiscID;
fArtist = from.fArtist;
fAlbum = from.fAlbum;
fGenre = from.fGenre;
fDiscTime = from.fDiscTime;
fYear = from.fYear;
for (int32 i = 0; i < from.fTrackList.CountItems(); i++) {
BString *string = (BString*)from.fTrackList.ItemAt(i);
CDAudioTime *time = (CDAudioTime*)from.fTimeList.ItemAt(i);
if (!string || !time)
continue;
AddTrack(string->String(),*time);
}
return *this;
}
void
CDDBData::MakeEmpty()
{
STRACE(("CDDBData::MakeEmpty\n"));
_EmptyLists();
fDiscID = -1;
fArtist = "";
fAlbum = "";
fGenre = "";
fDiscTime.SetMinutes(-1);
fDiscTime.SetSeconds(-1);
fYear = -1;
}
void
CDDBData::_EmptyLists()
{
STRACE(("CDDBData::_EmptyLists\n"));
for (int32 i = 0; i < fTrackList.CountItems(); i++) {
BString *string = (BString*)fTrackList.ItemAt(i);
delete string;
}
fTrackList.MakeEmpty();
for (int32 j = 0; j < fTimeList.CountItems(); j++) {
CDAudioTime *time = (CDAudioTime*)fTimeList.ItemAt(j);
delete time;
}
fTimeList.MakeEmpty();
}
status_t
CDDBData::Load(const entry_ref &ref)
{
STRACE(("CDDBData::Load(%s)\n", ref.name));
BFile file(&ref, B_READ_ONLY);
if (file.InitCheck() != B_OK) {
STRACE(("CDDBData::Load failed\n"));
return file.InitCheck();
}
attr_info info;
if (file.GetAttrInfo("Audio:Genre", &info) == B_OK && info.size > 0) {
char genreData[info.size + 2];
if (file.ReadAttr("Audio:Genre", B_STRING_TYPE, 0, genreData, info.size) > 0)
fGenre = genreData;
}
if (file.GetAttrInfo("Audio:Year", &info) == B_OK && info.size > 0) {
int32 yearData;
if (file.ReadAttr("Audio:Year", B_INT32_TYPE, 0, &yearData, info.size) > 0)
fYear = yearData;
}
if (file.GetAttrInfo("CD:tracks", &info) == B_OK && info.size > 0) {
char trackData[info.size + 2];
if (file.ReadAttr("CD:tracks", B_STRING_TYPE, 0, trackData, info.size) > 0) {
trackData[info.size] = 0;
BString tmp = GetLineFromString(trackData);
char *index;
if (fTrackList.CountItems() > 0)
_EmptyLists();
fArtist = tmp;
fArtist.Truncate(fArtist.FindFirst(" - "));
STRACE(("CDDBData::Load: Artist set to %s\n", fArtist.String()));
fAlbum = tmp.String() + (tmp.FindFirst(" - ") + 3);
STRACE(("CDDBData::Load: Album set to %s\n", fAlbum.String()));
index = strchr(trackData, '\n') + 1;
while (*index) {
tmp = GetLineFromString(index);
if (tmp.CountChars() > 0) {
BString *newTrack = new BString(tmp);
CDAudioTime *time = new CDAudioTime;
time->SetMinutes(0);
time->SetSeconds(0);
STRACE(("CDDBData::Load: Adding Track %s (%ld:%ld)\n", newTrack->String(),
time->minutes, time->seconds));
fTrackList.AddItem(newTrack);
fTimeList.AddItem(time);
}
index = strchr(index,'\n') + 1;
}
return B_NO_INIT;
}
}
return B_ERROR;
}
status_t
CDDBData::Load()
{
BPath path;
if (find_directory(B_USER_DIRECTORY, &path, true) != B_OK)
return B_ERROR;
path.Append("cd");
create_directory(path.Path(), 0755);
BString filename(path.Path());
filename << "/" << Artist() << " - " << Album();
if (filename.Compare("Artist") == 0)
filename << "." << DiscID();
BEntry entry(filename.String());
if (entry.InitCheck() != B_OK)
return entry.InitCheck();
entry_ref ref;
entry.GetRef(&ref);
return Load(ref);
}
status_t
CDDBData::Save()
{
BPath path;
if (find_directory(B_USER_DIRECTORY, &path, true) != B_OK)
return B_ERROR;
path.Append("cd");
create_directory(path.Path(), 0755);
BString filename(path.Path());
filename << "/" << Artist() << " - " << Album();
if (filename.Compare("Artist")==0)
filename << "." << DiscID();
return Save(filename.String());
}
status_t
CDDBData::Save(const char *filename)
{
if (!filename) {
STRACE(("CDDBData::Save failed - NULL filename\n"));
return B_ERROR;
}
BFile file(filename, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
if (file.InitCheck() != B_OK) {
STRACE(("CDDBData::Save failed - couldn't create file %s\n", filename));
return file.InitCheck();
}
BString entry;
char timestring[10];
sprintf(timestring,"%.2ld:%.2ld", fDiscTime.GetMinutes(), fDiscTime.GetSeconds());
entry << fArtist << " - " << fAlbum << "\t" << timestring << "\n";
file.Write(entry.String(), entry.Length());
STRACE(("CDDBData::Save: wrote first line: %s", entry.String()));
BString tracksattr(fArtist);
tracksattr << " - " << fAlbum << "\n";
for (int32 i = 0; i < fTrackList.CountItems(); i++) {
BString *trackstr = (BString *)fTrackList.ItemAt(i);
CDAudioTime *time= (CDAudioTime*)fTimeList.ItemAt(i);
if (!trackstr || !time)
continue;
entry = *trackstr;
sprintf(timestring,"%.2ld:%.2ld", time->GetMinutes(), time->GetSeconds());
entry << "\t" << timestring << "\n";
file.Write(entry.String(), entry.Length());
STRACE(("CDDBData::Save: Wrote line: %s", entry.String()));
tracksattr << *trackstr << "\n";
}
file.WriteAttr("CD:key", B_INT32_TYPE, 0, &fDiscID, sizeof(int32));
STRACE(("CDDBData::Save: Wrote CD identifier: %ld(%lx)\n", fDiscID, fDiscID));
file.WriteAttr("CD:tracks", B_STRING_TYPE, 0, tracksattr.String(), tracksattr.Length() + 1);
if (fGenre.CountChars() > 0)
file.WriteAttr("Audio:Genre",B_STRING_TYPE, 0, fGenre.String(), fGenre.Length() + 1);
if (fYear > 0)
file.WriteAttr("Audio:Year", B_INT32_TYPE, 0, &fYear, sizeof(int32));
return B_OK;
}
uint16
CDDBData::CountTracks() const
{
return fTrackList.CountItems();
}
bool
CDDBData::RenameTrack(const int32 &index, const char *newName)
{
if (!newName) {
STRACE(("CDDBData::RenameTrack failed - NULL newName\n"));
return false;
}
BString *name = (BString*)fTrackList.ItemAt(index);
if (name) {
STRACE(("CDDBData::RenameTrack(%ld,%s)\n", index, newName));
name->SetTo(newName);
return true;
}
STRACE(("CDDBData::RenameTrack failed - invalid index\n"));
return false;
}
void
CDDBData::AddTrack(const char *track, const CDAudioTime &time,
const int16 &index)
{
if (!track) {
STRACE(("CDDBData::AddTrack failed - NULL name\n"));
return;
}
STRACE(("CDDBData::AddTrack(%s, %ld:%.2ld,%d)\n", track, time.minutes, time.seconds, index));
fTrackList.AddItem(new BString(track));
fTimeList.AddItem(new CDAudioTime(time));
}
void
CDDBData::RemoveTrack(const int16 &index)
{
fTrackList.RemoveItem(index);
fTimeList.RemoveItem(index);
STRACE(("CDDBData::RemoveTrack(%d)\n", index));
}
const char *
CDDBData::TrackAt(const int16 &index) const
{
BString *track = (BString*)fTrackList.ItemAt(index);
if (!track)
return NULL;
return track->String();
}
CDAudioTime *
CDDBData::TrackTimeAt(const int16 &index)
{
return (CDAudioTime *)fTimeList.ItemAt(index);
}
CDDBQuery::CDDBQuery(const char *server, int32 port)
:
fServerName(server),
fPort(port),
fConnected(false),
fState(kInitial)
{
}
CDDBQuery::~CDDBQuery()
{
kill_thread(fThread);
}
void
CDDBQuery::SetToSite(const char *server, int32 port)
{
_Disconnect();
fServerName = server;
fPort = port;
fState = kInitial;
}
void
CDDBQuery::SetToCD(const char *path)
{
if (!path)
return;
int device = open(path, O_RDONLY);
if (device < 0)
return;
status_t result = ioctl(device, B_SCSI_GET_TOC, &fSCSIData);
close(device);
if (result != B_OK)
return;
if (fState == kInitial) {
fCDData.SetDiscID(GetDiscID(&fSCSIData));
fCDData.SetDiscTime(GetDiscTime(&fSCSIData));
} else {
int32 discID = GetDiscID(&fSCSIData);
if (fCDData.DiscID() == discID)
return;
fCDData.SetDiscID(discID);
fCDData.SetDiscTime(GetDiscTime(&fSCSIData));
fFrameOffsetString = OffsetsToString(&fSCSIData);
}
result = B_OK;
fState = kReading;
fThread = spawn_thread(&CDDBQuery::_QueryThread, "CDDBLookup", B_NORMAL_PRIORITY, this);
if (fThread >= 0)
resume_thread(fThread);
else {
fState = kError;
result = fThread;
}
}
const char *
CDDBQuery::_GetToken(const char *stream, BString &result)
{
result = "";
while (*stream && *stream <= ' ')
stream++;
while (*stream && *stream > ' ')
result += *stream++;
return stream;
}
status_t
CDDBQuery::GetSites(bool (*eachFunc)(const char *site, int port, const char *latitude,
const char *longitude, const char *description, void *state), void *passThru)
{
if (!_IsConnected())
_Connect();
BString tmp;
tmp = "sites\n";
STRACE((">%s", tmp.String()));
if (fSocket.Send(tmp.String(), tmp.Length()) == -1)
return B_ERROR;
_ReadLine(tmp);
if (tmp.FindFirst("210") == -1) {
_Disconnect();
return B_ERROR;
}
for (;;) {
BString site;
int32 sitePort;
BString latitude;
BString longitude;
BString description;
_ReadLine(tmp);
if (tmp == ".")
break;
const char *scanner = tmp.String();
scanner = _GetToken(scanner, site);
BString portString;
scanner = _GetToken(scanner, portString);
sitePort = atoi(portString.String());
scanner = _GetToken(scanner, latitude);
scanner = _GetToken(scanner, longitude);
description = scanner;
if (eachFunc(site.String(), sitePort, latitude.String(),
longitude.String(), description.String(), passThru))
break;
}
_Disconnect();
return B_OK;
}
bool
CDDBQuery::GetData(CDDBData *data, bigtime_t timeout)
{
if (!data)
return false;
bigtime_t deadline = system_time() + timeout;
while (fState == kReading) {
snooze(50000);
if (system_time() > deadline)
break;
}
if (fState != kDone)
return false;
*data = fCDData;
return true;
}
int32
CDDBQuery::GetDiscID(const scsi_toc *toc)
{
TrackRecord *tocData = (TrackRecord*)&(toc->toc_data[4]);
int32 numTracks = toc->toc_data[3] - toc->toc_data[2] + 1;
int32 sum1 = 0;
int32 sum2 = 0;
for (int index = 0; index < numTracks; index++) {
sum1 += cddb_sum((tocData[index].min * 60) + tocData[index].sec);
sum2 += (tocData[index + 1].min * 60 + tocData[index + 1].sec)
- (tocData[index].min * 60 + tocData[index].sec);
}
int32 discID = ((sum1 % 0xff) << 24) + (sum2 << 8) + numTracks;
return discID;
}
BString
CDDBQuery::OffsetsToString(const scsi_toc *toc)
{
TrackRecord *tocData = (TrackRecord*)&(toc->toc_data[4]);
int32 numTracks = toc->toc_data[3] - toc->toc_data[2] + 1;
BString string;
for (int index = 0; index < numTracks; index++) {
string << tocData[index].min * 4500 + tocData[index].sec * 75
+ tocData[index].frame << ' ';
}
return string;
}
int32
CDDBQuery::GetTrackCount(const scsi_toc *toc)
{
return toc->toc_data[3] - toc->toc_data[2] + 1;
}
CDAudioTime
CDDBQuery::GetDiscTime(const scsi_toc *toc)
{
int16 trackcount = toc->toc_data[3] - toc->toc_data[2] + 1;
TrackRecord *desc = (TrackRecord*)&(toc->toc_data[4]);
CDAudioTime disc;
disc.SetMinutes(desc[trackcount].min);
disc.SetSeconds(desc[trackcount].sec);
return disc;
}
void
CDDBQuery::GetTrackTimes(const scsi_toc *toc, vector<CDAudioTime> ×)
{
TrackRecord *tocData = (TrackRecord*)&(toc->toc_data[4]);
int16 trackCount = toc->toc_data[3] - toc->toc_data[2] + 1;
for (int index = 0; index < trackCount + 1; index++) {
CDAudioTime cdtime;
cdtime.SetMinutes(tocData[index].min);
cdtime.SetSeconds(tocData[index].sec);
times.push_back(cdtime);
}
}
status_t
CDDBQuery::_ReadFromServer(BString &data)
{
char idString[10];
sprintf(idString, "%08lx", fCDData.DiscID());
BString query;
int32 trackCount = GetTrackCount(&fSCSIData);
BString offsetString = OffsetsToString(&fSCSIData);
int32 discLength = (fCDData.DiscTime()->GetMinutes() * 60)
+ fCDData.DiscTime()->GetSeconds();
query << "cddb query " << idString << ' ' << trackCount << ' '
<< offsetString << ' ' << discLength << '\n';
STRACE((">%s", query.String()));
if (fSocket.Send(query.String(), query.Length()) == -1) {
_Disconnect();
return B_ERROR;
}
BString tmp;
_ReadLine(tmp);
STRACE(("<%s", tmp.String()));
BString category;
BString queryDiscID(idString);
if (tmp.FindFirst("200") != 0) {
if (tmp.FindFirst("211") == 0) {
_ReadLine(tmp);
_GetToken(tmp.String(), category);
_GetToken(tmp.String() + category.CountChars(), queryDiscID);
BString throwaway;
_ReadLine(throwaway);
while (throwaway.ByteAt(0) != '.')
_ReadLine(throwaway);
} else {
STRACE(("CDDB lookup error: %s\n", tmp.String()));
fCDData.SetGenre("misc");
return B_NAME_NOT_FOUND;
}
} else {
_GetToken(tmp.String() + 3, category);
}
STRACE(("CDDBQuery::_ReadFromServer: Genre: %s\n", category.String()));
if (!category.Length()) {
STRACE(("Set genre to 'misc'\n"));
category = "misc";
}
query = "";
query << "cddb read " << category << ' ' << queryDiscID << '\n' ;
if (fSocket.Send(query.String(), query.Length()) == -1) {
_Disconnect();
return B_ERROR;
}
while (true) {
_ReadLine(tmp);
if (tmp == "." || tmp == ".\n")
break;
data << tmp << '\n';
}
return B_OK;
}
status_t
CDDBQuery::_Connect()
{
if (fConnected)
_Disconnect();
BNetAddress address;
status_t status = address.SetTo(fServerName.String(), fPort);
if (status != B_OK)
return status;
status = fSocket.Connect(address);
if (status != B_OK)
return status;
fConnected = true;
BString tmp;
_ReadLine(tmp);
_IdentifySelf();
return B_OK;
}
bool
CDDBQuery::_IsConnected() const
{
return fConnected;
}
void
CDDBQuery::_Disconnect()
{
if (fConnected) {
fSocket.Close();
fConnected = false;
}
}
void
CDDBQuery::_ReadLine(BString &buffer)
{
buffer = "";
unsigned char ch;
for (;;) {
if (fSocket.Receive(&ch, 1) <= 0)
break;
if (ch > 0x7f) {
unsigned char *string, *stringindex;
int32 length = buffer.Length();
if ( ((ch & 0xe0) == 0xc0)) {
unsigned char ch2;
if (fSocket.Receive(&ch2, 1) <= 0)
break;
if ( (ch2 & 0xc0) == 0x80) {
string = (unsigned char *)buffer.LockBuffer(length + 10);
stringindex = string + length;
stringindex[0] = ch;
stringindex[1] = ch2;
stringindex[2] = 0;
buffer.UnlockBuffer();
continue;
}
}
char srcstr[2], deststr[5];
int32 srclen, destlen, state;
srcstr[0] = ch;
srcstr[1] = '\0';
srclen = 1;
destlen = 5;
memset(deststr, 0, 5);
if (convert_to_utf8(B_ISO1_CONVERSION, srcstr, &srclen, deststr,
&destlen, &state) == B_OK) {
length = buffer.Length();
string = (unsigned char *)buffer.LockBuffer(length + 10);
stringindex = string + length;
for (int i = 0; i < 5; i++) {
stringindex[i] = deststr[i];
if (!deststr[i])
break;
}
buffer.UnlockBuffer();
} else {
buffer += ch;
}
} else
buffer += ch;
if (ch == '\n')
break;
}
buffer.RemoveAll("\r");
STRACE(("<%s", buffer.String()));
}
status_t
CDDBQuery::_IdentifySelf()
{
BString username, hostname;
username = getenv("USER");
hostname = getenv("HOSTNAME");
if (username.Length() < 1)
username = "baron";
if (hostname.Length() < 1)
hostname = "haiku";
BString tmp;
tmp << "cddb hello " << username << " " << hostname
<< " HaikuCDPlayer 1.0\n";
STRACE((">%s", tmp.String()));
if (fSocket.Send(tmp.String(), tmp.Length())==-1) {
_Disconnect();
return B_ERROR;
}
_ReadLine(tmp);
return B_OK;
}
status_t
CDDBQuery::_OpenContentFile(const int32 &discID)
{
BFile file;
BString predicate;
predicate << "CD:key == " << discID;
entry_ref ref;
BVolumeRoster roster;
BVolume volume;
roster.Rewind();
while (roster.GetNextVolume(&volume) == B_OK) {
if (volume.IsReadOnly() || !volume.IsPersistent() || !volume.KnowsAttr()
|| !volume.KnowsQuery())
continue;
fs_create_index(volume.Device(), "CD:key", B_INT32_TYPE, 0);
BQuery query;
query.SetVolume(&volume);
query.SetPredicate(predicate.String());
if (query.Fetch() != B_OK)
continue;
if (query.GetNextRef(&ref) == B_OK)
break;
}
status_t status = fCDData.Load(ref);
if (status == B_NO_INIT) {
vector<CDAudioTime> times;
GetTrackTimes(&fSCSIData,times);
for (int32 i = 0; i < fCDData.CountTracks(); i++) {
CDAudioTime *item = fCDData.TrackTimeAt(i);
*item = times[i + 1] - times[i];
}
status = B_OK;
}
return status;
}
int32
CDDBQuery::_QueryThread(void *owner)
{
CDDBQuery *query = (CDDBQuery *)owner;
signal(kTerminatingSignal, DoNothing);
if (query->_OpenContentFile(query->fCDData.DiscID()) != B_OK) {
if (query->_Connect() == B_OK) {
BString data;
query->_ReadFromServer(data);
query->_ParseData(data);
query->_WriteFile();
} else {
query->_SetDefaultInfo();
}
}
query->fState = kDone;
query->fThread = -1;
query->fResult = B_OK;
return 0;
}
void
CDDBQuery::_WriteFile()
{
BPath path;
if (find_directory(B_USER_DIRECTORY, &path, true) != B_OK)
return;
path.Append("cd");
create_directory(path.Path(), 0755);
BString filename(path.Path());
filename << "/" << fCDData.Artist() << " - " << fCDData.Album();
if (filename.Compare("Artist") == 0)
filename << "." << fCDData.DiscID();
fCDData.Save(filename.String());
}
void
CDDBQuery::_SetDefaultInfo()
{
for (int16 i = fCDData.CountTracks(); i >= 0; i--)
fCDData.RemoveTrack(i);
vector<CDAudioTime> trackTimes;
GetTrackTimes(&fSCSIData, trackTimes);
int32 trackCount = GetTrackCount(&fSCSIData);
fCDData.SetArtist("Artist");
fCDData.SetAlbum("Audio CD");
fCDData.SetGenre("Misc");
for (int32 i = 0; i < trackCount; i++) {
BString trackname("Track ");
if (i < 9)
trackname << "0";
trackname << i + 1;
CDAudioTime time = trackTimes[i + 1] - trackTimes[i];
fCDData.AddTrack(trackname.String(), time);
}
}
void
CDDBQuery::_ParseData(const BString &data)
{
for (int16 i = fCDData.CountTracks(); i >= 0; i--)
fCDData.RemoveTrack(i);
vector<CDAudioTime> trackTimes;
GetTrackTimes(&fSCSIData,trackTimes);
int32 trackCount = GetTrackCount(&fSCSIData);
if (data.CountChars() < 1) {
fCDData.SetArtist("Artist");
fCDData.SetAlbum("Audio CD");
fCDData.SetGenre("Misc");
for (int32 i = 0; i < trackCount; i++) {
BString trackname("Track ");
if (i < 9)
trackname << "0";
trackname << i + 1;
CDAudioTime time = trackTimes[i + 1] - trackTimes[i];
fCDData.AddTrack(trackname.String(), time);
}
}
int32 pos;
pos = data.FindFirst("DYEAR=");
if (pos > 0) {
BString artist,album;
artist = album = GetLineFromString(data.String() + sizeof("DYEAR")
+ pos);
BAlert *alert = new BAlert("SimplyVorbis", "DYEAR entry found\n", "OK");
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
pos = data.FindFirst("DTITLE=");
if (pos > 0) {
BString artist,album;
artist = album = GetLineFromString(data.String() + sizeof("DTITLE")
+ pos);
pos = artist.FindFirst(" / ");
if (pos > 0)
artist.Truncate(pos);
fCDData.SetArtist(artist.String());
album = album.String() + pos + sizeof(" / ") - 1;
fCDData.SetAlbum(album.String());
}
BString category = GetLineFromString(data.String() + 4);
pos = category.FindFirst(" ");
if (pos > 0)
category.Truncate(pos);
fCDData.SetGenre(category.String());
pos = data.FindFirst("TTITLE0=");
if (pos > 0) {
int32 trackCount = 0;
BString searchString = "TTITLE0=";
while (pos > 0) {
BString trackName = data.String() + pos + searchString.Length();
trackName.Truncate(trackName.FindFirst("\n"));
CDAudioTime tracktime = trackTimes[trackCount + 1]
- trackTimes[trackCount];
fCDData.AddTrack(trackName.String(),tracktime);
trackCount++;
searchString = "TTITLE";
searchString << trackCount << "=";
pos = data.FindFirst(searchString.String(),pos);
}
}
}
void CDDBQuery::SetData(const CDDBData &data)
{
fCDData = data;
}