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
This commit is contained in:
@@ -12,6 +12,85 @@
|
||||
#include <errno.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,23 @@
|
||||
#include <String.h>
|
||||
#include <List.h>
|
||||
|
||||
// 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
|
||||
|
||||
+541
-153
@@ -7,6 +7,7 @@
|
||||
#include "CDDBSupport.h"
|
||||
|
||||
#include <Debug.h>
|
||||
#include <Alert.h>
|
||||
#include <Directory.h>
|
||||
#include <Entry.h>
|
||||
#include <File.h>
|
||||
@@ -25,6 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdlib.h>
|
||||
#include <UTF8.h>
|
||||
|
||||
//#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; i<from.fTrackList.CountItems(); i++)
|
||||
{
|
||||
BString *string = (BString*)from.fTrackList.ItemAt(i);
|
||||
cdaudio_time *time = (cdaudio_time*)from.fTimeList.ItemAt(i);
|
||||
|
||||
if(!string || !time)
|
||||
continue;
|
||||
|
||||
AddTrack(string->String(),*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; i<from.fTrackList.CountItems(); i++)
|
||||
{
|
||||
BString *string = (BString*)from.fTrackList.ItemAt(i);
|
||||
cdaudio_time *time = (cdaudio_time*)from.fTimeList.ItemAt(i);
|
||||
|
||||
if(!string || !time)
|
||||
continue;
|
||||
|
||||
AddTrack(string->String(),*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<fTrackList.CountItems(); i++)
|
||||
{
|
||||
BString *string = (BString*)fTrackList.ItemAt(i);
|
||||
delete string;
|
||||
}
|
||||
fTrackList.MakeEmpty();
|
||||
|
||||
for(int32 j=0; j<fTimeList.CountItems(); j++)
|
||||
{
|
||||
cdaudio_time *time = (cdaudio_time*)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 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; i<fTrackList.CountItems(); i++)
|
||||
{
|
||||
BString *trackstr = (BString *)fTrackList.ItemAt(i);
|
||||
cdaudio_time *time= (cdaudio_time*)fTimeList.ItemAt(i);
|
||||
|
||||
if(!trackstr || !time)
|
||||
continue;
|
||||
|
||||
entry = *trackstr;
|
||||
|
||||
sprintf(timestring,"%.2ld:%.2ld",time->minutes, 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 <class InitCheckable>
|
||||
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<BString> *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<BString> *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<cdaudio_time> times;
|
||||
GetTrackTimes(&fSCSIData,times);
|
||||
|
||||
if(file.ReadAttr("CD:tracks",B_STRING_TYPE,0,trackdata,info.size)>0)
|
||||
for(int32 i=0; i<fCDData.CountTracks(); i++)
|
||||
{
|
||||
trackdata[info.size] = 0;
|
||||
BString tmp = GetLineFromString(trackdata);
|
||||
char *index;
|
||||
|
||||
fTrackNames.clear();
|
||||
fArtist = tmp;
|
||||
fArtist.Truncate(fArtist.FindFirst(" - "));
|
||||
|
||||
fTitle = strchr(tmp.String(),'-') + 2;
|
||||
|
||||
index = strchr(trackdata,'\n') + 1;
|
||||
while(*index)
|
||||
{
|
||||
tmp = GetLineFromString(index);
|
||||
|
||||
fTrackNames.push_back(tmp);
|
||||
index = strchr(index,'\n') + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
cdaudio_time *item = fCDData.TrackTimeAt(i);
|
||||
*item = times[i+1] - times[i];
|
||||
}
|
||||
|
||||
status = B_OK;
|
||||
}
|
||||
|
||||
// If we got this far, it means the file is corrupted, so nuke it and get a new one
|
||||
BEntry entry(&ref);
|
||||
entry.Remove();
|
||||
return false;
|
||||
return status;
|
||||
}
|
||||
|
||||
int32
|
||||
CDDBQuery::QueryThread(void *owner)
|
||||
{
|
||||
CDDBQuery *query = (CDDBQuery *)owner;
|
||||
|
||||
try
|
||||
{
|
||||
signal(kTerminatingSignal, DoNothing);
|
||||
|
||||
if(!query->OpenContentFile(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<fTrackCount; i++)
|
||||
{
|
||||
entry = fTrackNames[i];
|
||||
|
||||
sprintf(timestring,"%.2ld:%.2ld",fTrackTimes[i+1].minutes-fTrackTimes[i].minutes,
|
||||
fTrackTimes[i].seconds);
|
||||
|
||||
entry << "\t" << timestring << "\n";
|
||||
file.Write(entry.String(),entry.Length());
|
||||
|
||||
tracksattr << fTrackNames[i] << "\n";
|
||||
}
|
||||
|
||||
file.WriteAttr("CD:key", B_INT32_TYPE, 0, &fDiscID, sizeof(int32));
|
||||
file.WriteAttr("CD:tracks", B_STRING_TYPE, 0, tracksattr.String(), tracksattr.Length());
|
||||
fCDData.Save(filename.String());
|
||||
}
|
||||
|
||||
void
|
||||
CDDBQuery::ParseData(const BString &data)
|
||||
{
|
||||
fTitle = "";
|
||||
fTrackNames.clear();
|
||||
// Can't simply call MakeEmpty() because the thread is spawned when the discID kept in fCDData
|
||||
// is not the same as what's in the drive and MakeEmpty invalidates *everything* in the object.
|
||||
// Considering that we reassign everything here, simply emptying the track list should be sufficient
|
||||
for(int16 i = fCDData.CountTracks(); i>=0; i--)
|
||||
fCDData.RemoveTrack(i);
|
||||
|
||||
vector<cdaudio_time> 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<fTrackCount; i++)
|
||||
for(int32 i=0; i<trackCount; i++)
|
||||
{
|
||||
BString trackname("Track ");
|
||||
|
||||
@@ -659,7 +1041,9 @@ CDDBQuery::ParseData(const BString &data)
|
||||
trackname << "0";
|
||||
|
||||
trackname << i+1;
|
||||
fTrackNames.push_back(trackname.String());
|
||||
|
||||
cdaudio_time time = trackTimes[i+1] - trackTimes[i];
|
||||
fCDData.AddTrack(trackname.String(),time);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -671,41 +1055,39 @@ CDDBQuery::ParseData(const BString &data)
|
||||
|
||||
int32 pos;
|
||||
|
||||
pos = data.FindFirst("DYEAR=");
|
||||
if(pos > 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;
|
||||
|
||||
@@ -5,8 +5,69 @@
|
||||
#include <String.h>
|
||||
#include <scsi.h>
|
||||
#include <vector>
|
||||
#include <Entry.h>
|
||||
#include <List.h>
|
||||
#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<BString> *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<BString> fTrackNames;
|
||||
int32 fTrackCount;
|
||||
|
||||
CDDBData fCDData;
|
||||
BString fFrameOffsetString;
|
||||
vector<cdaudio_time> fTrackTimes;
|
||||
scsi_toc fSCSIData;
|
||||
};
|
||||
|
||||
BString GetLineFromString(const char *string);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,6 @@ Application CDPlayer :
|
||||
TrackMenu.cpp
|
||||
TwoStateDrawButton.cpp
|
||||
|
||||
: be net netapi translation
|
||||
: be net netapi translation textencoding
|
||||
: CDPlayer.rdef
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user