* 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:
@@ -6,8 +6,5 @@ UsePrivateHeaders [ FDirName storage ] ;
|
|||||||
|
|
||||||
KernelAddon cdda :
|
KernelAddon cdda :
|
||||||
kernel_interface.cpp
|
kernel_interface.cpp
|
||||||
;
|
|
||||||
|
|
||||||
SimpleTest cdda_text :
|
|
||||||
cdda.cpp
|
cdda.cpp
|
||||||
: be ;
|
;
|
||||||
|
|||||||
@@ -4,17 +4,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <bus/scsi/scsi_cmds.h>
|
#include "cdda.h"
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
#include <device/scsi.h>
|
#include <device/scsi.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define kFramesPerSecond 75
|
|
||||||
#define kFramesPerMinute (kFramesPerSecond * 60)
|
|
||||||
|
|
||||||
struct cdtext_pack_data {
|
struct cdtext_pack_data {
|
||||||
uint8 id;
|
uint8 id;
|
||||||
@@ -27,35 +25,37 @@ struct cdtext_pack_data {
|
|||||||
uint8 crc[2];
|
uint8 crc[2];
|
||||||
} _PACKED;
|
} _PACKED;
|
||||||
|
|
||||||
|
|
||||||
static const uint8 kMaxTracks = 0x63;
|
|
||||||
enum {
|
enum {
|
||||||
kTrackID = 0x80,
|
kTrackID = 0x80,
|
||||||
kArtistID = 0x81,
|
kArtistID = 0x81,
|
||||||
kMessageID = 0x85,
|
kMessageID = 0x85,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cdtext {
|
static const uint32 kBufferSize = 16384;
|
||||||
char *artist;
|
static const uint32 kSenseSize = 1024;
|
||||||
char *album;
|
|
||||||
char *titles[kMaxTracks];
|
|
||||||
char *artists[kMaxTracks];
|
|
||||||
uint8 track_count;
|
|
||||||
char *genre;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - string functions
|
// #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)
|
is_garbage(char c)
|
||||||
{
|
{
|
||||||
return isspace(c) || c == '-' || c == '/' || c == '\\';
|
return isspace(c) || c == '-' || c == '/' || c == '\\';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
sanitize_string(char *string)
|
sanitize_string(char *string)
|
||||||
{
|
{
|
||||||
if (string == NULL)
|
if (string == NULL)
|
||||||
@@ -106,7 +106,7 @@ find_string(const char *string, const char *find)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
cut_string(char *string, char *cut)
|
cut_string(char *string, char *cut)
|
||||||
{
|
{
|
||||||
if (string == NULL || cut == NULL)
|
if (string == NULL || cut == NULL)
|
||||||
@@ -121,7 +121,7 @@ cut_string(char *string, char *cut)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
sanitize_album(cdtext &text)
|
sanitize_album(cdtext &text)
|
||||||
{
|
{
|
||||||
cut_string(text.album, text.artist);
|
cut_string(text.album, text.artist);
|
||||||
@@ -133,7 +133,7 @@ sanitize_album(cdtext &text)
|
|||||||
if (space != NULL) {
|
if (space != NULL) {
|
||||||
space[0] = '\0';
|
space[0] = '\0';
|
||||||
text.artist = text.album;
|
text.artist = text.album;
|
||||||
text.album = strdup(space + 2);
|
text.album = copy_string(space + 2);
|
||||||
|
|
||||||
sanitize_string(text.artist);
|
sanitize_string(text.artist);
|
||||||
sanitize_string(text.album);
|
sanitize_string(text.album);
|
||||||
@@ -142,26 +142,28 @@ sanitize_album(cdtext &text)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
sanitize_titles(cdtext &text)
|
sanitize_titles(cdtext &text)
|
||||||
{
|
{
|
||||||
for (uint8 i = 0; i < text.track_count; i++) {
|
for (uint8 i = 0; i < text.track_count; i++) {
|
||||||
cut_string(text.titles[i], "(Album Version)");
|
cut_string(text.titles[i], "(Album Version)");
|
||||||
sanitize_string(text.titles[i]);
|
sanitize_string(text.titles[i]);
|
||||||
sanitize_string(text.artists[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
|
// if the title artist is the same as the main artist, remove it
|
||||||
free(text.artists[i]);
|
free(text.artists[i]);
|
||||||
text.artists[i] = NULL;
|
text.artists[i] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.titles[i] != NULL && text.titles[i][0] == '\t' && i > 0)
|
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)
|
single_case(const char *string, bool &upper, bool &first)
|
||||||
{
|
{
|
||||||
if (string == NULL)
|
if (string == NULL)
|
||||||
@@ -185,7 +187,7 @@ single_case(const char *string, bool &upper, bool &first)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
capitalize_string(char *string)
|
capitalize_string(char *string)
|
||||||
{
|
{
|
||||||
if (string == NULL)
|
if (string == NULL)
|
||||||
@@ -207,7 +209,7 @@ capitalize_string(char *string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
correct_case(cdtext &text)
|
correct_case(cdtext &text)
|
||||||
{
|
{
|
||||||
// check if all titles share a single case
|
// check if all titles share a single case
|
||||||
@@ -238,14 +240,39 @@ correct_case(cdtext &text)
|
|||||||
// #pragma mark - CD-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)
|
is_string_id(uint8 id)
|
||||||
{
|
{
|
||||||
return id >= kTrackID && id <= kMessageID;
|
return id >= kTrackID && id <= kMessageID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft,
|
parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft,
|
||||||
cdtext_pack_data *&lastPack, uint8 &id, uint8 &track, uint8 &state,
|
cdtext_pack_data *&lastPack, uint8 &id, uint8 &track, uint8 &state,
|
||||||
char *buffer, size_t &length)
|
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) {
|
while (id == pack->id && track == pack->track) {
|
||||||
#if 1
|
#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);
|
pack->double_byte, pack->block_number, pack->character_position);
|
||||||
for (int32 i = 0; i < 12; i++) {
|
for (int32 i = 0; i < 12; i++) {
|
||||||
if (isprint(pack->text[i]))
|
if (isprint(pack->text[i]))
|
||||||
putchar(pack->text[i]);
|
dprintf("%c", pack->text[i]);
|
||||||
}
|
}
|
||||||
putchar('\n');
|
dprintf("\n");
|
||||||
#endif
|
#endif
|
||||||
if (is_string_id(id)) {
|
if (is_string_id(id)) {
|
||||||
// TODO: support double byte characters
|
// TODO: support double byte characters
|
||||||
@@ -330,72 +357,22 @@ parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
dump_cdtext(cdtext_pack_data *pack, size_t packLength)
|
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)
|
if (text.album)
|
||||||
printf("Album: \"%s\"\n", text.album);
|
dprintf("Album: \"%s\"\n", text.album);
|
||||||
if (text.artist)
|
if (text.artist)
|
||||||
printf("Artist: \"%s\"\n", text.artist);
|
dprintf("Artist: \"%s\"\n", text.artist);
|
||||||
for (uint8 i = 0; i < text.track_count; i++) {
|
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] ? " (" : "", text.artists[i] ? text.artists[i] : "",
|
||||||
text.artists[i] ? ")" : "");
|
text.artists[i] ? ")" : "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
dump_toc(scsi_toc_toc *toc)
|
dump_toc(scsi_toc_toc *toc)
|
||||||
{
|
{
|
||||||
int32 numTracks = toc->last_track + 1 - toc->first_track;
|
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.second = (diff % kFramesPerMinute) / kFramesPerSecond;
|
||||||
length.frame = diff % 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,
|
track.track_number, start.minute, start.second, start.frame,
|
||||||
length.minute, length.second, length.frame);
|
length.minute, length.second, length.frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
static status_t
|
||||||
read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
|
read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
|
||||||
size_t bufferSize)
|
size_t bufferSize)
|
||||||
{
|
{
|
||||||
raw_device_command raw;
|
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(&raw, 0, sizeof(raw_device_command));
|
||||||
memset(senseData, 0, sizeof(senseData));
|
memset(senseData, 0, kSenseSize);
|
||||||
memset(buffer, 0, bufferSize);
|
memset(buffer, 0, bufferSize);
|
||||||
|
|
||||||
scsi_cmd_read_toc &toc = *(scsi_cmd_read_toc*)&raw.command;
|
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.data_length = bufferSize;
|
||||||
raw.timeout = 10000000LL; // 10 secs
|
raw.timeout = 10000000LL; // 10 secs
|
||||||
raw.sense_data = senseData;
|
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 (ioctl(fd, B_RAW_DEVICE_COMMAND, &raw) == 0
|
||||||
if (raw.scsi_status == 0 && raw.cam_status == 1) {
|
&& raw.scsi_status == 0 && raw.cam_status == 1) {
|
||||||
puts("success!\n");
|
free(senseData);
|
||||||
} else {
|
return B_OK;
|
||||||
puts("failure!\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
free(senseData);
|
||||||
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
// #pragma mark - exported functions
|
||||||
dump_block(const uint8 *buffer, int size, const char *prefix)
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
read_cdtext(int fd, struct cdtext &cdtext)
|
||||||
{
|
{
|
||||||
int i;
|
uint8 *buffer = (uint8 *)malloc(kBufferSize);
|
||||||
|
if (buffer == NULL)
|
||||||
for (i = 0; i < size;)
|
return B_NO_MEMORY;
|
||||||
{
|
|
||||||
int start = i;
|
|
||||||
|
|
||||||
printf(prefix);
|
// do it twice, just in case...
|
||||||
for (; i < start+16; i++)
|
// (at least my CD-ROM sometimes returned broken data on first try)
|
||||||
{
|
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer,
|
||||||
if (!(i % 4))
|
kBufferSize);
|
||||||
printf(" ");
|
if (read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer,
|
||||||
|
kBufferSize) < B_OK) {
|
||||||
if (i >= size)
|
free(buffer);
|
||||||
printf(" ");
|
return B_ERROR;
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
scsi_toc_general *header = (scsi_toc_general *)buffer;
|
||||||
|
|
||||||
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_TOC, buffer, sizeof(buffer));
|
size_t packLength = B_BENDIAN_TO_HOST_INT16(header->data_length) - 2;
|
||||||
header->data_length = B_BENDIAN_TO_HOST_INT16(header->data_length);
|
cdtext_pack_data *pack = (cdtext_pack_data *)(header + 1);
|
||||||
printf("TOC header %u, %d, %d\n", header->data_length, header->first, header->last);
|
cdtext_pack_data *lastPack = NULL;
|
||||||
//dump_block(buffer, header->data_length + 2, "TOC");
|
uint8 state = 0;
|
||||||
dump_toc((scsi_toc_toc *)buffer);
|
char text[256];
|
||||||
|
|
||||||
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, sizeof(buffer));
|
while (true) {
|
||||||
read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer, sizeof(buffer));
|
size_t length = sizeof(text);
|
||||||
header->data_length = B_BENDIAN_TO_HOST_INT16(header->data_length);
|
uint8 id, track;
|
||||||
printf("TEXT header %u, %d, %d\n", header->data_length, header->first, header->last);
|
|
||||||
//dump_block(buffer, header->data_length + 2, "TEXT");
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Axel Dörfler, [email protected].
|
||||||
|
* 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
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "cdda.h"
|
||||||
#include "Lock.h"
|
#include "Lock.h"
|
||||||
|
|
||||||
#include <fs_info.h>
|
#include <fs_info.h>
|
||||||
@@ -57,7 +58,8 @@ class Volume {
|
|||||||
off_t NumBlocks() const { return fNumBlocks; }
|
off_t NumBlocks() const { return fNumBlocks; }
|
||||||
|
|
||||||
private:
|
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;
|
Semaphore fLock;
|
||||||
int fDevice;
|
int fDevice;
|
||||||
@@ -74,7 +76,8 @@ class Volume {
|
|||||||
|
|
||||||
class Inode {
|
class Inode {
|
||||||
public:
|
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();
|
~Inode();
|
||||||
|
|
||||||
status_t InitCheck();
|
status_t InitCheck();
|
||||||
@@ -93,8 +96,12 @@ class Inode {
|
|||||||
{ return fCreationTime; }
|
{ return fCreationTime; }
|
||||||
time_t ModificationTime() const
|
time_t ModificationTime() const
|
||||||
{ return fModificationTime; }
|
{ return fModificationTime; }
|
||||||
|
off_t StartFrame() const
|
||||||
|
{ return fStartFrame; }
|
||||||
|
off_t FrameCount() const
|
||||||
|
{ return fFrameCount; }
|
||||||
off_t Size() const
|
off_t Size() const
|
||||||
{ return fSize; }
|
{ return fFrameCount * kFrameSize /* + WAV header */; }
|
||||||
|
|
||||||
Inode *Next() const { return fNext; }
|
Inode *Next() const { return fNext; }
|
||||||
void SetNext(Inode *inode) { fNext = inode; }
|
void SetNext(Inode *inode) { fNext = inode; }
|
||||||
@@ -103,12 +110,13 @@ class Inode {
|
|||||||
Inode *fNext;
|
Inode *fNext;
|
||||||
vnode_id fID;
|
vnode_id fID;
|
||||||
int32 fType;
|
int32 fType;
|
||||||
const char *fName;
|
char *fName;
|
||||||
gid_t fGroupID;
|
gid_t fGroupID;
|
||||||
uid_t fUserID;
|
uid_t fUserID;
|
||||||
time_t fCreationTime;
|
time_t fCreationTime;
|
||||||
time_t fModificationTime;
|
time_t fModificationTime;
|
||||||
off_t fSize;
|
off_t fStartFrame;
|
||||||
|
off_t fFrameCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -142,7 +150,7 @@ Volume::Volume(mount_id id)
|
|||||||
fFirstEntry(NULL)
|
fFirstEntry(NULL)
|
||||||
{
|
{
|
||||||
// create the root vnode
|
// 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();
|
next = inode->Next();
|
||||||
delete inode;
|
delete inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(fName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -181,6 +191,64 @@ Volume::Mount(const char* device)
|
|||||||
if (fDevice < 0)
|
if (fDevice < 0)
|
||||||
return errno;
|
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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,9 +261,10 @@ Volume::Lock()
|
|||||||
|
|
||||||
|
|
||||||
Inode *
|
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)
|
if (inode == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -245,14 +314,24 @@ Volume::Find(const char *name)
|
|||||||
status_t
|
status_t
|
||||||
Volume::SetName(const char *name)
|
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 -
|
// #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)
|
fNext(NULL)
|
||||||
{
|
{
|
||||||
@@ -262,6 +341,8 @@ Inode::Inode(Volume *volume, Inode *parent, const char *name, int32 type)
|
|||||||
|
|
||||||
fID = volume->GetNextNodeID();
|
fID = volume->GetNextNodeID();
|
||||||
fType = type;
|
fType = type;
|
||||||
|
fStartFrame = start;
|
||||||
|
fFrameCount = frames;
|
||||||
|
|
||||||
fUserID = geteuid();
|
fUserID = geteuid();
|
||||||
fGroupID = parent ? parent->GroupID() : getegid();
|
fGroupID = parent ? parent->GroupID() : getegid();
|
||||||
@@ -289,7 +370,16 @@ Inode::InitCheck()
|
|||||||
status_t
|
status_t
|
||||||
Inode::SetName(const char* name)
|
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)
|
|| strchr(newName, '/') != NULL)
|
||||||
return B_BAD_VALUE;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
SubDir HAIKU_TOP src tests add-ons kernel file_systems ;
|
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 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 fs_shell ;
|
||||||
#SubInclude HAIKU_TOP src tests add-ons kernel file_systems iso9660 ;
|
#SubInclude HAIKU_TOP src tests add-ons kernel file_systems iso9660 ;
|
||||||
SubInclude HAIKU_TOP src tests add-ons kernel file_systems udf ;
|
SubInclude HAIKU_TOP src tests add-ons kernel file_systems udf ;
|
||||||
|
|||||||
@@ -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
|
||||||
|
;
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Axel Dörfler, [email protected].
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user