From 8ff15f1d7e02fb9811c778d98c33e72fe51d4069 Mon Sep 17 00:00:00 2001 From: DarkWyrm Date: Fri, 4 Nov 2005 02:02:34 +0000 Subject: [PATCH] Integrating fixes from current dev. build of SimplyVorbis which includes: Refactored CDDB lookups with Latin-1 support and buggy UTF-8 lookup code Ability to modify and save lookup data to disk Genre is stored as an attribute so as to be compatible with R5 git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@14671 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/cdplayer/CDAudioDevice.cpp | 115 ++++- src/apps/cdplayer/CDAudioDevice.h | 42 +- src/apps/cdplayer/CDDBSupport.cpp | 694 ++++++++++++++++++++++------ src/apps/cdplayer/CDDBSupport.h | 93 +++- src/apps/cdplayer/Jamfile | 2 +- 5 files changed, 754 insertions(+), 192 deletions(-) diff --git a/src/apps/cdplayer/CDAudioDevice.cpp b/src/apps/cdplayer/CDAudioDevice.cpp index 69f3a7da1b..541f760ae0 100644 --- a/src/apps/cdplayer/CDAudioDevice.cpp +++ b/src/apps/cdplayer/CDAudioDevice.cpp @@ -12,6 +12,85 @@ #include #include "scsi.h" +cdaudio_data::cdaudio_data(const int32 &id, const int32 &count, + const int32 &disclength) + : disc_id(id), + track_count(count), + length(disclength) +{ +} + +cdaudio_data::cdaudio_data(const cdaudio_data &from) + : disc_id(from.disc_id), + track_count(from.track_count), + length(from.length) +{ +} + +cdaudio_data & +cdaudio_data::operator=(const cdaudio_data &from) +{ + disc_id = from.disc_id; + track_count = from.track_count; + length = from.length; + frame_offsets = from.frame_offsets; + return *this; +} + +cdaudio_time::cdaudio_time(const int32 min,const int32 &sec) + : minutes(min), + seconds(sec) +{ +} + +cdaudio_time::cdaudio_time(const cdaudio_time &from) + : minutes(from.minutes), + seconds(from.seconds) +{ +} + +cdaudio_time & +cdaudio_time::operator=(const cdaudio_time &from) +{ + minutes = from.minutes; + seconds = from.seconds; + return *this; +} + +cdaudio_time +cdaudio_time::operator+(const cdaudio_time &from) +{ + cdaudio_time time; + + time.minutes = minutes + from.minutes; + time.seconds = seconds + from.seconds; + + while(time.seconds > 59) + { + time.minutes++; + time.seconds-=60; + } + return time; +} + +cdaudio_time +cdaudio_time::operator-(const cdaudio_time &from) +{ + cdaudio_time time; + + int32 tsec = ((minutes * 60) + seconds) - ((from.minutes * 60) + from.seconds); + if(tsec<0) + { + time.minutes = 0; + time.seconds = 0; + return time; + } + + time.minutes = tsec / 60; + time.seconds = tsec % 60; + + return time; +} CDAudioDevice::CDAudioDevice(void) { @@ -289,6 +368,7 @@ CDAudioDevice::SetDrive(const int32 &drive) { fFileHandle = device; fDrivePath = path; + fDriveIndex = drive; return true; } @@ -396,20 +476,6 @@ CDAudioDevice::GetTime(cdaudio_time &track, cdaudio_time &disc) return true; } -// The SCSI table of contents consists of a 4-byte header followed by 100 track -// descriptors, which are each 8 bytes. We don't really need the first 5 bytes -// of the track descriptor, so we'll just ignore them. All we really want is the -// length of each track, which happen to be the last 3 bytes of the descriptor. -struct TrackDescriptor -{ - int32 unused; - int8 unused2; - - int8 min; - int8 sec; - int8 frame; -}; - bool CDAudioDevice::GetTimeForTrack(const int16 &index, cdaudio_time &track) { @@ -511,3 +577,24 @@ CDAudioDevice::GetDiscID(void) return id; } + +bool CDAudioDevice::IsDataTrack(const int16 &track) +{ + scsi_toc toc; + status_t result = ioctl(fFileHandle, B_SCSI_GET_TOC, &toc); + + if (result != B_OK) + return false; + + TrackDescriptor *trackindex = (TrackDescriptor*) &(toc.toc_data[4]); + if(track>toc.toc_data[3]) + return false; + + // At least under R5, the SCSI CD drive has each legitimate audio track + // have a value of 0x10. Data tracks have a value of 0x14; + if(trackindex[track].adr_control & 4) + return true; + + return false; +} + diff --git a/src/apps/cdplayer/CDAudioDevice.h b/src/apps/cdplayer/CDAudioDevice.h index 40eb0d875b..30f27ef9e4 100644 --- a/src/apps/cdplayer/CDAudioDevice.h +++ b/src/apps/cdplayer/CDAudioDevice.h @@ -5,6 +5,23 @@ #include #include +// The SCSI table of contents consists of a 4-byte header followed by 100 track +// descriptors, which are each 8 bytes. We don't really need the first 5 bytes +// of the track descriptor, so we'll just ignore them. All we really want is the +// length of each track, which happen to be the last 3 bytes of the descriptor. +typedef struct +{ + uint8 reserved; + uint8 adr_control; // bytes 0-3 are control, 4-7 are ADR + uint8 track_number; + uint8 reserved2; + + uint8 reserved3; + uint8 min; + uint8 sec; + uint8 frame; +}TrackDescriptor; + enum CDState { kNoCD=0, kStopped, @@ -15,20 +32,33 @@ enum CDState { kInit }; -typedef struct +class cdaudio_time { +public: + cdaudio_time(const int32 min=-1,const int32 &sec=-1); + cdaudio_time(const cdaudio_time &from); + cdaudio_time &operator=(const cdaudio_time &from); + cdaudio_time operator+(const cdaudio_time &from); + cdaudio_time operator-(const cdaudio_time &from); + int32 minutes; int32 seconds; -} cdaudio_time; +}; -typedef struct +class cdaudio_data { +public: + cdaudio_data(const int32 &id=-1, const int32 &count=-1, + const int32 &disclength=-1); + cdaudio_data(const cdaudio_data &from); + cdaudio_data &operator=(const cdaudio_data &from); + int32 disc_id; int32 track_count; int32 length; BString frame_offsets; -} cdaudio_data; +}; class CDAudioDevice { @@ -59,18 +89,22 @@ public: uint8 CountDrives(void); bool SetDrive(const int32 &drive); const char * GetDrivePath(void) const; + int32 GetDrive(void) const { return fDriveIndex; } bool GetTime(cdaudio_time &track, cdaudio_time &disc); bool GetTimeForTrack(const int16 &index, cdaudio_time &track); bool GetTimeForDisc(cdaudio_time &disc); int32 GetDiscID(void); + bool IsDataTrack(const int16 &track); + private: int32 FindDrives(const char *path); int fFileHandle; BString * fDrivePath; BList fDriveList; + int32 fDriveIndex; }; #endif diff --git a/src/apps/cdplayer/CDDBSupport.cpp b/src/apps/cdplayer/CDDBSupport.cpp index 2db1ac84f0..3429d05d16 100644 --- a/src/apps/cdplayer/CDDBSupport.cpp +++ b/src/apps/cdplayer/CDDBSupport.cpp @@ -7,6 +7,7 @@ #include "CDDBSupport.h" #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include //#define DEBUG_CDDB @@ -50,6 +52,361 @@ typedef struct TrackRecord }; +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(fYear) +{ + STRACE(("CDDBData::Copy Constructor\n")); + + for(int32 i=0; iString(),*time); + } +} + +CDDBData::~CDDBData(void) +{ + 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=fYear; + + for(int32 i=0; iString(),*time); + } + return *this; +} + +void +CDDBData::MakeEmpty(void) +{ + STRACE(("CDDBData::MakeEmpty\n")); + + EmptyLists(); + fDiscID=-1; + fArtist=""; + fAlbum=""; + fGenre=""; + fDiscTime.minutes=-1; + fDiscTime.seconds=-1; + fYear=-1; +} + +void +CDDBData::EmptyLists(void) +{ + STRACE(("CDDBData::EmptyLists\n")); + + for(int32 i=0; i 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 data; + + if(file.ReadAttr("Audio:Year",B_INT32_TYPE,0,&data,info.size)>0) + fYear = data; + } + + // TODO: Attempt reading the file before attempting to read the attributes + 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); + cdaudio_time *time = new cdaudio_time; + time->minutes = 0; + time->seconds = 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; + } + + // We return this so that the caller knows to initialize tracktimes + return B_NO_INIT; + } + } + + return B_ERROR; +} + +status_t +CDDBData::Load(void) +{ + // This uses the default R5 path + + 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(void) +{ + // This uses the default R5 path + + 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.minutes, fDiscTime.seconds); + + 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; iminutes, time->seconds); + + 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(void) 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 cdaudio_time &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 cdaudio_time(time)); +} + +void +CDDBData::RemoveTrack(const int16 &index) +{ + // This breaks the general following of the BList style because + // removing a track would require returning 2 pointers + + 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(); +} + +cdaudio_time * +CDDBData::TrackTimeAt(const int16 &index) +{ + return (cdaudio_time *)fTimeList.ItemAt(index); +} + template void InitCheck(InitCheckable *item) @@ -80,8 +437,7 @@ CDDBQuery::CDDBQuery(const char *server, int32 port) : fServerName(server), fPort(port), fConnected(false), - fState(kInitial), - fDiscID(-1) + fState(kInitial) { } @@ -110,8 +466,7 @@ CDDBQuery::SetToCD(const char *path) if(device < 0) return; - scsi_toc toc; - status_t result = ioctl(device, B_SCSI_GET_TOC, &toc); + status_t result = ioctl(device, B_SCSI_GET_TOC, &fSCSIData); close(device); @@ -121,28 +476,19 @@ CDDBQuery::SetToCD(const char *path) // Calculate the disc's CDDB ID if (fState == kInitial) { - fDiscID = GetDiscID(&toc); - fTrackCount = GetTrackCount(&toc); - - cdaudio_time time = GetDiscTime(&toc); - fDiscLength = (time.minutes * 60) + time.seconds; - fFrameOffsetString = OffsetsToString(&toc); - GetTrackTimes(&toc, fTrackTimes); + fCDData.SetDiscID(GetDiscID(&fSCSIData)); + fCDData.SetDiscTime(GetDiscTime(&fSCSIData)); } else { - int32 discID = GetDiscID(&toc); + int32 discID = GetDiscID(&fSCSIData); - if (fDiscID == discID) + if (fCDData.DiscID() == discID) return; - fDiscID = discID; - fTrackCount = GetTrackCount(&toc); - - cdaudio_time time = GetDiscTime(&toc); - fDiscLength = (time.minutes * 60) + time.seconds; - fFrameOffsetString = OffsetsToString(&toc); - GetTrackTimes(&toc, fTrackTimes); + fCDData.SetDiscID(discID); + fCDData.SetDiscTime(GetDiscTime(&fSCSIData)); + fFrameOffsetString = OffsetsToString(&fSCSIData); } result = B_OK; @@ -220,8 +566,11 @@ CDDBQuery::GetSites(bool (*eachFunc)(const char *site, int port, const char *lat } bool -CDDBQuery::GetTitles(BString *resultingTitle, vector *tracks, bigtime_t timeout) +CDDBQuery::GetData(CDDBData *data, bigtime_t timeout) { + if(!data) + return false; + bigtime_t deadline = system_time() + timeout; while (fState == kReading) { @@ -229,18 +578,11 @@ CDDBQuery::GetTitles(BString *resultingTitle, vector *tracks, bigtime_t if (system_time() > deadline) break; } - if (fState != kDone) + if(fState != kDone) return false; - - if (resultingTitle) - { - *resultingTitle = fArtist; - resultingTitle->Append(" / "); - resultingTitle->Append(fTitle); - } - if (tracks) - *tracks = fTrackNames; + *data = fCDData; + return true; } @@ -339,16 +681,20 @@ CDDBQuery::ReadFromServer(BString &data) // Query for the existence of the disc in the database char idString[10]; - sprintf(idString, "%08lx", fDiscID); - + sprintf(idString, "%08lx", fCDData.DiscID()); BString query; - query << "cddb query " << idString << ' ' << fTrackCount << ' ' - << fFrameOffsetString << ' ' << fDiscLength << '\n'; + + int32 trackCount = GetTrackCount(&fSCSIData); + BString offsetString = OffsetsToString(&fSCSIData); + int32 discLength = (fCDData.DiscTime()->minutes * 60) + fCDData.DiscTime()->seconds; + + query << "cddb query " << idString << ' ' << trackCount << ' ' + << offsetString << ' ' << discLength << '\n'; STRACE((">%s", query.String())); ThrowIfNotSize( fSocket.Send(query.String(), query.Length()) ); - + BString tmp; ReadLine(tmp); @@ -385,7 +731,7 @@ CDDBQuery::ReadFromServer(BString &data) { // We get here for any time the CDDB server does not recognize the CD, amongst other things STRACE(("CDDB lookup error: %s\n",tmp.String())); - fCategory = "misc"; + fCDData.SetGenre("misc"); return B_NAME_NOT_FOUND; } } @@ -394,9 +740,13 @@ CDDBQuery::ReadFromServer(BString &data) GetToken(tmp.String() + 3, category); } - if (!category.Length()) + STRACE(("CDDBQuery::ReadFromServer: Genre: %s\n",category.String())); + if(!category.Length()) + { + STRACE(("Set genre to 'misc'\n")); category = "misc"; - + } + // Query for the disc's data - artist, album, tracks, etc. query = ""; query << "cddb read " << category << ' ' << queryDiscID << '\n' ; @@ -446,22 +796,100 @@ void CDDBQuery::ReadLine(BString &buffer) { buffer = ""; - char ch; + unsigned char ch; + for (;;) { if (fSocket.Receive(&ch, 1) <= 0) break; - if (ch >= ' ') - buffer += ch; - if (ch == '\n') + + + // This function is more work than it should have to be. FreeDB lookups can sometimes + // be in a non-ASCII encoding, such as Latin-1 or UTF8. The problem lies in Be's implementation + // of BString, which does not support UTF8 string assignments. The Be Book says we have to + // flatten the string and adjust the character counts manually. Man, this *really* sucks. + if(ch > 0x7f) + { + // Obviously non-ASCII character detected. Let's see if it's Latin-1 or UTF8. + unsigned char *string, *stringindex; + int32 length = buffer.Length(); + + // The first byte of a UTF8 string will be 110xxxxx + if( ((ch & 0xe0) == 0xc0)) + { + // This is UTF8. Get the next byte + 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(); + + // We've added the character, so go to the next iteration + continue; + } + } + + // Nope. Just Latin-1. Convert to UTF8 and assign + char srcstr[2], deststr[5]; + int32 srclen, destlen, state; + + srcstr[0]=ch; + srcstr[1]='\0'; + srclen = 1; + destlen = 5; + + if(convert_to_utf8(B_ISO1_CONVERSION,srcstr,&srclen,deststr,&destlen,&state)==B_OK) + { + // We succeeded. Amazing. Now we hack the string into having the character + length = buffer.Length(); + + string = (unsigned char *)buffer.LockBuffer(length+10); + stringindex = string + length; + + for (int i = 0; i < 5; i++) + stringindex[i] = deststr[i]; + + buffer.UnlockBuffer(); + } + else + { + // well, we tried. Append the character to the string and live with it + buffer+=ch; + } + } + else + buffer +=ch; + + if(ch == '\n') break; } - STRACE(("<%s\n", buffer.String())); + + buffer.RemoveAll("\r"); + STRACE(("<%s", buffer.String())); } void CDDBQuery::IdentifySelf() { + // TODO: Figure out a better way to do this for Zeta +#ifdef B_ZETA_VERSION + BString tmp; + tmp << "cddb hello simplyvorbis svhost SimplyVorbis v0.1\n"; + + STRACE((">%s", tmp.String())); + ThrowIfNotSize( fSocket.Send(tmp.String(), tmp.Length()) ); + + ReadLine(tmp); +#else char username[256]; if (!getusername(username,256)) strcpy(username, "unknown"); @@ -471,15 +899,17 @@ CDDBQuery::IdentifySelf() strcpy(hostname, "unknown"); BString tmp; - tmp << "cddb hello " << username << " " << hostname << " Haiku_CD_Player v1.0\n"; + tmp << "cddb hello " << username << " " << hostname << " SimplyVorbis v0.1\n"; STRACE((">%s", tmp.String())); ThrowIfNotSize( fSocket.Send(tmp.String(), tmp.Length()) ); ReadLine(tmp); +#endif + } -bool +status_t CDDBQuery::OpenContentFile(const int32 &discID) { // Makes sure that the lookup has a valid file to work with for the CD content. @@ -509,64 +939,38 @@ CDDBQuery::OpenContentFile(const int32 &discID) continue; if(query.GetNextRef(&ref) == B_OK) - { - file.SetTo(&ref, B_READ_ONLY); break; - } } - if(file.InitCheck() == B_NO_INIT) - return false; - - // Getting this far means that we have a file. Now we parse the data in it and assign it - // to the appropriate members of the object - attr_info info; - - if(file.GetAttrInfo("CD:tracks",&info)==B_OK && info.size > 0) + status_t status = fCDData.Load(ref); + if(status == B_NO_INIT) { - char trackdata[info.size+2]; + // We receive this error when the Load() function couldn't load the track times + // This just means that we get it from the SCSI data given to us in SetToCD + vector times; + GetTrackTimes(&fSCSIData,times); - if(file.ReadAttr("CD:tracks",B_STRING_TYPE,0,trackdata,info.size)>0) + for(int32 i=0; iOpenContentFile(query->fDiscID)) + + if(query->OpenContentFile(query->fCDData.DiscID())!=B_OK) { // new content file, read it in from the server Connector connection(query); @@ -600,58 +1004,36 @@ CDDBQuery::WriteFile(void) create_directory(path.Path(), 0755); BString filename(path.Path()); - filename << "/" << fArtist << " - " << fTitle; + filename << "/" << fCDData.Artist() << " - " << fCDData.Album(); if(filename.Compare("Artist")==0) - filename << "." << fDiscID; + filename << "." << fCDData.DiscID(); - BFile file(filename.String(), B_READ_WRITE | B_CREATE_FILE | B_FAIL_IF_EXISTS); - if(file.InitCheck() != B_OK) - return; - - BString entry; - char timestring[10]; - - sprintf(timestring,"%.2ld:%.2ld",fDiscLength / 60, fDiscLength % 60); - - entry << fArtist << " - " << fTitle << "\t" << timestring << "\n"; - file.Write(entry.String(),entry.Length()); - - BString tracksattr(fArtist); - tracksattr << " - " << fTitle << "\n"; - - for(int32 i=0; i=0; i--) + fCDData.RemoveTrack(i); + + vector trackTimes; + GetTrackTimes(&fSCSIData,trackTimes); + int32 trackCount = GetTrackCount(&fSCSIData); if(data.CountChars()<1) { // This case occurs when the CDDB lookup fails. On these occasions, we need to generate // the file ourselves. This is actually pretty easy. - fArtist = "Artist"; - fTitle = "Audio CD"; - fCategory = "misc"; + fCDData.SetArtist("Artist"); + fCDData.SetAlbum("Audio CD"); + fCDData.SetGenre("misc"); - for(int32 i=0; i 0) + { + BString artist,album; + artist = album = GetLineFromString(data.String() + sizeof("DYEAR") + pos); + + // TODO: finish, once I find an entry which actually has a year in it + BAlert *alert = new BAlert("SimplyVorbis","DYEAR entry found\n","OK"); + alert->Go(); + + } + pos = data.FindFirst("DTITLE="); if(pos > 0) { - fTitle = fArtist = GetLineFromString(data.String() + sizeof("DTITLE") + pos); + BString artist,album; + artist = album = GetLineFromString(data.String() + sizeof("DTITLE") + pos); - pos = fArtist.FindFirst(" / "); + pos = artist.FindFirst(" / "); if(pos > 0) - fArtist.Truncate(pos); + artist.Truncate(pos); + fCDData.SetArtist(artist.String()); - fTitle = fTitle.String() + pos + sizeof(" / ") - 1; + album = album.String() + pos + sizeof(" / ") - 1; + fCDData.SetAlbum(album.String()); } - - fCategory = GetLineFromString(data.String() + 4); - pos = fCategory.FindFirst(" "); + BString category = GetLineFromString(data.String() + 4); + pos = category.FindFirst(" "); if(pos > 0) - fCategory.Truncate(pos); + category.Truncate(pos); + fCDData.SetGenre(category.String()); - - pos = data.FindFirst("Disc length: "); - if(pos > 0) - { - BString discLengthString = GetLineFromString(data.String() + pos); - pos = discLengthString.FindFirst(" seconds"); - if(pos > 0) - { - discLengthString.Truncate(pos); - - discLengthString = discLengthString.String() + sizeof("Disc length:"); - fDiscLength = atoi(discLengthString.String()); - } - else - fDiscLength = -1; - } - pos = data.FindFirst("TTITLE0="); if(pos > 0) { @@ -716,19 +1098,25 @@ CDDBQuery::ParseData(const BString &data) { BString trackName = data.String() + pos + searchString.Length(); trackName.Truncate(trackName.FindFirst("\n")); - fTrackNames.push_back(trackName); + + cdaudio_time tracktime=trackTimes[trackCount+1] - trackTimes[trackCount]; + fCDData.AddTrack(trackName.String(),tracktime); trackCount++; searchString = "TTITLE"; searchString << trackCount << "="; pos = data.FindFirst(searchString.String(),pos); } - fTrackCount = trackCount; } } +void CDDBQuery::SetData(const CDDBData &data) +{ + fCDData=data; +} + BString -CDDBQuery::GetLineFromString(const char *string) +GetLineFromString(const char *string) { if(!string) return NULL; diff --git a/src/apps/cdplayer/CDDBSupport.h b/src/apps/cdplayer/CDDBSupport.h index d98d2ca1c1..0c166f396c 100644 --- a/src/apps/cdplayer/CDDBSupport.h +++ b/src/apps/cdplayer/CDDBSupport.h @@ -5,8 +5,69 @@ #include #include #include +#include +#include #include "CDAudioDevice.h" +class CDDBData +{ +public: + CDDBData(int32 discid=-1); + CDDBData(const CDDBData &from); + ~CDDBData(void); + CDDBData & operator=(const CDDBData &from); + + status_t Load(const entry_ref &ref); + status_t Save(const char *filename); + + status_t Load(void); + status_t Save(void); + + void MakeEmpty(void); + void SetDiscID(const int32 &id) { fDiscID=id; } + int32 DiscID(void) const { return fDiscID; } + + uint16 CountTracks(void) const; + const char * TrackAt(const int16 &index) const; + bool RenameTrack(const int32 &index, const char *newname); + void AddTrack(const char *track, const cdaudio_time &time, + const int16 &index=-1); + void RemoveTrack(const int16 &index); + + cdaudio_time * TrackTimeAt(const int16 &index); + + void SetDiscTime(const cdaudio_time time) { fDiscTime = time; } + cdaudio_time * DiscTime(void) { return &fDiscTime; } + + void SetGenre(const char *genre) { fGenre=genre; } + const char * Genre(void) { return fGenre.String(); } + + void SetArtist(const char *artist) { fArtist=artist; } + const char * Artist(void) const { return fArtist.String(); } + + void SetAlbum(const char *album) { fAlbum=album; } + const char * Album(void) const { return fAlbum.String(); } + + void SetYear(const int16 &year) { fYear=year; } + int16 Year(void) const { return fYear; } + + +private: + void EmptyLists(void); + + int32 fDiscID; + + BString fArtist; + BString fAlbum; + BString fGenre; + + BList fTrackList; + BList fTimeList; + + cdaudio_time fDiscTime; + int32 fYear; +}; + // based on Jukebox by Chip Paul class CDDBQuery @@ -21,15 +82,15 @@ public: void SetToCD(const char *devicepath); - const char * GetArtist(void) { return fArtist.String(); } - const char * GetAlbum(void) { return fTitle.String(); } - const char * GetGenre(void) { return fCategory.String(); } + const char * GetArtist(void) { return fCDData.Artist(); } + const char * GetAlbum(void) { return fCDData.Album(); } + const char * GetGenre(void) { return fCDData.Genre(); } + + bool GetData(CDDBData *data, bigtime_t timeout); + void SetData(const CDDBData &data); - bool GetTitles(BString *title, vector *tracks, - bigtime_t timeout); - bool Ready() const { return fState == kDone; } - int32 CurrentDiscID() const { return fDiscID; } + int32 CurrentDiscID() const { return fCDData.DiscID(); } static int32 GetDiscID(const scsi_toc *); static int32 GetTrackCount(const scsi_toc *); @@ -77,13 +138,12 @@ private: kError }; - bool OpenContentFile(const int32 &discID); + status_t OpenContentFile(const int32 &discID); status_t ReadFromServer(BString &data); void ParseData(const BString &data); void WriteFile(void); - BString GetLineFromString(const char *string); void ReadLine(BString &); void IdentifySelf(); @@ -100,18 +160,11 @@ private: status_t fResult; // disc information - BString fArtist; - BString fTitle; - BString fCategory; - - int32 fDiscID; - int32 fDiscLength; - - vector fTrackNames; - int32 fTrackCount; - + CDDBData fCDData; BString fFrameOffsetString; - vector fTrackTimes; + scsi_toc fSCSIData; }; +BString GetLineFromString(const char *string); + #endif diff --git a/src/apps/cdplayer/Jamfile b/src/apps/cdplayer/Jamfile index 5b8d5d7f9b..c0d7597b6d 100644 --- a/src/apps/cdplayer/Jamfile +++ b/src/apps/cdplayer/Jamfile @@ -10,6 +10,6 @@ Application CDPlayer : TrackMenu.cpp TwoStateDrawButton.cpp - : be net netapi translation + : be net netapi translation textencoding : CDPlayer.rdef ;