1 /*
2 * Copyright 2007-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "cdda.h"
8
9 #include <KernelExport.h>
10 #include <device/scsi.h>
11
12 #include <algorithm>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <strings.h>
18
19
20 struct cdtext_pack_data {
21 uint8 id;
22 uint8 track;
23 uint8 number;
24 uint8 character_position : 4;
25 uint8 block_number : 3;
26 uint8 double_byte : 1;
27 char text[12];
28 uint8 crc[2];
29 } _PACKED;
30
31 enum {
32 kTrackID = 0x80,
33 kArtistID = 0x81,
34 kMessageID = 0x85,
35 };
36
37 static const uint32 kBufferSize = 16384;
38 static const uint32 kSenseSize = 1024;
39
40
41 // #pragma mark - string functions
42
43
44 static char *
copy_string(const char * string)45 copy_string(const char *string)
46 {
47 if (string == NULL || !string[0])
48 return NULL;
49
50 return strdup(string);
51 }
52
53
54 static char *
to_utf8(const char * string)55 to_utf8(const char* string)
56 {
57 char buffer[256];
58 size_t out = 0;
59
60 // TODO: assume CP1252 or ISO-8859-1 character set for now
61 while (uint32 c = (uint8)string[0]) {
62
63 if (c < 0x80) {
64 if (out >= sizeof(buffer) - 1)
65 break;
66 // ASCII character: no change needed
67 buffer[out++] = c;
68 } else {
69 if (c < 0xA0) {
70 // Windows CP-1252 - Use a lookup table
71 static const uint32 lookup[] = {
72 0x20AC, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
73 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0x017D, 0,
74 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
75 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0x017E, 0x0178
76 };
77
78 c = lookup[c - 0x80];
79 }
80
81 // Convert to 2 or 3-byte representation
82 if (c == 0) {
83 // invalid character, ignore
84 } else if (c < 0x800) {
85 if (out >= sizeof(buffer) - 2)
86 break;
87 buffer[out++] = 0xc0 | (c >> 6);
88 buffer[out++] = 0x80 | (c & 0x3f);
89 } else {
90 if (out >= sizeof(buffer) - 3)
91 break;
92 buffer[out++] = 0xe0 | (c >> 12);
93 buffer[out++] = 0x80 | ((c >> 6) & 0x3f);
94 buffer[out++] = 0x80 | (c & 0x3f);
95 }
96 }
97
98 string++;
99 }
100 buffer[out++] = '\0';
101
102 char *copy = (char *)malloc(out);
103 if (copy == NULL)
104 return NULL;
105
106 memcpy(copy, buffer, out);
107 return copy;
108 }
109
110
111 static bool
is_garbage(char c)112 is_garbage(char c)
113 {
114 return isspace(c) || c == '-' || c == '/' || c == '\\';
115 }
116
117
118 static void
sanitize_string(char * & string)119 sanitize_string(char *&string)
120 {
121 if (string == NULL)
122 return;
123
124 // strip garbage at the start
125
126 uint32 length = strlen(string);
127 uint32 garbage = 0;
128 while (is_garbage(string[garbage])) {
129 garbage++;
130 }
131
132 length -= garbage;
133 if (garbage)
134 memmove(string, string + garbage, length + 1);
135
136 // strip garbage from the end
137
138 while (length > 1 && isspace(string[length - 1])) {
139 string[--length] = '\0';
140 }
141
142 if (!string[0]) {
143 // free string if it's empty
144 free(string);
145 string = NULL;
146 }
147 }
148
149
150 //! Finds the first occurrence of \a find in \a string, ignores case.
151 static char*
find_string(const char * string,const char * find)152 find_string(const char *string, const char *find)
153 {
154 if (string == NULL || find == NULL)
155 return NULL;
156
157 char first = tolower(find[0]);
158 if (first == '\0')
159 return (char *)string;
160
161 int32 findLength = strlen(find) - 1;
162 find++;
163
164 for (; string[0]; string++) {
165 if (tolower(string[0]) != first)
166 continue;
167 if (strncasecmp(string + 1, find, findLength) == 0)
168 return (char *)string;
169 }
170
171 return NULL;
172 }
173
174
175 static void
cut_string(char * string,const char * cut)176 cut_string(char *string, const char *cut)
177 {
178 if (string == NULL || cut == NULL)
179 return;
180
181 char *found = find_string(string, cut);
182 if (found != NULL) {
183 uint32 foundLength = strlen(found);
184 uint32 cutLength = strlen(cut);
185 memmove(found, found + cutLength, foundLength + 1 - cutLength);
186 }
187 }
188
189
190 static void
sanitize_album(cdtext & text)191 sanitize_album(cdtext &text)
192 {
193 cut_string(text.album, text.artist);
194 sanitize_string(text.album);
195
196 if (text.album != NULL && !strcasecmp(text.album, "My CD")) {
197 // don't laugh, people really do that!
198 free(text.album);
199 text.album = NULL;
200 }
201
202 if ((text.artist == NULL || text.artist[0] == '\0') && text.album != NULL) {
203 // try to extract artist from album
204 char *space = strstr(text.album, " ");
205 if (space != NULL) {
206 space[0] = '\0';
207 text.artist = text.album;
208 text.album = copy_string(space + 2);
209
210 sanitize_string(text.artist);
211 sanitize_string(text.album);
212 }
213 }
214 }
215
216
217 static void
sanitize_titles(cdtext & text)218 sanitize_titles(cdtext &text)
219 {
220 for (uint8 i = 0; i < text.track_count; i++) {
221 cut_string(text.titles[i], "(Album Version)");
222 sanitize_string(text.titles[i]);
223 sanitize_string(text.artists[i]);
224
225 if (text.artists[i] != NULL && text.artist != NULL
226 && !strcasecmp(text.artists[i], text.artist)) {
227 // if the title artist is the same as the main artist, remove it
228 free(text.artists[i]);
229 text.artists[i] = NULL;
230 }
231
232 if (text.titles[i] != NULL && text.titles[i][0] == '\t' && i > 0)
233 text.titles[i] = copy_string(text.titles[i - 1]);
234 }
235 }
236
237
238 static bool
single_case(const char * string,bool & upper,bool & first)239 single_case(const char *string, bool &upper, bool &first)
240 {
241 if (string == NULL)
242 return true;
243
244 while (string[0]) {
245 while (!isalpha(string[0])) {
246 string++;
247 }
248
249 if (first) {
250 upper = isupper(string[0]) != 0;
251 first = false;
252 } else if ((isupper(string[0]) != 0) ^ upper)
253 return false;
254
255 string++;
256 }
257
258 return true;
259 }
260
261
262 static void
capitalize_string(char * string)263 capitalize_string(char *string)
264 {
265 if (string == NULL)
266 return;
267
268 bool newWord = isalpha(string[0]) || isspace(string[0]);
269 while (string[0]) {
270 if (isalpha(string[0])) {
271 if (newWord) {
272 string[0] = toupper(string[0]);
273 newWord = false;
274 } else
275 string[0] = tolower(string[0]);
276 } else if (string[0] != '\'')
277 newWord = true;
278
279 string++;
280 }
281 }
282
283
284 static void
correct_case(cdtext & text)285 correct_case(cdtext &text)
286 {
287 // check if all titles share a single case
288 bool first = true;
289 bool upper;
290 if (!single_case(text.album, upper, first)
291 || !single_case(text.artist, upper, first))
292 return;
293
294 for (int32 i = 0; i < text.track_count; i++) {
295 if (!single_case(text.titles[i], upper, first)
296 || !single_case(text.artists[i], upper, first))
297 return;
298 }
299
300 // If we get here, everything has a single case; we fix that
301 // and capitalize each word
302
303 capitalize_string(text.album);
304 capitalize_string(text.artist);
305 for (int32 i = 0; i < text.track_count; i++) {
306 capitalize_string(text.titles[i]);
307 capitalize_string(text.artists[i]);
308 }
309 }
310
311
312 // #pragma mark - CD-Text
313
314
cdtext()315 cdtext::cdtext()
316 :
317 artist(NULL),
318 album(NULL),
319 genre(NULL),
320 track_count(0)
321 {
322 memset(titles, 0, sizeof(titles));
323 memset(artists, 0, sizeof(artists));
324 }
325
326
~cdtext()327 cdtext::~cdtext()
328 {
329 free(album);
330 free(artist);
331 free(genre);
332
333 for (uint8 i = 0; i < track_count; i++) {
334 free(titles[i]);
335 free(artists[i]);
336 }
337 }
338
339
340 static bool
is_string_id(uint8 id)341 is_string_id(uint8 id)
342 {
343 return id >= kTrackID && id <= kMessageID;
344 }
345
346
347 /*! Parses a \a pack data into the provided text buffer; the corresponding
348 track number will be left in \a track, and the type of the data in \a id.
349 The pack data is explained in SCSI MMC-3.
350
351 \a id, \a track, and \a state must stay constant between calls to this
352 function. \a state must be initialized to zero for the first call.
353 */
354 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)355 parse_pack_data(cdtext_pack_data *&pack, uint32 &packLeft,
356 cdtext_pack_data *&lastPack, uint8 &id, uint8 &track, uint8 &state,
357 char *buffer, size_t &length)
358 {
359 if (packLeft < sizeof(cdtext_pack_data))
360 return false;
361
362 uint8 number = pack->number;
363 size_t size = length;
364
365 if (state != 0) {
366 // we had a terminated string and a missing track
367 track++;
368
369 memcpy(buffer, lastPack->text + state, 12 - state);
370 if (pack->track - track == 1)
371 state = 0;
372 else
373 state += strnlen(buffer, 12 - state);
374 return true;
375 }
376
377 id = pack->id;
378 track = pack->track;
379
380 buffer[0] = '\0';
381 length = 0;
382
383 size_t position = pack->character_position;
384 if (position > 0 && lastPack != NULL) {
385 memcpy(buffer, &lastPack->text[12 - position], position);
386 length = position;
387 }
388
389 while (id == pack->id && track == pack->track) {
390 #if 0
391 dprintf("%u.%u.%u, %u.%u.%u, ", pack->id, pack->track, pack->number,
392 pack->double_byte, pack->block_number, pack->character_position);
393 for (int32 i = 0; i < 12; i++) {
394 if (isprint(pack->text[i]))
395 dprintf("%c", pack->text[i]);
396 else
397 dprintf("-");
398 }
399 dprintf("\n");
400 #endif
401 if (is_string_id(id)) {
402 // TODO: support double byte characters
403 if (length + 12 < size) {
404 memcpy(buffer + length, pack->text, 12);
405 length += 12;
406 }
407 }
408
409 packLeft -= sizeof(cdtext_pack_data);
410 if (packLeft < sizeof(cdtext_pack_data))
411 return false;
412
413 lastPack = pack;
414 number++;
415 pack++;
416
417 if (pack->number != number)
418 return false;
419 }
420
421 if (id == pack->id) {
422 length -= pack->character_position;
423 if (length >= size)
424 length = size - 1;
425 buffer[length] = '\0';
426
427 if (pack->track > lastPack->track + 1) {
428 // there is a missing track
429 for (int32 i = 0; i < 12; i++) {
430 if (lastPack->text[i] == '\0') {
431 state = i + (lastPack->double_byte ? 2 : 1);
432 break;
433 }
434 }
435 }
436 }
437
438 return true;
439 }
440
441
442 static void
dump_cdtext(cdtext & text)443 dump_cdtext(cdtext &text)
444 {
445 if (text.album)
446 dprintf("Album: \"%s\"\n", text.album);
447 if (text.artist)
448 dprintf("Artist: \"%s\"\n", text.artist);
449 for (uint8 i = 0; i < text.track_count; i++) {
450 dprintf("Track %02u: \"%s\"%s%s%s\n", i + 1, text.titles[i],
451 text.artists[i] ? " (" : "", text.artists[i] ? text.artists[i] : "",
452 text.artists[i] ? ")" : "");
453 }
454 }
455
456
457 static void
dump_toc(scsi_toc_toc * toc)458 dump_toc(scsi_toc_toc *toc)
459 {
460 int32 numTracks = toc->last_track + 1 - toc->first_track;
461
462 for (int32 i = 0; i < numTracks; i++) {
463 scsi_toc_track& track = toc->tracks[i];
464 scsi_cd_msf& next = toc->tracks[i + 1].start.time;
465 // the last track is always lead-out
466 scsi_cd_msf& start = toc->tracks[i].start.time;
467 scsi_cd_msf length;
468
469 uint64 diff = next.minute * kFramesPerMinute
470 + next.second * kFramesPerSecond + next.frame
471 - start.minute * kFramesPerMinute
472 - start.second * kFramesPerSecond - start.frame;
473 length.minute = diff / kFramesPerMinute;
474 length.second = (diff % kFramesPerMinute) / kFramesPerSecond;
475 length.frame = diff % kFramesPerSecond;
476
477 dprintf("%02u. %02u:%02u.%02u (length %02u:%02u.%02u)\n",
478 track.track_number, start.minute, start.second, start.frame,
479 length.minute, length.second, length.frame);
480 }
481 }
482
483
484 static status_t
read_frames(int fd,off_t firstFrame,uint8 * buffer,size_t count)485 read_frames(int fd, off_t firstFrame, uint8 *buffer, size_t count)
486 {
487 size_t framesLeft = count;
488
489 while (framesLeft > 0) {
490 // If the initial count was >= 32, and not a multiple of 8, and the
491 // ioctl fails, we switch to reading 8 frames at a time. However the
492 // last read can read between 1 and 7 frames only, to not overflow
493 // the buffer.
494 count = std::min(count, framesLeft);
495
496 scsi_read_cd read;
497 read.start_m = firstFrame / kFramesPerMinute;
498 read.start_s = (firstFrame / kFramesPerSecond) % 60;
499 read.start_f = firstFrame % kFramesPerSecond;
500
501 read.length_m = count / kFramesPerMinute;
502 read.length_s = (count / kFramesPerSecond) % 60;
503 read.length_f = count % kFramesPerSecond;
504
505 read.buffer_length = count * kFrameSize;
506 read.buffer = (char *)buffer;
507 read.play = false;
508
509 if (ioctl(fd, B_SCSI_READ_CD, &read, sizeof(scsi_read_cd)) < 0) {
510 // drive couldn't read data - try again to read with a smaller block size
511 if (count == 1)
512 return errno;
513
514 if (count >= 32)
515 count = 8;
516 else
517 count = 1;
518
519 continue;
520 }
521
522 buffer += count * kFrameSize;
523 framesLeft -= count;
524 firstFrame += count;
525 }
526
527 return B_OK;
528 }
529
530
531 static status_t
read_table_of_contents(int fd,uint32 track,uint8 format,uint8 * buffer,size_t bufferSize)532 read_table_of_contents(int fd, uint32 track, uint8 format, uint8 *buffer,
533 size_t bufferSize)
534 {
535 raw_device_command raw;
536 uint8 *senseData = (uint8 *)malloc(kSenseSize);
537 if (senseData == NULL)
538 return B_NO_MEMORY;
539
540 memset(&raw, 0, sizeof(raw_device_command));
541 memset(senseData, 0, kSenseSize);
542 memset(buffer, 0, bufferSize);
543
544 scsi_cmd_read_toc &toc = *(scsi_cmd_read_toc*)&raw.command;
545 toc.opcode = SCSI_OP_READ_TOC;
546 toc.time = 1;
547 toc.format = format;
548 toc.track = track;
549 toc.allocation_length = B_HOST_TO_BENDIAN_INT16(bufferSize);
550
551 raw.command_length = 10;
552 raw.flags = B_RAW_DEVICE_DATA_IN | B_RAW_DEVICE_REPORT_RESIDUAL
553 | B_RAW_DEVICE_SHORT_READ_VALID;
554 raw.scsi_status = 0;
555 raw.cam_status = 0;
556 raw.data = buffer;
557 raw.data_length = bufferSize;
558 raw.timeout = 10000000LL; // 10 secs
559 raw.sense_data = senseData;
560 raw.sense_data_length = sizeof(kSenseSize);
561
562 if (ioctl(fd, B_RAW_DEVICE_COMMAND, &raw, sizeof(raw)) == 0
563 && raw.scsi_status == 0 && raw.cam_status == 1) {
564 free(senseData);
565 return B_OK;
566 }
567
568 free(senseData);
569 return B_ERROR;
570 }
571
572
573 // #pragma mark - exported functions
574
575
576 status_t
read_cdtext(int fd,struct cdtext & cdtext)577 read_cdtext(int fd, struct cdtext &cdtext)
578 {
579 uint8 *buffer = (uint8 *)malloc(kBufferSize);
580 if (buffer == NULL)
581 return B_NO_MEMORY;
582
583 // do it twice, just in case...
584 // (at least my CD-ROM sometimes returned broken data on first try)
585 read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer,
586 kBufferSize);
587 if (read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_CD_TEXT, buffer,
588 kBufferSize) != B_OK) {
589 free(buffer);
590 return B_ERROR;
591 }
592
593 scsi_toc_general *header = (scsi_toc_general *)buffer;
594
595 uint32 packLength = B_BENDIAN_TO_HOST_INT16(header->data_length) - 2;
596 cdtext_pack_data *pack = (cdtext_pack_data *)(header + 1);
597 cdtext_pack_data *lastPack = NULL;
598 uint8 state = 0;
599 uint8 track = 0;
600 uint8 id = 0;
601 char text[256];
602
603 // TODO: determine encoding!
604
605 while (true) {
606 size_t length = sizeof(text);
607
608 if (!parse_pack_data(pack, packLength, lastPack, id, track,
609 state, text, length))
610 break;
611
612 switch (id) {
613 case kTrackID:
614 if (track == 0) {
615 if (cdtext.album == NULL)
616 cdtext.album = to_utf8(text);
617 } else if (track <= kMaxTracks) {
618 if (cdtext.titles[track - 1] == NULL)
619 cdtext.titles[track - 1] = to_utf8(text);
620 if (track > cdtext.track_count)
621 cdtext.track_count = track;
622 }
623 break;
624
625 case kArtistID:
626 if (track == 0) {
627 if (cdtext.artist == NULL)
628 cdtext.artist = to_utf8(text);
629 } else if (track <= kMaxTracks) {
630 if (cdtext.artists[track - 1] == NULL)
631 cdtext.artists[track - 1] = to_utf8(text);
632 }
633 break;
634
635 default:
636 if (is_string_id(id))
637 dprintf("UNKNOWN %u: \"%s\"\n", id, text);
638 break;
639 }
640 }
641
642 free(buffer);
643
644 if (cdtext.artist == NULL && cdtext.album == NULL)
645 return B_ERROR;
646
647 for (int i = 0; i < cdtext.track_count; i++) {
648 if (cdtext.titles[i] == NULL)
649 return B_ERROR;
650 }
651
652 sanitize_string(cdtext.artist);
653 sanitize_album(cdtext);
654 sanitize_titles(cdtext);
655 correct_case(cdtext);
656
657 dump_cdtext(cdtext);
658 return B_OK;
659 }
660
661
662 status_t
read_table_of_contents(int fd,scsi_toc_toc * toc,size_t length)663 read_table_of_contents(int fd, scsi_toc_toc *toc, size_t length)
664 {
665 status_t status = read_table_of_contents(fd, 1, SCSI_TOC_FORMAT_TOC,
666 (uint8*)toc, length);
667 if (status < B_OK)
668 return status;
669
670 // make sure the values in the TOC make sense
671
672 int32 lastTrack = toc->last_track + 1 - toc->first_track;
673 size_t dataLength = B_BENDIAN_TO_HOST_INT16(toc->data_length) + 2;
674 if (dataLength < sizeof(scsi_toc_toc) || lastTrack <= 0)
675 return B_BAD_DATA;
676
677 if (length > dataLength)
678 length = dataLength;
679
680 length -= sizeof(scsi_toc_general);
681
682 if (lastTrack * sizeof(scsi_toc_track) > length)
683 toc->last_track = length / sizeof(scsi_toc_track) + toc->first_track;
684
685 dump_toc(toc);
686 return B_OK;
687 }
688
689
690 status_t
read_cdda_data(int fd,off_t endFrame,off_t offset,void * data,size_t length,off_t bufferOffset,void * buffer,size_t bufferSize)691 read_cdda_data(int fd, off_t endFrame, off_t offset, void *data, size_t length,
692 off_t bufferOffset, void *buffer, size_t bufferSize)
693 {
694 if (bufferOffset >= 0 && bufferOffset <= offset + (off_t)length
695 && bufferOffset + (off_t)bufferSize > offset) {
696 if (offset >= bufferOffset) {
697 // buffer reaches into the beginning of the request
698 off_t dataOffset = offset - bufferOffset;
699 size_t bytes = min_c(bufferSize - dataOffset, length);
700 if (user_memcpy(data, (uint8 *)buffer + dataOffset, bytes) < B_OK)
701 return B_BAD_ADDRESS;
702
703 data = (void *)((uint8 *)data + bytes);
704 length -= bytes;
705 offset += bytes;
706 } else if (offset < bufferOffset
707 && offset + length < bufferOffset + bufferSize) {
708 // buffer overlaps at the end of the request
709 off_t dataOffset = bufferOffset - offset;
710 size_t bytes = length - dataOffset;
711 if (user_memcpy((uint8 *)data + dataOffset, buffer, bytes) < B_OK)
712 return B_BAD_ADDRESS;
713
714 length -= bytes;
715 }
716 // we don't handle the case where we would need to split the request
717 }
718
719 while (length > 0) {
720 off_t frame = offset / kFrameSize;
721 uint32 count = bufferSize / kFrameSize;
722 if (frame + count > endFrame)
723 count = endFrame - frame;
724
725 status_t status = read_frames(fd, frame, (uint8 *)buffer, count);
726 if (status < B_OK)
727 return status;
728
729 off_t dataOffset = offset % kFrameSize;
730 size_t bytes = bufferSize - dataOffset;
731 if (bytes > length)
732 bytes = length;
733
734 if (user_memcpy(data, (uint8 *)buffer + dataOffset, bytes) < B_OK)
735 return B_BAD_ADDRESS;
736
737 data = (void *)((uint8 *)data + bytes);
738 length -= bytes;
739 offset += bytes;
740 }
741
742 return B_OK;
743 }
744