* Moved cdda_text test application to the test directory, and separated it from cdda.cpp.

* Implemented most of the directory retrieval functions of the file system; even renaming
  titles should work now.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21139 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-05-15 00:51:35 +00:00
parent d0b38183fd
commit 40897d3964
7 changed files with 364 additions and 172 deletions

View File

@ -6,8 +6,5 @@ UsePrivateHeaders [ FDirName storage ] ;
KernelAddon cdda :
kernel_interface.cpp
;
SimpleTest cdda_text :
cdda.cpp
: be ;
;

View File

@ -4,17 +4,15 @@
*/
#include <bus/scsi/scsi_cmds.h>
#include "cdda.h"
#include <KernelExport.h>
#include <device/scsi.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define kFramesPerSecond 75
#define kFramesPerMinute (kFramesPerSecond * 60)
struct cdtext_pack_data {
uint8 id;
@ -27,35 +25,37 @@ struct cdtext_pack_data {
uint8 crc[2];
} _PACKED;
static const uint8 kMaxTracks = 0x63;
enum {
kTrackID = 0x80,
kArtistID = 0x81,
kMessageID = 0x85,
};
struct cdtext {
char *artist;
char *album;
char *titles[kMaxTracks];
char *artists[kMaxTracks];
uint8 track_count;
char *genre;
};
static const uint32 kBufferSize = 16384;
static const uint32 kSenseSize = 1024;
// #pragma mark - string functions
bool
static char *
copy_string(const char *string)
{
if (string == NULL || !string[0])
return NULL;
return strdup(string);
}
static bool
is_garbage(char c)
{
return isspace(c) || c == '-' || c == '/' || c == '\\';
}
void
static void
sanitize_string(char *string)
{
if (string == NULL)
@ -106,7 +106,7 @@ find_string(const char *string, const char *find)
}
void
static void
cut_string(char *string, char *cut)
{
if (string == NULL || cut == NULL)
@ -121,7 +121,7 @@ cut_string(char *string, char *cut)
}
void
static void
sanitize_album(cdtext &text)
{
cut_string(text.album, text.artist);
@ -133,7 +133,7 @@ sanitize_album(cdtext &text)
if (space != NULL) {
space[0] = '\0';
text.artist = text.album;
text.album = strdup(space + 2);
text.album = copy_string(space + 2);
sanitize_string(text.artist);
sanitize_string(text.album);
@ -142,26 +142,28 @@ sanitize_album(cdtext &text)
}
void
static void
sanitize_titles(cdtext &text)
{
for (uint8 i = 0; i < text.track_count; i++) {
cut_string(text.titles[i], "(Album Version)");
sanitize_string(text.titles[i]);
sanitize_string(text.artists[i]);
if (!strcasecmp(text.artists[i], text.artist)) {
if (text.artists[i] != NULL && text.artist != NULL
&& !strcasecmp(text.artists[i], text.artist)) {
// if the title artist is the same as the main artist, remove it
free(text.artists[i]);
text.artists[i] = NULL;
}
if (text.titles[i] != NULL && text.titles[i][0] == '\t' && i > 0)
text.titles[i] = strdup(text.titles[i - 1]);
text.titles[i] = copy_string(text.titles[i - 1]);
}
}
bool
static bool
single_case(const char *string, bool &upper, bool &first)
{
if (string == NULL)
@ -185,7 +187,7 @@ single_case(const char *string, bool &upper, bool &first)
}
void
static void
capitalize_string(char *string)
{
if (string == NULL)
@ -207,7 +209,7 @@ capitalize_string(char *string)
}
void
static void
correct_case(cdtext &text)
{
// check if all titles share a single case
@ -238,14 +240,39 @@ correct_case(cdtext &text)
// #pragma mark - CD-Text
bool
cdtext::cdtext()
:
artist(NULL),
album(NULL),
genre(NULL),
track_count(0)
{
memset(titles, 0, sizeof(titles));
memset(artists, 0, sizeof(artists));
}
cdtext::~cdtext()
{
free(album);
free(artist);
free(genre);
for (uint8 i = 0; i < track_count; i++) {
free(titles[i]);
free(artists[i]);
}
}
static bool
is_string_id(uint8 id)
{
return id >= kTrackID && id <= kMessageID;
}
bool
static bool
parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft,
cdtext_pack_data *&lastPack, uint8 &id, uint8 &track, uint8 &state,
char *buffer, size_t &length)
@ -280,13 +307,13 @@ parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft,
while (id == pack->id && track == pack->track) {
#if 1
printf("%u.%u.%u, %u.%u.%u, ", pack->id, pack->track, pack->number,
dprintf("%u.%u.%u, %u.%u.%u, ", pack->id, pack->track, pack->number,
pack->double_byte, pack->block_number, pack->character_position);
for (int32 i = 0; i < 12; i++) {
if (isprint(pack->text[i]))
putchar(pack->text[i]);
dprintf("%c", pack->text[i]);
}
putchar('\n');
dprintf("\n");
#endif
if (is_string_id(id)) {
// TODO: support double byte characters
@ -330,72 +357,22 @@ parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft,
}
void
dump_cdtext(cdtext_pack_data *pack, size_t packLength)
static void
dump_cdtext(cdtext &text)
{
cdtext_pack_data *lastPack = NULL;
char buffer[256];
uint8 state = 0;
cdtext text;
memset(&text, 0, sizeof(cdtext));
while (true) {
size_t length = sizeof(buffer);
uint8 id, track;
if (!parse_pack_data(pack, packLength, lastPack, id, track,
state, buffer, length))
break;
switch (id) {
case kTrackID:
if (track == 0) {
if (text.album == NULL)
text.album = strdup(buffer);
} else if (track <= kMaxTracks) {
if (text.titles[track - 1] == NULL)
text.titles[track - 1] = strdup(buffer);
if (track > text.track_count)
text.track_count = track;
}
break;
case kArtistID:
if (track == 0) {
if (text.artist == NULL)
text.artist = strdup(buffer);
} else if (track <= kMaxTracks) {
if (text.artists[track - 1] == NULL)
text.artists[track - 1] = strdup(buffer);
}
break;
default:
if (is_string_id(id))
printf("UNKNOWN %u: \"%s\"\n", id, buffer);
break;
}
}
sanitize_string(text.artist);
sanitize_album(text);
sanitize_titles(text);
correct_case(text);
if (text.album)
printf("Album: \"%s\"\n", text.album);
dprintf("Album: \"%s\"\n", text.album);
if (text.artist)
printf("Artist: \"%s\"\n", text.artist);
dprintf("Artist: \"%s\"\n", text.artist);
for (uint8 i = 0; i < text.track_count; i++) {
printf("Track %02u: \"%s\"%s%s%s\n", i + 1, text.titles[i],
dprintf("Track %02u: \"%s\"%s%s%s\n", i + 1, text.titles[i],
text.artists[i] ? " (" : "", text.artists[i] ? text.artists[i] : "",
text.artists[i] ? ")" : "");
}
}
void
static void
dump_toc(scsi_toc_toc *toc)
{
int32 numTracks = toc->last_track + 1 - toc->first_track;
@ -415,22 +392,24 @@ dump_toc(scsi_toc_toc *toc)
length.second = (diff % kFramesPerMinute) / kFramesPerSecond;
length.frame = diff % kFramesPerSecond;
printf("%02u. %02u:%02u.%02u (length %02u:%02u.%02u)\n",
dprintf("%02u. %02u:%02u.%02u (length %02u:%02u.%02u)\n",
track.track_number, start.minute, start.second, start.frame,
length.minute, length.second, length.frame);
}
}
status_t
static status_t
read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
size_t bufferSize)
{
raw_device_command raw;
uint8 senseData[1024];
uint8 *senseData = (uint8 *)malloc(kSenseSize);
if (senseData == NULL)
return B_NO_MEMORY;
memset(&raw, 0, sizeof(raw_device_command));
memset(senseData, 0, sizeof(senseData));
memset(senseData, 0, kSenseSize);
memset(buffer, 0, bufferSize);
scsi_cmd_read_toc &toc = *(scsi_cmd_read_toc*)&raw.command;
@ -449,84 +428,106 @@ read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
raw.data_length = bufferSize;
raw.timeout = 10000000LL; // 10 secs
raw.sense_data = senseData;
raw.sense_data_length = sizeof(senseData);
raw.sense_data_length = sizeof(kSenseSize);
if (ioctl(fd, B_RAW_DEVICE_COMMAND, &raw) == 0) {
if (raw.scsi_status == 0 && raw.cam_status == 1) {
puts("success!\n");
} else {
puts("failure!\n");
}
if (ioctl(fd, B_RAW_DEVICE_COMMAND, &raw) == 0
&& raw.scsi_status == 0 && raw.cam_status == 1) {
free(senseData);
return B_OK;
}
return 0;
free(senseData);
return B_ERROR;
}
void
dump_block(const uint8 *buffer, int size, const char *prefix)
// #pragma mark - exported functions
status_t
read_cdtext(int fd, struct cdtext &cdtext)
{
int i;
for (i = 0; i < size;)
{
int start = i;
uint8 *buffer = (uint8 *)malloc(kBufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
printf(prefix);
for (; i < start+16; i++)
{
if (!(i % 4))
printf(" ");
if (i >= size)
printf(" ");
else
printf("%02x", *(unsigned char *)(buffer + i));
}
printf(" ");
for (i = start; i < start + 16; i++)
{
if (i < size)
{
char c = buffer[i];
if (c < 30)
printf(".");
else
printf("%c", c);
}
else
break;
}
printf("\n");
// do it twice, just in case...
// (at least my CD-ROM sometimes returned broken data on first try)
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer,
kBufferSize);
if (read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer,
kBufferSize) < B_OK) {
free(buffer);
return B_ERROR;
}
}
int
main(int argc, char** argv)
{
int fd = open(argv[1], O_RDONLY);
if (fd < 0)
return -1;
uint8 buffer[1024];
scsi_toc_general *header = (scsi_toc_general *)buffer;
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_TOC, buffer, sizeof(buffer));
header->data_length = B_BENDIAN_TO_HOST_INT16(header->data_length);
printf("TOC header %u, %d, %d\n", header->data_length, header->first, header->last);
//dump_block(buffer, header->data_length + 2, "TOC");
dump_toc((scsi_toc_toc *)buffer);
size_t packLength = B_BENDIAN_TO_HOST_INT16(header->data_length) - 2;
cdtext_pack_data *pack = (cdtext_pack_data *)(header + 1);
cdtext_pack_data *lastPack = NULL;
uint8 state = 0;
char text[256];
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, sizeof(buffer));
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, sizeof(buffer));
header->data_length = B_BENDIAN_TO_HOST_INT16(header->data_length);
printf("TEXT header %u, %d, %d\n", header->data_length, header->first, header->last);
//dump_block(buffer, header->data_length + 2, "TEXT");
while (true) {
size_t length = sizeof(text);
uint8 id, track;
dump_cdtext((cdtext_pack_data *)(buffer + 4), header->data_length - 2);
if (!parse_pack_data(pack, packLength, lastPack, id, track,
state, text, length))
break;
close(fd);
switch (id) {
case kTrackID:
if (track == 0) {
if (cdtext.album == NULL)
cdtext.album = copy_string(text);
} else if (track <= kMaxTracks) {
if (cdtext.titles[track - 1] == NULL)
cdtext.titles[track - 1] = copy_string(text);
if (track > cdtext.track_count)
cdtext.track_count = track;
}
break;
case kArtistID:
if (track == 0) {
if (cdtext.artist == NULL)
cdtext.artist = copy_string(text);
} else if (track <= kMaxTracks) {
if (cdtext.artists[track - 1] == NULL)
cdtext.artists[track - 1] = copy_string(text);
}
break;
default:
if (is_string_id(id))
dprintf("UNKNOWN %u: \"%s\"\n", id, text);
break;
}
}
free(buffer);
sanitize_string(cdtext.artist);
sanitize_album(cdtext);
sanitize_titles(cdtext);
correct_case(cdtext);
dump_cdtext(cdtext);
return B_OK;
}
status_t
read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length)
{
status_t status = read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_TOC,
(uint8*)toc, length);
if (status < B_OK)
return status;
dump_toc(toc);
return B_OK;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#ifndef CDDA_H
#define CDDA_H
#include <bus/scsi/scsi_cmds.h>
static const uint32 kFramesPerSecond = 75;
static const uint32 kFramesPerMinute = kFramesPerSecond * 60;
static const uint32 kFrameSize = 2352;
static const uint8 kMaxTracks = 0x63;
struct cdtext {
cdtext();
~cdtext();
char *artist;
char *album;
char *genre;
char *titles[kMaxTracks];
char *artists[kMaxTracks];
uint8 track_count;
};
status_t read_cdtext(int fd, cdtext &text);
status_t read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length);
#endif // CDDA_H

View File

@ -4,6 +4,7 @@
*/
#include "cdda.h"
#include "Lock.h"
#include <fs_info.h>
@ -57,7 +58,8 @@ class Volume {
off_t NumBlocks() const { return fNumBlocks; }
private:
Inode *_CreateNode(Inode *parent, const char *name, int32 type);
Inode *_CreateNode(Inode *parent, const char *name,
off_t start, off_t frames, int32 type);
Semaphore fLock;
int fDevice;
@ -74,7 +76,8 @@ class Volume {
class Inode {
public:
Inode(Volume *volume, Inode *parent, const char *name, int32 type);
Inode(Volume *volume, Inode *parent, const char *name, off_t start,
off_t frames, int32 type);
~Inode();
status_t InitCheck();
@ -93,8 +96,12 @@ class Inode {
{ return fCreationTime; }
time_t ModificationTime() const
{ return fModificationTime; }
off_t StartFrame() const
{ return fStartFrame; }
off_t FrameCount() const
{ return fFrameCount; }
off_t Size() const
{ return fSize; }
{ return fFrameCount * kFrameSize /* + WAV header */; }
Inode *Next() const { return fNext; }
void SetNext(Inode *inode) { fNext = inode; }
@ -103,12 +110,13 @@ class Inode {
Inode *fNext;
vnode_id fID;
int32 fType;
const char *fName;
char *fName;
gid_t fGroupID;
uid_t fUserID;
time_t fCreationTime;
time_t fModificationTime;
off_t fSize;
off_t fStartFrame;
off_t fFrameCount;
};
@ -142,7 +150,7 @@ Volume::Volume(mount_id id)
fFirstEntry(NULL)
{
// create the root vnode
fRootNode = _CreateNode(NULL, "", S_IFDIR | 0777);
fRootNode = _CreateNode(NULL, "", 0, 0, S_IFDIR | 0777);
}
@ -160,6 +168,8 @@ Volume::~Volume()
next = inode->Next();
delete inode;
}
free(fName);
}
@ -181,6 +191,64 @@ Volume::Mount(const char* device)
if (fDevice < 0)
return errno;
scsi_toc_toc *toc = (scsi_toc_toc *)malloc(1024);
if (toc == NULL)
return B_NO_MEMORY;
status_t status = read_table_of_contents(fDevice, toc, 1024);
if (status < B_OK) {
free(toc);
return status;
}
cdtext text;
if (read_cdtext(fDevice, text) < B_OK)
dprintf("CDDA: no CD-Text found.\n");
int32 trackCount = toc->last_track + 1 - toc->first_track;
char title[256];
for (int32 i = 0; i < trackCount; i++) {
scsi_cd_msf& next = toc->tracks[i + 1].start.time;
// the last track is always lead-out
scsi_cd_msf& start = toc->tracks[i].start.time;
off_t startFrame = start.minute * kFramesPerMinute
+ start.second * kFramesPerSecond + start.frame;
off_t frames = next.minute * kFramesPerMinute
+ next.second * kFramesPerSecond + next.frame
- startFrame;
if (text.titles[i] != NULL && text.titles[i]) {
if (text.artists[i] != NULL && text.artists[i]) {
snprintf(title, sizeof(title), "%02ld. %s - %s.wav", i + 1,
text.artists[i], text.titles[i]);
} else {
snprintf(title, sizeof(title), "%02ld. %s.wav", i + 1,
text.titles[i]);
}
} else
snprintf(title, sizeof(title), "%02ld.wav", i + 1);
_CreateNode(fRootNode, title, startFrame, frames, S_IFREG | 0444);
}
free(toc);
// determine volume title
if (text.artist != NULL && text.album != NULL)
snprintf(title, sizeof(title), "%s - %s", text.artist, text.album);
else if (text.artist != NULL || text.album != NULL) {
snprintf(title, sizeof(title), "%s", text.artist != NULL
? text.artist : text.album);
} else
strcpy(title, "Audio CD");
fName = strdup(title);
if (fName == NULL)
return B_NO_MEMORY;
return B_OK;
}
@ -193,9 +261,10 @@ Volume::Lock()
Inode *
Volume::_CreateNode(Inode *parent, const char *name, int32 type)
Volume::_CreateNode(Inode *parent, const char *name, off_t start, off_t frames,
int32 type)
{
Inode *inode = new Inode(this, parent, name, type);
Inode *inode = new Inode(this, parent, name, start, frames, type);
if (inode == NULL)
return NULL;
@ -245,14 +314,24 @@ Volume::Find(const char *name)
status_t
Volume::SetName(const char *name)
{
return B_ERROR;
if (name == NULL || !name[0])
return B_BAD_VALUE;
name = strdup(name);
if (name == NULL)
return B_NO_MEMORY;
free(fName);
fName = (char *)name;
return B_OK;
}
// #pragma mark -
Inode::Inode(Volume *volume, Inode *parent, const char *name, int32 type)
Inode::Inode(Volume *volume, Inode *parent, const char *name, off_t start,
off_t frames, int32 type)
:
fNext(NULL)
{
@ -262,6 +341,8 @@ Inode::Inode(Volume *volume, Inode *parent, const char *name, int32 type)
fID = volume->GetNextNodeID();
fType = type;
fStartFrame = start;
fFrameCount = frames;
fUserID = geteuid();
fGroupID = parent ? parent->GroupID() : getegid();
@ -289,7 +370,16 @@ Inode::InitCheck()
status_t
Inode::SetName(const char* name)
{
return B_ERROR;
if (name == NULL || !name[0])
return B_BAD_VALUE;
name = strdup(name);
if (name == NULL)
return B_NO_MEMORY;
free(fName);
fName = (char *)name;
return B_OK;
}
@ -578,7 +668,19 @@ cdda_rename(fs_volume _volume, void *_oldDir, const char *oldName, void *_newDir
|| strchr(newName, '/') != NULL)
return B_BAD_VALUE;
return B_ERROR;
// we only have a single directory which simplifies things a bit :-)
Volume *volume = (Volume *)_volume;
Locker _(volume->Lock());
Inode *inode = volume->Find(oldName);
if (inode == NULL)
return B_ENTRY_NOT_FOUND;
if (volume->Find(newName) != NULL)
return B_NAME_IN_USE;
return inode->SetName(newName);
}

View File

@ -1,6 +1,7 @@
SubDir HAIKU_TOP src tests add-ons kernel file_systems ;
SubInclude HAIKU_TOP src tests add-ons kernel file_systems bfs ;
SubInclude HAIKU_TOP src tests add-ons kernel file_systems cdda ;
SubInclude HAIKU_TOP src tests add-ons kernel file_systems fs_shell ;
#SubInclude HAIKU_TOP src tests add-ons kernel file_systems iso9660 ;
SubInclude HAIKU_TOP src tests add-ons kernel file_systems udf ;

View File

@ -0,0 +1,9 @@
SubDir HAIKU_TOP src tests add-ons kernel file_systems cdda ;
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems cdda ] ;
SimpleTest cdda_text
:
cdda_text.cpp
cdda.cpp
;

View File

@ -0,0 +1,48 @@
/*
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "cdda.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
extern const char* __progname;
extern "C" void
dprintf(const char* format, ...)
{
va_list args;
va_start(args, format);
vprintf(format, args);
fflush(stdout);
va_end(args);
}
int
main(int argc, char** argv)
{
if (argc < 2)
return -1;
int fd = open(argv[1], O_RDONLY);
if (fd < 0)
return -1;
uint8 buffer[1024];
if (read_table_of_contents(fd, (scsi_toc_toc*)buffer, sizeof(buffer)) < 0) {
fprintf(stderr, "%s: Retrieving TOC failed", __progname);
return -1;
}
cdtext text;
read_cdtext(fd, text);
close(fd);
}