1 /*  cdrdao - write audio CD-Rs in disc-at-once mode
2  *
3  *  Copyright (C) 1998-2001 Andreas Mueller <andreas@daneb.de>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include <config.h>
21 
22 #include <stdio.h>
23 #include <assert.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <unistd.h>
28 
29 #include <fstream>
30 #include <iostream>
31 #include <sstream>
32 
33 #include "Toc.h"
34 #include "util.h"
35 #include "log.h"
36 #include "TrackDataList.h"
37 #include "CdTextItem.h"
38 #include "CueParser.h"
39 #include "FormatConverter.h"
40 
41 #ifdef UNIXWARE
42 extern "C" {
43   extern int      strcasecmp(const char *, const char *);
44 }
45 #endif
46 
47 extern Toc *parseToc(FILE *fp, const char *filename);
48 
Toc()49 Toc::Toc() : length_(0)
50 {
51   tocType_ = CD_DA;
52   nofTracks_ = 0; firstTrackNo_ = 0;
53   tracks_ = lastTrack_ = NULL;
54 
55   catalogValid_ = 0;
56 }
57 
58 // copy constructor
Toc(const Toc & obj)59 Toc::Toc(const Toc &obj) : length_(0), cdtext_(obj.cdtext_)
60 {
61   tocType_ = obj.tocType_;
62 
63   if ((catalogValid_ = obj.catalogValid_))
64     memcpy(catalog_, obj.catalog_, 13);
65 
66   nofTracks_ = 0;
67   firstTrackNo_ = obj.firstTrackNo_;
68   tracks_ = lastTrack_ = NULL;
69 
70   // copy all tracks
71   TrackIterator itr(&obj);
72   const Track *trun;
73 
74   for (trun = itr.first(); trun != NULL; trun = itr.next())
75     append(trun);
76 }
77 
78 
~Toc()79 Toc::~Toc()
80 {
81   TrackEntry *run = tracks_;
82   TrackEntry *next;
83 
84   while (run != NULL) {
85     next = run->next;
86     delete run->track;
87     delete run;
88     run = next;
89   }
90 }
91 
92 
93 // appends track
94 // return: 0: OK
95 //         1: first track contains pregap
append(const Track * t)96 int Toc::append(const Track *t)
97 {
98   if (tracks_ == NULL) {
99     tracks_ = lastTrack_ = new TrackEntry;
100   }
101   else {
102     lastTrack_->next = new TrackEntry;
103     lastTrack_->next->pred = lastTrack_;
104     lastTrack_ = lastTrack_->next;
105   }
106 
107   lastTrack_->track = new Track(*t);
108   nofTracks_ += 1;
109 
110   update();
111   return 0;
112 }
113 
update()114 void Toc::update()
115 {
116   TrackEntry *run;
117   long length = 0; // length of disc in blocks
118   long tlength; // length of single track in blocks
119   int tnum;
120 
121   for (run = tracks_, tnum = 1; run != NULL; run = run->next, tnum++) {
122     tlength = run->track->length().lba();
123 
124     run->absStart = Msf(length);
125     run->start = Msf(length + run->track->start().lba());
126     run->end = Msf(length + tlength);
127     run->trackNr = tnum;
128 
129     length += tlength;
130   }
131 
132   length_ = Msf(length);
133 }
134 
135 
136 
read(const char * filename)137 Toc *Toc::read(const char *filename)
138 {
139   FILE *fp;
140   Toc *ret;
141   const char *p;
142 
143   if ((fp = fopen(filename, "r")) == NULL) {
144     log_message(-2, "Cannot open toc file '%s' for reading: %s",
145 	    filename, strerror(errno));
146     return NULL;
147   }
148 
149   if ((p = strrchr(filename, '.')) != NULL && strcasecmp(p, ".cue") == 0)
150     ret = parseCue(fp, filename);
151   else
152     ret = parseToc(fp, filename);
153 
154   fclose(fp);
155 
156   return ret;
157 }
158 
resolveFilenames(const char * filename)159 bool Toc::resolveFilenames(const char* filename)
160 {
161   // Resolve all relative filenames to absoluate paths wrt to the toc
162   // file current directory.
163   std::string path = filename;
164   path = path.substr(0, path.rfind('/'));
165   if (path.empty()) path = ".";
166 
167   for (TrackEntry* t = tracks_; t != NULL; t = t->next)
168     if (!t->track->resolveFilename(path.c_str()))
169       return false;
170 
171   return true;
172 }
173 
174 // Writes toc to file with given name.
175 // Return: 0: OK
176 //         1: error occured
177 
write(const char * filename) const178 int Toc::write(const char *filename) const
179 {
180   assert(filename != NULL);
181   assert(*filename != 0);
182 
183   std::ofstream out(filename);
184 
185   if (!out) {
186     log_message(-2, "Cannot open file \"%s\" for writing: %s", filename,
187 	    strerror(errno));
188     return 1;
189   }
190 
191   print(out);
192 
193   return 0;
194 }
195 
write(int fd,bool conversions) const196 bool Toc::write(int fd, bool conversions) const
197 {
198     assert(fd);
199 
200     std::ostringstream oss(std::ostringstream::out);
201     print(oss, conversions);
202 
203     std::string ossstr = oss.str();
204     const char* content = ossstr.c_str();
205     int written = ::write(fd, content, strlen(content));
206 
207     return (written >= 0);
208 }
209 
check() const210 int Toc::check() const
211 {
212   TrackEntry *t;
213   int trackNr;
214   int ret = 0;
215 
216   for (t = tracks_, trackNr = 1; t != NULL; t = t->next, trackNr++) {
217     ret |= t->track->check(trackNr);
218   }
219 
220   return ret;
221 }
222 
recomputeLength()223 bool Toc::recomputeLength()
224 {
225   for (TrackEntry* t = tracks_; t; t = t->next) {
226     if (!t->track->recomputeLength())
227       return false;
228   }
229 
230   update();
231   return true;
232 }
233 
234 // Sets catalog number. 's' must be a string of 13 digits.
235 // return: 0: OK
236 //         1: illegal catalog string
catalog(const char * s)237 int Toc::catalog(const char *s)
238 {
239   int i;
240 
241   if (s == NULL) {
242     catalogValid_ = 0;
243     return 0;
244   }
245 
246   if (strlen(s) != 13) {
247     return 1;
248   }
249 
250   for (i = 0; i < 13; i++) {
251     if (!isdigit(s[i]))
252       return 1;
253   }
254 
255   for (i = 0; i < 13; i++)
256     catalog_[i] = s[i] - '0';
257 
258   catalogValid_ = 1;
259 
260   return 0;
261 }
262 
263 
catalog() const264 const char *Toc::catalog() const
265 {
266   static char buf[14];
267   int i;
268 
269   if (!catalogValid_)
270     return NULL;
271 
272   for (i = 0; i < 13; i++)
273     buf[i] = catalog_[i] + '0';
274 
275   buf[13] = 0;
276 
277   return buf;
278 }
279 
280 // writes contents in TOC file syntax
print(std::ostream & out,bool conversions) const281 void Toc::print(std::ostream &out, bool conversions) const
282 {
283   int i;
284   TrackEntry *t;
285 
286   out << tocType2String(tocType()) << "\n\n";
287 
288   if (catalogValid()) {
289     out << "CATALOG \"";
290     for (i = 0; i < 13; i++) {
291       out << (char)(catalog(i) + '0');
292     }
293     out << "\"" << std::endl;
294   }
295 
296   cdtext_.print(0, out);
297 
298   for (t = tracks_, i = 1; t != NULL; t = t->next, i++) {
299     out << "\n// Track " << i << "\n";
300     t->track->print(out, conversions);
301     out << std::endl;
302   }
303 }
304 
convertFilesToWav()305 bool Toc::convertFilesToWav()
306 {
307   FormatSupport::Status status = formatConverter.convert(this);
308 
309   return (status == FormatSupport::FS_SUCCESS);
310 }
311 
collectFiles(std::set<std::string> & set)312 void Toc::collectFiles(std::set<std::string>& set)
313 {
314   for (TrackEntry* t = tracks_; t != NULL; t = t->next)
315     t->track->collectFiles(set);
316 }
317 
markFileConversion(const char * src,const char * dst)318 void Toc::markFileConversion(const char* src, const char* dst)
319 {
320   for (TrackEntry* t = tracks_; t != NULL; t = t->next)
321     t->track->markFileConversion(src, dst);
322 }
323 
324 // find track entry that contains given sample number
325 // return: found track entry or 'NULL' if sample is out of range
findTrack(unsigned long sample) const326 Toc::TrackEntry *Toc::findTrack(unsigned long sample) const
327 {
328   TrackEntry *run;
329 
330   for (run = tracks_; run != NULL; run = run->next) {
331     if (sample < run->end.samples())
332       return run;
333   }
334 
335   return NULL;
336 }
337 
338 // find track with given number
339 // return: found track entry or 'NULL' if 'trackNr' is out of range
findTrackByNumber(int trackNr) const340 Toc::TrackEntry *Toc::findTrackByNumber(int trackNr) const
341 {
342   TrackEntry *run;
343 
344   for (run = tracks_; run != NULL; run = run->next) {
345     if (run->trackNr == trackNr)
346       return run;
347   }
348 
349   return NULL;
350 }
351 
getTrack(int trackNr)352 Track *Toc::getTrack(int trackNr)
353 {
354   TrackEntry *ent = findTrackByNumber(trackNr);
355 
356   if (ent != NULL)
357     return ent->track;
358   else
359     return NULL;
360 }
361 
362 // Moves specified track/index position to given LBA if possible.
363 // return: 0: OK
364 //         1: Cannot move pre-gap of first track
365 //         2: specified track/index position not found in toc
366 //         3: 'lba' is an illegal position for track/index mark
367 //         4: resulting track length would be short than 4 seconds
368 //         5: cannot cross track/index boundaries
369 //         6: cannot modify data track
moveTrackMarker(int trackNr,int indexNr,long lba)370 int Toc::moveTrackMarker(int trackNr, int indexNr, long lba)
371 {
372   TrackEntry *act;
373 
374   if (trackNr == 1 && indexNr == 0)
375     return 1;
376 
377   if ((act = findTrackByNumber(trackNr)) == NULL) {
378     return 2;
379   }
380 
381   if (indexNr <= 1 &&
382       (act->track->type() != TrackData::AUDIO ||
383        act->track->subChannelType() != TrackData::SUBCHAN_NONE))
384     return 6;
385 
386   if ((indexNr == 0 || (indexNr == 1 && act->track->start().lba() == 0)) &&
387       act->pred != NULL &&
388       (act->pred->track->type() != TrackData::AUDIO ||
389        act->track->subChannelType() != TrackData::SUBCHAN_NONE))
390     return 6;
391 
392 
393   if (indexNr == 0 && act->track->start().lba() == 0)
394     return 2;
395 
396   if (indexNr > 1 && indexNr - 2 >= act->track->nofIndices())
397     return 2;
398 
399   if (lba < 0 || lba >= length().lba())
400     return 3;
401 
402   if ((indexNr == 1 && (act->track->start().lba() > 0 || trackNr == 1)) ||
403       indexNr > 1) {
404     // change track/index position within track
405     if (indexNr == 1) {
406       if (lba > act->end.lba() - 4 * 75)
407 	return 4;
408 
409       if (lba <= act->absStart.lba() && trackNr > 1)
410 	return 3;
411     }
412     else {
413       if (lba - act->absStart.lba() <= 0)
414 	return 3;
415     }
416 
417     switch (act->track->moveIndex(indexNr, lba - act->absStart.lba())) {
418     case 1:
419       return 4;
420     case 2:
421       return 5;
422     }
423   }
424   else {
425     // move track start position, we need to shift audio data from
426     // on track to the other
427 
428     // 'act->pred' is always non NULL in this case because track 1 is
429     // exhaustively handled above
430 
431     TrackDataList *dataList; // audio data that is removed from one track
432 
433     if (lba < act->absStart.lba()) {
434       // move to the left
435 
436       if (lba < act->pred->start.lba() + 4 * 75)
437 	return 4;
438 
439       dataList =
440 	act->pred->track->removeToEnd(Msf(lba - act->pred->absStart.lba()).samples());
441       act->track->prependTrackData(dataList);
442       delete dataList;
443 
444       // adjust start of track
445       act->track->start(Msf(act->start.lba() - lba));
446 
447       if (indexNr == 1)
448 	act->track->moveIndex(1, 0); // remove intermediate pre-gap
449     }
450     else if (lba > act->absStart.lba()) {
451       // move to the right
452 
453       if (indexNr == 1) {
454 	// introduce an intermediate pre-gap that adjusts the index
455 	// increments
456 	switch (act->track->moveIndex(1, lba - act->absStart.lba())) {
457 	case 1:
458 	  return 4;
459 	case 2:
460 	  return 5;
461 	}
462 
463 	// remove intermediate pre-gap
464 	act->track->start(Msf(0));
465       }
466       else {
467 	// adjust pre-gap
468 	if (lba >= act->start.lba())
469 	  return 5;
470 
471 	act->track->start(Msf(act->start.lba() - lba));
472       }
473 
474       dataList =
475 	act->track->removeFromStart(Msf(lba - act->absStart.lba()).samples());
476       act->pred->track->appendTrackData(dataList);
477       delete dataList;
478 
479     }
480   }
481 
482   update();
483   checkConsistency();
484 
485   return 0;
486 }
487 
remove(TrackEntry * ent)488 void Toc::remove(TrackEntry *ent)
489 {
490   if (ent->pred != NULL)
491     ent->pred->next = ent->next;
492   else
493     tracks_ = ent->next;
494 
495   if (ent->next != NULL)
496     ent->next->pred = ent->pred;
497   else
498     lastTrack_ = ent->pred;
499 
500   ent->pred = ent->next = NULL;
501   ent->track = NULL;
502   delete ent;
503 }
504 
505 // Removes specified track marker.
506 // return: 0: OK
507 //         1: cannot remove first track
508 //         2: specified track/index position not found in toc
509 //         3: cannot modify a data track
removeTrackMarker(int trackNr,int indexNr)510 int Toc::removeTrackMarker(int trackNr, int indexNr)
511 {
512   TrackEntry *act;
513 
514   if (trackNr == 1 && indexNr == 1)
515     return 1;
516 
517   if ((act = findTrackByNumber(trackNr)) == NULL) {
518     return 2;
519   }
520 
521   if ((act->track->type() != TrackData::AUDIO ||
522        act->track->subChannelType() != TrackData::SUBCHAN_NONE) &&
523       indexNr <= 1)
524     return 3;
525 
526   if (act->pred != NULL &&
527       (act->pred->track->type() != TrackData::AUDIO ||
528        act->track->subChannelType() != TrackData::SUBCHAN_NONE) &&
529       indexNr <= 1)
530     return 3;
531 
532   if (trackNr == 1 && indexNr == 0) {
533     // pre-gap of first track
534 
535     if (act->start.lba() == 0)
536       return 2; // no pre-gap
537 
538     act->track->start(Msf(0));
539   }
540   else if (indexNr > 1) {
541     // index increment
542     if (act->track->removeIndex(indexNr - 2) != 0)
543       return 2;
544   }
545   else if (indexNr == 0) {
546     // remove pre-gap, audio data of pre-gap is appended to previous track
547     unsigned long len = act->track->start().samples();
548 
549     if (len == 0)
550       return 2; // track has no pre-gap
551 
552     act->track->start(Msf(0));
553     TrackDataList *dataList =  act->track->removeFromStart(len);
554     act->pred->track->appendTrackData(dataList);
555     delete dataList;
556   }
557   else {
558     // index == 1, remove track completely
559 
560     act->pred->track->appendTrackData(act->track);
561 
562     Track *store = act->track;
563     remove(act);
564     delete store;
565 
566     nofTracks_--;
567   }
568 
569   update();
570   checkConsistency();
571 
572   return 0;
573 }
574 
575 // Adds index increment at given LBA.
576 // return: 0: OK
577 //         1: LBA out of range
578 //         2: cannot add index at this position
579 //         3: more than 98 index increments
addIndexMarker(long lba)580 int Toc::addIndexMarker(long lba)
581 {
582   TrackEntry *act = findTrack(Msf(lba).samples());
583 
584   if (act == NULL)
585     return 1;
586 
587   if (lba <= act->start.lba())
588     return 2;
589 
590   switch (act->track->addIndex(Msf(lba - act->start.lba()))) {
591   case 1:
592     return 3;
593   case 2:
594     return 2;
595   }
596 
597   return 0;
598 }
599 
600 // Adds a track marker at given LBA.
601 // return: 0: OK
602 //         1: LBA out of range
603 //         2: cannot add track at this position
604 //         3: resulting track would be shorter than 4 seconds
605 //         4: previous track would be short than 4 seconds
606 //         5: cannot modify a data track
addTrackMarker(long lba)607 int Toc::addTrackMarker(long lba)
608 {
609   TrackEntry *act = findTrack(Msf(lba).samples());
610 
611   if (act == NULL)
612     return 1;
613 
614   if (act->track->type() != TrackData::AUDIO)
615     return 5;
616 
617   if (act->track->subChannelType() != TrackData::SUBCHAN_NONE)
618     return 5;
619 
620   if (lba <= act->start.lba())
621     return 2;
622 
623   if (lba - act->start.lba() < 4 * 75)
624     return 4;
625 
626   if (act->end.lba() - lba < 4 * 75)
627     return 3;
628 
629 
630   TrackDataList *dataList =
631     act->track->removeToEnd(Msf(lba - act->absStart.lba()).samples());
632 
633   Track *t = new Track(act->track->type(), act->track->subChannelType());
634   t->appendTrackData(dataList);
635   delete dataList;
636 
637   TrackEntry *ent = new TrackEntry;
638   ent->track = t;
639 
640   ent->next = act->next;
641   if (ent->next != NULL)
642     ent->next->pred = ent;
643 
644   ent->pred = act;
645   act->next = ent;
646 
647   if (act == lastTrack_)
648     lastTrack_ = ent;
649 
650   nofTracks_++;
651 
652   update();
653   checkConsistency();
654 
655   return 0;
656 }
657 
658 
659 // Adds pre-gap add given LBA.
660 // return: 0: OK
661 //         1: LBA out of range
662 //         2: cannot add pre-gap at this point
663 //         3: actual track would be shorter than 4 seconds
664 //         4: cannot modify a data track
addPregap(long lba)665 int Toc::addPregap(long lba)
666 {
667   TrackEntry *act = findTrack(Msf(lba).samples());
668 
669   if (act == NULL)
670     return 1;
671 
672   if (act->track->type() != TrackData::AUDIO)
673     return 4;
674 
675   if (act->track->subChannelType() != TrackData::SUBCHAN_NONE)
676     return 4;
677 
678   if (act->next == NULL)
679     return 2; // no next track where we could add pre-gap
680 
681   if (act->next->track->type() != act->track->type() ||
682       act->next->track->subChannelType() != act->track->subChannelType())
683     return 4;
684 
685   if (lba <= act->start.lba())
686     return 2;
687 
688   if (act->next->track->start().lba() != 0)
689     return 2; // track has already a pre-gap
690 
691   if (lba - act->start.lba() < 4 * 75)
692     return 3;
693 
694   TrackDataList *dataList =
695     act->track->removeToEnd(Msf(lba - act->absStart.lba()).samples());
696   act->next->track->prependTrackData(dataList);
697   delete dataList;
698 
699   act->next->track->start(Msf(act->next->start.lba() - lba));
700 
701   update();
702   checkConsistency();
703 
704   return 0;
705 }
706 
fixLengths()707 void Toc::fixLengths()
708 {
709   TrackEntry* te;
710   int i;
711 
712   for (i = 0 , te = tracks_; te; te = te->next, i++) {
713     printf("%d : Track %d\n", i, te->trackNr);
714   }
715 }
716 
checkConsistency()717 void Toc::checkConsistency()
718 {
719   TrackEntry *run, *last = NULL;
720   long cnt = 0;
721 
722   for (run = tracks_; run != NULL; last = run, run = run->next) {
723     cnt++;
724     if (run->pred != last)
725       log_message(-3, "Toc::checkConsistency: wrong pred pointer.");
726 
727     run->track->checkConsistency();
728   }
729 
730   if (last != lastTrack_)
731     log_message(-3, "Toc::checkConsistency: wrong last pointer.");
732 
733   if (cnt != nofTracks_)
734     log_message(-3, "Toc::checkConsistency: wrong sub track counter.");
735 }
736 
737 
738 // Appends a track with given audio data. 'start' is filled with
739 // first LBA of new track, 'end' is filled with last LBA + 1 of new track.
appendTrack(const TrackDataList * list,long * start,long * end)740 void Toc::appendTrack(const TrackDataList *list, long *start, long *end)
741 {
742   Track t(TrackData::AUDIO, TrackData::SUBCHAN_NONE);
743   const TrackData *run;
744 
745   for (run = list->first(); run != NULL; run = list->next())
746     t.append(SubTrack(SubTrack::DATA, *run));
747 
748   // ensure that track lasts at least 4 seconds
749   unsigned long minTime = 4 * 75 * SAMPLES_PER_BLOCK; // 4 seconds
750   unsigned long len = list->length();
751 
752   if (len < minTime)
753     t.append(SubTrack(SubTrack::DATA, TrackData(minTime - len)));
754 
755   *start = length().lba();
756 
757   append(&t);
758   checkConsistency();
759 
760   *end = length().lba();
761 }
762 
763 // Appends given audio data to last track. If no track exists 'appendTrack'
764 // will be called. 'start' and 'end' is filled with position of modified
765 // region.
766 // Return: 0: OK
767 //         1: cannot modify a data track
appendTrackData(const TrackDataList * list,long * start,long * end)768 int Toc::appendTrackData(const TrackDataList *list, long *start, long *end)
769 {
770   const TrackData *run;
771 
772   if (lastTrack_ == NULL) {
773     appendTrack(list, start, end);
774     return 0;
775   }
776 
777   if (lastTrack_->track->type() != TrackData::AUDIO)
778     return 1;
779 
780   if (lastTrack_->track->subChannelType() != TrackData::SUBCHAN_NONE)
781     return 1;
782 
783 
784   *start = length().lba();
785 
786   for (run = list->first(); run != NULL; run = list->next())
787     lastTrack_->track->append(SubTrack(SubTrack::DATA, *run));
788 
789   update();
790   checkConsistency();
791 
792   *end = length().lba();
793 
794   return 0;
795 }
796 
797 // Removes specified range of samples from a single track.
798 // Return: 0: OK
799 //         1: samples range covers more than one track
800 //         2: cannot modify a data track
801 //         3: illegal 'start' position
removeTrackData(unsigned long start,unsigned long end,TrackDataList ** list)802 int Toc::removeTrackData(unsigned long start, unsigned long end,
803 			 TrackDataList **list)
804 {
805   TrackEntry *tent = findTrack(start);
806 
807   if (tent == NULL)
808     return 3;
809 
810   if (tent->track->type() != TrackData::AUDIO)
811     return 2;
812 
813   if (tent->track->subChannelType() != TrackData::SUBCHAN_NONE)
814     return 2;
815 
816   if (tent != findTrack(end))
817     return 1;
818 
819   *list = tent->track->removeTrackData(start - tent->absStart.samples(),
820 				       end - tent->absStart.samples());
821 
822   update();
823   checkConsistency();
824 
825   return 0;
826 }
827 
828 // Inserts given track data at specified postion.
829 // Return: 0: OK
830 //         1: cannot modify a data track
831 
insertTrackData(unsigned long pos,const TrackDataList * list)832 int Toc::insertTrackData(unsigned long pos, const TrackDataList *list)
833 {
834   TrackEntry *tent = findTrack(pos);
835 
836   if (tent != NULL && tent->track->type() != TrackData::AUDIO)
837     return 1;
838 
839   if (tent != NULL && tent->track->subChannelType() != TrackData::SUBCHAN_NONE)
840     return 1;
841 
842 
843   if (tent != NULL) {
844     tent->track->insertTrackData(pos - tent->absStart.samples(), list);
845 
846     update();
847     checkConsistency();
848 
849     return 0;
850   }
851   else {
852     long start, end;
853 
854     return appendTrackData(list, &start, &end);
855   }
856 
857 }
858 
859 
860 // Returns mode that should be used for lead-in. The mode of first track's
861 // first sub-track is used.
leadInMode() const862 TrackData::Mode Toc::leadInMode() const
863 {
864   const SubTrack *t;
865 
866   if (tracks_ == NULL || (t = tracks_->track->firstSubTrack()) == NULL) {
867     // no track or track data available - return AUDIO in this case
868     return TrackData::AUDIO;
869   }
870 
871   return t->mode();
872 }
873 
874 // Returns mode that should be used for lead-out. The mode of last track's
875 // last sub-track is used
leadOutMode() const876 TrackData::Mode Toc::leadOutMode() const
877 {
878   const SubTrack *t;
879 
880   if (lastTrack_ == NULL || (t = lastTrack_->track->lastSubTrack()) == NULL) {
881     // no track or track data available - return AUDIO in this case
882     return TrackData::AUDIO;
883   }
884 
885   return t->mode();
886 }
887 
tocType2String(TocType t)888 const char *Toc::tocType2String(TocType t)
889 {
890   const char *ret = NULL;
891   switch (t) {
892   case CD_DA:
893     ret = "CD_DA";
894     break;
895   case CD_ROM:
896     ret = "CD_ROM";
897     break;
898   case CD_I:
899     ret = "CD_I";
900     break;
901   case CD_ROM_XA:
902     ret = "CD_ROM_XA";
903     break;
904   }
905 
906   return ret;
907 }
908 
addCdTextItem(int trackNr,CdTextItem * item)909 void Toc::addCdTextItem(int trackNr, CdTextItem *item)
910 {
911   assert(trackNr >= 0 && trackNr <= 99);
912 
913   if (trackNr == 0) {
914     cdtext_.add(item);
915   }
916   else {
917     TrackEntry *track = findTrackByNumber(trackNr);
918 
919     if (track == NULL) {
920       log_message(-3, "addCdTextItem: Track %d is not available.", trackNr);
921       return;
922     }
923 
924     track->track->addCdTextItem(item);
925   }
926 }
927 
removeCdTextItem(int trackNr,CdTextItem::PackType type,int blockNr)928 void Toc::removeCdTextItem(int trackNr, CdTextItem::PackType type, int blockNr)
929 {
930   assert(trackNr >= 0 && trackNr <= 99);
931 
932   if (trackNr == 0) {
933     cdtext_.remove(type, blockNr);
934   }
935   else {
936     TrackEntry *track = findTrackByNumber(trackNr);
937 
938     if (track == NULL) {
939       log_message(-3, "addCdTextItem: Track %d is not available.", trackNr);
940       return;
941     }
942 
943     track->track->removeCdTextItem(type, blockNr);
944   }
945 }
946 
existCdTextBlock(int blockNr) const947 int Toc::existCdTextBlock(int blockNr) const
948 {
949   if (cdtext_.existBlock(blockNr))
950     return 1;
951 
952   TrackEntry *run;
953 
954   for (run = tracks_; run != NULL; run = run->next) {
955     if (run->track->existCdTextBlock(blockNr))
956       return 1;
957   }
958 
959   return 0;
960 }
961 
getCdTextItem(int trackNr,int blockNr,CdTextItem::PackType type) const962 const CdTextItem *Toc::getCdTextItem(int trackNr, int blockNr,
963 				     CdTextItem::PackType type) const
964 {
965   if (trackNr == 0) {
966     return cdtext_.getPack(blockNr, type);
967   }
968 
969   TrackEntry *track = findTrackByNumber(trackNr);
970 
971   if (track == NULL)
972     return NULL;
973 
974   return track->track->getCdTextItem(blockNr, type);
975 }
976 
cdTextLanguage(int blockNr,int lang)977 void Toc::cdTextLanguage(int blockNr, int lang)
978 {
979   cdtext_.language(blockNr, lang);
980 
981 }
982 
983 
cdTextLanguage(int blockNr) const984 int Toc::cdTextLanguage(int blockNr) const
985 {
986   return cdtext_.language(blockNr);
987 }
988 
989 // Check the consistency of the CD-TEXT data.
990 // Return: 0: OK
991 //         1: at least one warning occured
992 //         2: at least one error occured
checkCdTextData() const993 int Toc::checkCdTextData() const
994 {
995   TrackEntry *trun;
996   int err = 0;
997   int l;
998   int last;
999   int titleCnt, performerCnt, songwriterCnt, composerCnt, arrangerCnt;
1000   int messageCnt, isrcCnt, genreCnt;
1001   int languageCnt = 0;
1002 
1003   genreCnt = 0;
1004 
1005   // Check if language numbers are continuously used starting at 0
1006   for (l = 0, last = -1; l <= 7; l++) {
1007     if (cdTextLanguage(l) >= 0) {
1008       languageCnt++;
1009 
1010       if (cdtext_.getPack(l, CdTextItem::CDTEXT_GENRE) != NULL)
1011 	genreCnt++;
1012 
1013       if (l - 1 != last) {
1014 	if (last == -1)
1015 	  log_message(-2, "CD-TEXT: Language number %d: Language numbers must start at 0.", l);
1016 	else
1017 	  log_message(-2, "CD-TEXT: Language number %d: Language numbers are not continuously used.", l);
1018 
1019 	if (err < 2)
1020 	  err = 2;
1021       }
1022 
1023       last = l;
1024     }
1025   }
1026 
1027   if (genreCnt > 0 && genreCnt != languageCnt) {
1028     log_message(-1, "CD-TEXT: %s field not defined for all languages.",
1029 	    CdTextItem::packType2String(1, CdTextItem::CDTEXT_GENRE));
1030     if (err < 1)
1031       err = 1;
1032   }
1033 
1034 
1035   for (l = 0, last = -1; l <= 7; l++) {
1036     if (cdTextLanguage(l) < 0)
1037       continue;
1038 
1039     titleCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_TITLE) != NULL) ? 1 : 0;
1040     performerCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_PERFORMER) != NULL) ? 1 : 0;
1041     songwriterCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_SONGWRITER) != NULL) ? 1 : 0;
1042     composerCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_COMPOSER) != NULL) ? 1 : 0;
1043     arrangerCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_ARRANGER) != NULL) ? 1 : 0;
1044     messageCnt = (cdtext_.getPack(l, CdTextItem::CDTEXT_MESSAGE) != NULL) ? 1 : 0;
1045     isrcCnt = 0;
1046 
1047     for (trun = tracks_; trun != NULL; trun = trun->next) {
1048       if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_TITLE) != NULL)
1049 	titleCnt++;
1050 
1051       if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_PERFORMER) != NULL)
1052 	performerCnt++;
1053 
1054       if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_SONGWRITER) != NULL)
1055 	songwriterCnt++;
1056 
1057       if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_COMPOSER) != NULL)
1058 	composerCnt++;
1059 
1060       if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_ARRANGER) != NULL)
1061 	arrangerCnt++;
1062 
1063       if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_MESSAGE) != NULL)
1064 	messageCnt++;
1065 
1066       if (trun->track->getCdTextItem(l, CdTextItem::CDTEXT_UPCEAN_ISRC) != NULL)
1067 	isrcCnt++;
1068     }
1069 
1070     if (titleCnt > 0 && titleCnt != nofTracks_ + 1) {
1071       log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1072 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_TITLE));
1073       if (err < 2)
1074 	err = 2;
1075     }
1076     else if (titleCnt == 0) {
1077       log_message(-1, "CD-TEXT: Language %d: %s field is not defined.",
1078 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_TITLE));
1079       if (err < 1)
1080 	err = 1;
1081     }
1082 
1083     if (performerCnt > 0 && performerCnt != nofTracks_ + 1) {
1084       log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1085 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_PERFORMER));
1086       if (err < 2)
1087 	err = 2;
1088     }
1089     else if (performerCnt == 0) {
1090       log_message(-1, "CD-TEXT: Language %d: %s field is not defined.",
1091 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_PERFORMER));
1092       if (err < 1)
1093 	err = 1;
1094     }
1095 
1096     if (songwriterCnt > 0 && songwriterCnt != nofTracks_ + 1) {
1097       log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1098 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_SONGWRITER));
1099       if (err < 2)
1100 	err = 2;
1101     }
1102 
1103     if (composerCnt > 0 && composerCnt != nofTracks_ + 1) {
1104       log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1105 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_COMPOSER));
1106       if (err < 2)
1107 	err = 2;
1108     }
1109 
1110     if (arrangerCnt > 0 && arrangerCnt != nofTracks_ + 1) {
1111       log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1112 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_ARRANGER));
1113       if (err < 2)
1114 	err = 2;
1115     }
1116 
1117     if (messageCnt > 0 && messageCnt != nofTracks_ + 1) {
1118       log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks or disk.",
1119 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_MESSAGE));
1120       if (err < 2)
1121 	err = 2;
1122     }
1123 
1124     if ((isrcCnt > 0 && isrcCnt != nofTracks_) ||
1125 	(isrcCnt == 0 &&
1126 	 cdtext_.getPack(l, CdTextItem::CDTEXT_UPCEAN_ISRC) != NULL)) {
1127       log_message(-2, "CD-TEXT: Language %d: %s field not defined for all tracks.",
1128 	      l, CdTextItem::packType2String(1, CdTextItem::CDTEXT_UPCEAN_ISRC));
1129       if (err < 2)
1130 	err = 2;
1131     }
1132   }
1133 
1134   return err;
1135 }
1136 
1137 
trackSummary(int * nofAudioTracks,int * nofMode1Tracks,int * nofMode2Tracks) const1138 void Toc::trackSummary(int *nofAudioTracks, int *nofMode1Tracks,
1139 		       int *nofMode2Tracks) const
1140 {
1141   TrackEntry *run;
1142 
1143   if (nofAudioTracks != NULL)
1144     *nofAudioTracks = 0;
1145 
1146   if (nofMode1Tracks != NULL)
1147     *nofMode1Tracks = 0;
1148 
1149   if (nofMode2Tracks != NULL)
1150     *nofMode2Tracks = 0;
1151 
1152   for (run = tracks_; run != NULL; run = run->next) {
1153     switch (run->track->type()) {
1154     case TrackData::AUDIO:
1155       if (nofAudioTracks != NULL)
1156 	*nofAudioTracks += 1;
1157       break;
1158 
1159     case TrackData::MODE1:
1160     case TrackData::MODE1_RAW:
1161       if (nofMode1Tracks != NULL)
1162 	*nofMode1Tracks += 1;
1163       break;
1164 
1165     case TrackData::MODE2:
1166     case TrackData::MODE2_FORM1:
1167     case TrackData::MODE2_FORM2:
1168     case TrackData::MODE2_FORM_MIX:
1169     case TrackData::MODE2_RAW:
1170       if (nofMode2Tracks != NULL)
1171 	*nofMode2Tracks += 1;
1172       break;
1173 
1174     case TrackData::MODE0:
1175       break;
1176     }
1177   }
1178 }
1179 
1180 // Class TrackIterator
TrackIterator(const Toc * t)1181 TrackIterator::TrackIterator(const Toc *t)
1182 {
1183   toc_ = t;
1184   iterator_ = NULL;
1185 }
1186 
~TrackIterator()1187 TrackIterator::~TrackIterator()
1188 {
1189   toc_ = NULL;
1190   iterator_ = NULL;
1191 }
1192 
find(int trackNr,Msf & start,Msf & end)1193 const Track *TrackIterator::find(int trackNr, Msf &start, Msf &end)
1194 {
1195   Track *t;
1196 
1197   iterator_ = toc_->findTrackByNumber(trackNr);
1198 
1199   if (iterator_ != NULL) {
1200     start = iterator_->start;
1201     end = iterator_->end;
1202     t = iterator_->track;
1203     iterator_ = iterator_->next;
1204     return t;
1205   }
1206 
1207   return NULL;
1208 }
1209 
find(unsigned long sample,Msf & start,Msf & end,int * trackNr)1210 const Track *TrackIterator::find(unsigned long sample, Msf &start, Msf &end,
1211 				 int *trackNr)
1212 {
1213   Track *t;
1214 
1215   iterator_ = toc_->findTrack(sample);
1216 
1217   if (iterator_ != NULL) {
1218     start = iterator_->start;
1219     end = iterator_->end;
1220     *trackNr = iterator_->trackNr;
1221     t = iterator_->track;
1222     iterator_ = iterator_->next;
1223     return t;
1224   }
1225 
1226   return NULL;
1227 }
1228 
first(Msf & start,Msf & end)1229 const Track *TrackIterator::first(Msf &start, Msf &end)
1230 {
1231   iterator_ = toc_->tracks_;
1232 
1233   return next(start, end);
1234 }
1235 
first()1236 const Track *TrackIterator::first()
1237 {
1238   Msf start, end;
1239 
1240   return first(start, end);
1241 }
1242 
next(Msf & start,Msf & end)1243 const Track *TrackIterator::next(Msf &start, Msf &end)
1244 {
1245   Track *t;
1246 
1247   if (iterator_ != NULL) {
1248     start = iterator_->start;
1249     end = iterator_->end;
1250     t = iterator_->track;
1251     iterator_ = iterator_->next;
1252     return t;
1253   }
1254   else {
1255     return NULL;
1256   }
1257 }
1258 
next()1259 const Track *TrackIterator::next()
1260 {
1261   Msf start, end;
1262 
1263   return next(start, end);
1264 }
1265 
1266 // Class TocReader
1267 
TocReader(const Toc * t)1268 TocReader::TocReader(const Toc *t) : reader(NULL)
1269 {
1270   toc_ = t;
1271 
1272   readTrack_ = NULL;
1273   readPos_ = 0;
1274   readPosSample_ = 0;
1275   open_ = 0;
1276 }
1277 
~TocReader()1278 TocReader::~TocReader ()
1279 {
1280   if (open_) {
1281     closeData();
1282   }
1283 
1284   toc_ = NULL;
1285   readTrack_ = NULL;
1286 }
1287 
init(const Toc * t)1288 void TocReader::init(const Toc *t)
1289 {
1290   if (open_) {
1291     closeData();
1292   }
1293 
1294   reader.init(NULL);
1295 
1296   toc_ = t;
1297   readTrack_ = NULL;
1298 }
1299 
openData()1300 int TocReader::openData()
1301 {
1302   int ret = 0;
1303 
1304   assert(open_ == 0);
1305   assert(toc_ != NULL);
1306 
1307   readTrack_ = toc_->tracks_;
1308   readPos_ = 0;
1309   readPosSample_ = 0;
1310 
1311   reader.init(readTrack_->track);
1312 
1313   if (readTrack_ != NULL) {
1314     ret = reader.openData();
1315   }
1316 
1317   open_ = 1;
1318 
1319   return ret;
1320 }
1321 
closeData()1322 void TocReader::closeData()
1323 {
1324   if (open_ != 0) {
1325     reader.closeData();
1326 
1327     readTrack_ = NULL;
1328     readPos_ = 0;
1329     open_ = 0;
1330     readPosSample_ = 0;
1331   }
1332 }
1333 
1334 #if 0
1335 long TocReader::readData(long lba, char *buf, long len)
1336 {
1337   long n;
1338   long nread = 0;
1339 
1340   assert(open_ != 0);
1341 
1342   if (readPos_ + len > toc_->length_.lba()) {
1343     if ((len = toc_->length_.lba() - readPos_) <= 0) {
1344       return 0;
1345     }
1346   }
1347 
1348   do {
1349     n = reader.readData(0, lba, buf + (nread * AUDIO_BLOCK_LEN), len);
1350 
1351     if (n < 0) {
1352       return -1;
1353     }
1354 
1355     lba += n;
1356 
1357     if (n != len) {
1358       // skip to next track
1359       readTrack_ = readTrack_->next;
1360 
1361       assert(readTrack_ != NULL);
1362 
1363       reader.init(readTrack_->track);
1364       if (reader.openData() != 0) {
1365 	return -1;
1366       }
1367     }
1368 
1369     nread += n;
1370     len -= n;
1371   } while (len > 0);
1372 
1373   readPos_ += nread;
1374 
1375   return nread;
1376 }
1377 #endif
1378 
1379 // seeks to specified sample (absolute position)
1380 // return: 0: OK
1381 //         10: sample position out of range
1382 //         return codes from 'Track::openData()'
seekSample(unsigned long sample)1383 int TocReader::seekSample(unsigned long sample)
1384 {
1385   int ret;
1386 
1387   assert(open_ != 0);
1388 
1389   // find track that contains 'sample'
1390   Toc::TrackEntry *tr = toc_->findTrack(sample);
1391 
1392   if (tr == NULL)
1393     return 10;
1394 
1395   // open track if necessary
1396   if (tr != readTrack_) {
1397     readTrack_ = tr;
1398     reader.init(readTrack_->track);
1399 
1400     if ((ret = reader.openData() != 0))
1401       return ret;
1402   }
1403 
1404   assert(sample >= readTrack_->absStart.samples());
1405 
1406   unsigned long offset = sample - readTrack_->absStart.samples();
1407 
1408   // seek in track
1409   if ((ret = reader.seekSample(offset)) != 0)
1410     return ret;
1411 
1412   readPosSample_ = sample;
1413 
1414   return 0;
1415 }
1416 
readSamples(Sample * buf,long len)1417 long TocReader::readSamples(Sample *buf, long len)
1418 {
1419   long n;
1420   long nread = 0;
1421 
1422   assert(open_ != 0);
1423 
1424   if (readPosSample_ + (unsigned long)len > toc_->length_.samples()) {
1425     if ((len = toc_->length_.samples() - readPosSample_) <= 0)
1426       return 0;
1427   }
1428 
1429   do {
1430     n = reader.readSamples(buf + nread , len);
1431 
1432     if (n < 0)
1433       return -1;
1434 
1435     if (n != len) {
1436       // skip to next track
1437       readTrack_ = readTrack_->next;
1438       reader.init(readTrack_->track);
1439 
1440       assert(readTrack_ != NULL);
1441 
1442       if (reader.openData() != 0) {
1443 	return -1;
1444       }
1445     }
1446 
1447     nread += n;
1448     len -= n;
1449   } while (len > 0);
1450 
1451   readPosSample_ += nread;
1452 
1453   return nread;
1454 }
1455 
curFilename()1456 const char* TocReader::curFilename()
1457 {
1458     return reader.curFilename();
1459 }
1460