2 * FryingPan - Amiga CD/DVD Recording Software (User Interface and supporting Libraries only)
3 * Copyright (C) 2001-2011 Tomasz Wiszkowski Tomasz.Wiszkowski at gmail.com
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
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 Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "rCUESession.h"
21 #include <Generic/LibrarySpool.h>
22 #include <libclass/dos.h>
23 #include <Optical/IOptItem.h>
59 static file
*newFile(const String
&cue_location
, const String
&filename
, const String
&type
)
67 path
.AddPath(filename
);
69 fileptr
= DOS
->Open(path
.Data(), MODE_OLDFILE
);
77 fib
= new FileInfoBlock
;
78 DOS
->ExamineFH(fileptr
, fib
);
79 f
->length
= fib
->fib_Size
;
84 else if (type
== "MOTOROLA")
85 f
->type
= Type_Motorola
;
86 else if (type
== "AIFF")
88 else if (type
== "WAVE")
90 else if (type
== "MP3")
93 f
->type
= Type_Unknown
;
105 const String
& getFileName() const
110 uint32
getFileSize() const
115 file::filetype
getFileType() const
125 void seek(uint32 offset
) const
127 DOS
->Seek(fileptr
, offset
, OFFSET_BEGINNING
);
130 uint32
read(void* mem
, uint32 len
) const
132 return DOS
->Read(fileptr
, mem
, len
);
138 uint16 num
; // index number
139 uint32 loc
; // boundary: start
140 uint32 cnt
; // and count
157 static index
*newIndex(uint16 number
, uint32 location
)
159 index
*i
= new index();
168 void setSecCnt(uint32 scnt
)
173 uint32
getSecCnt() const
178 uint32
getLocation() const
183 void setLocation(uint32 l
)
188 void debug(DbgHandler
*__dbg
)
194 _D(Lvl_Info
, "Number: %ld", num
);
195 _D(Lvl_Info
, "Location: %ld", loc
);
196 _D(Lvl_Info
, "Sector Count: %ld", cnt
);
199 void setFile(file
* fl
)
204 file
*getFile() const
209 void setFileOffset(uint32 fo
)
214 void setDataLength(uint32 dl
)
219 uint32
getFileOffset() const
224 uint32
getDataLength() const
229 uint32
getNumber() const
234 void fillItem(IOptItem
* item
)
236 item
->setStartAddress(loc
);
237 item
->setItemNumber(num
);
238 item
->setDataBlockCount(cnt
);
243 currpos
= 0xffffffff;
246 uint32
read(void* mem
, uint32 len
)
248 if (currpos
== datalength
)
250 if (currpos
== 0xffffffff)
256 len
= len
< (datalength
- currpos
) ? len
: (datalength
- currpos
);
257 f
->read(mem
, len
); // i don't care if data is valid
258 currpos
+= len
; // if it's not, i won't interrupt the process
265 EDataType type
; // type of data
266 uint32 flags
; // additional flags (like "RAW")
267 uint16 secsize
; // resulting from the two above fields
268 uint16 pregap
; // in sectors
269 uint16 postgap
; // in sectors
270 String isrc
; // ISRC number
271 int16 number
; // track number
272 VectorT
<index
*> indices
; // indices
276 String arranger
; // CDText
295 static track
* newTrack(int32 num
, const String
&mode
)
297 track
*nt
= new track
;
302 nt
->type
= Data_Audio
, nt
->flags
= 0, nt
->secsize
= 2352;
303 else if (mode
== "MDOE1/2048")
304 nt
->type
= Data_Mode1
, nt
->flags
= 0, nt
->secsize
= 2048;
305 else if (mode
== "MODE2/2336")
306 nt
->type
= Data_Mode2
, nt
->flags
= 0, nt
->secsize
= 2336;
307 else if (mode
== "MODE1/2352")
308 nt
->type
= Data_Mode1
, nt
->flags
= DIF_Common_RawData
, nt
->secsize
= 2352;
309 else if (mode
== "MODE2/2352")
310 nt
->type
= Data_Mode2
, nt
->flags
= DIF_Common_RawData
, nt
->secsize
= 2352;
320 void debug(DbgHandler
*__dbg
)
325 _D(Lvl_Info
, "Number: %ld", number
);
326 _D(Lvl_Info
, "File type: %ld", type
);
327 _D(Lvl_Info
, "Flags: %ld", flags
);
328 _D(Lvl_Info
, "Sector Size: %ld", secsize
);
329 _D(Lvl_Info
, "PreGap: %ld", pregap
);
330 _D(Lvl_Info
, "PostGap: %ld", postgap
);
331 _D(Lvl_Info
, "ISRC: %s", (int32
)isrc
.Data());
332 _D(Lvl_Info
, "Arranger: %s", (int32
)arranger
.Data());
333 _D(Lvl_Info
, "Composer: %s", (int32
)composer
.Data());
334 _D(Lvl_Info
, "Performer: %s", (int32
)performer
.Data());
335 _D(Lvl_Info
, "Message: %s", (int32
)message
.Data());
336 _D(Lvl_Info
, "Title: %s", (int32
)title
.Data());
337 _D(Lvl_Info
, "Song Writer: %s", (int32
)songwriter
.Data());
339 for (int32 i
=0; i
<indices
.Count(); i
++)
340 indices
[i
]->debug(__dbg
);
345 void setArranger(const String
&s
)
350 void setComposer(const String
&s
)
355 void setPerformer(const String
&s
)
360 void setMessage(const String
&s
)
365 void setTitle(const String
&s
)
370 void setSongWriter(const String
&s
)
375 void setPreGap(int32 gap
)
380 void setPostGap(int32 gap
)
385 int32
getPreGap() const
390 int32
getPostGap() const
395 int32
getNumber() const
400 void setISRC(const String
&s
)
405 void addIndex(index
*i
)
410 index
*getIndex(int32 i
)
415 int32
getIndexCount()
417 return indices
.Count();
420 int32
getSectorSize() const
425 void fillItem(IOptItem
*item
)
429 item
->setCDTDirector(arranger
);
430 item
->setCDTComposer(composer
);
431 item
->setCDTArtist(performer
);
432 item
->setCDTMessage(message
);
433 item
->setCDTTitle(title
);
434 item
->setCDTLyrics(songwriter
);
435 item
->setFlags(flags
);
436 item
->setSectorSize(secsize
);
437 item
->setPreGapSize(pregap
);
438 item
->setItemNumber(number
);
439 item
->setDataType(type
);
441 item
->setStartAddress(indices
[0]->getLocation() - pregap
);
442 for (int32 i
=0; i
<indices
.Count(); i
++)
444 IOptItem
*indx
= item
->addChild();
446 indices
[i
]->fillItem(indx
);
447 len
+= indices
[i
]->getSecCnt();
449 item
->setDataBlockCount(len
);
453 // uint16 postgap; // in sectors
454 // String isrc; // ISRC number
459 for (int i
=0; i
<indices
.Count(); i
++)
465 uint32
read(void* &mem
, uint32 len
)
471 while (curridx
< indices
.Count())
473 ret
+= indices
[curridx
]->read(mem
, len
- ret
);
475 mem
= &((char*)mem
)[ret
];
483 return ret
/ secsize
;
490 // - use 'cdtextfile' parameter some day
491 // - apply params such as 'isrc', 'upc_ean', 'disc_id'
492 // - make class track return the data to the reader!!!!
496 rCUESession::rCUESession(const char* sFileName
, EDtError
&rc
)
498 _createDebug(true, "rCUESession");
500 analyse(sFileName
, rc
);
503 rCUESession::~rCUESession()
505 tracks
.ForEach(&freeTrack
);
506 for (int i
=0; i
<files
.Count(); i
++)
514 bool rCUESession::freeTrack(track
* const& t
)
520 IFileReader
* rCUESession::openRead(const char* sFile
, EDtError
&rc
)
522 IFileReader
*out
= 0;
524 checkFile(sFile
, rc
);
527 out
= new rCUESession(sFile
, rc
);
537 const char* rCUESession::getName()
539 return static_getName();
542 bool rCUESession::readData(const IOptItem
* item
, void* mem
, int len
)
544 if (item
->getItemType() != Item_Track
)
548 if (currtrk
== tracks
.Count())
551 ret
= tracks
[currtrk
]->read(mem
, len
);
556 ret
+= readData(item
, mem
, len
-ret
);
562 ASSERTS(false, "Functionality not yet implemented, sorry.");
567 bool rCUESession::setUp()
569 _d(Lvl_Info
, "%s: seeking all items to the start position...", (int)__PRETTY_FUNCTION__
);
571 for (int i
=0; i
<tracks
.Count(); i
++)
574 for (int i
=0; i
<files
.Count(); i
++)
575 DOS
->Seek(files
[i
]->getFile(), 0, OFFSET_BEGINNING
);
582 bool rCUESession::analyse(const char* name
, EDtError
&rc
)
589 track
*nt
= 0; // new track
592 rc
= DT_UnableToOpenFile
;
594 _d(Lvl_Info
, "%s: starting analysis. File name: %s", (int)__PRETTY_FUNCTION__
, (int)name
);
596 fh
= DOS
->Open(const_cast<char*>(name
), MODE_OLDFILE
);
600 BPTR lock
= DOS
->ParentOfFH(fh
);
601 DOS
->NameFromLock(lock
, (char*)s
, sizeof(s
));
604 _d(Lvl_Info
, "%s: current path for binaries: %s", (int)__PRETTY_FUNCTION__
, (int)path
.Data());
608 while (DOS
->FGets(fh
, (char*)s
, sizeof(s
)) != 0)
610 for (int i
=0; s
[i
] != 0; i
++)
612 if ((s
[i
] != 10) && (s
[i
] != 13) && (s
[i
] != '\t') && (s
[i
]<' '))
617 if ((s
[i
] >=128) && (s
[i
]<160))
624 String
str((char*)s
);
625 VectorT
<String
> v
= str
.Explode();
629 str
= v
[0].UpperCase();
635 _d(Lvl_Info
, "%s: parsing track", (int)__PRETTY_FUNCTION__
);
637 tn
= v
[1].SubString(1, -1).ToLong();
641 nt
= track::newTrack(tn
, v
[2]);
645 _d(Lvl_Info
, "%s: failed to create track", (int)__PRETTY_FUNCTION__
);
646 rc
= DT_FileMalformed
;
653 else if (str
== "INDEX")
657 _d(Lvl_Info
, "%s: parsing index", (int)__PRETTY_FUNCTION__
);
658 if ((files
.Count() == 0) || (nt
== 0))
660 _d(Lvl_Info
, "%s: files or tracks not defined yet", (int)__PRETTY_FUNCTION__
);
661 rc
= DT_FileMalformed
;
666 uint32 pos
= parseLocation(v
[2]);
668 idx
->setSecCnt(pos
-idx
->getLocation());
671 tn
= v
[1].SubString(1, -1).ToLong();
675 idx
= index::newIndex(tn
, pos
);
678 else if (str
== "FILE")
681 _d(Lvl_Info
, "%s: parsing file", (int)__PRETTY_FUNCTION__
);
683 f
= file::newFile(path
, v
[1], v
[2]);
686 _d(Lvl_Info
, "%s: failed to create file entry for %s", (int)__PRETTY_FUNCTION__
, (int)v
[1].Data());
687 rc
= DT_UnableToOpenFile
;
695 if (f
->getFileType() != file::Type_Intel
)
697 _d(Lvl_Warning
, "%s: Unsupported file format: %s", (int)__PRETTY_FUNCTION__
, (int)v
[2].Data());
698 rc
= DT_FileFormatNotSupported
;
703 else if (str
== "FLAGS")
705 _d(Lvl_Info
, "%s: flags are not supported at the moment.", (int)__PRETTY_FUNCTION__
);
706 rc
= DT_FileFormatNotSupported
;
710 else if (str
== "REM")
712 _d(Lvl_Info
, "%s: %s", (int)__PRETTY_FUNCTION__
, (int)str
.Data());
714 else if (str
== "CATALOG")
716 _d(Lvl_Info
, "%s: parsing catalog", (int)__PRETTY_FUNCTION__
);
717 catalog
= v
[1].ToQuad();
719 else if (str
== "CDTEXTFILE")
721 _d(Lvl_Warning
, "%s: CDTEXTFILE TAG IS NOT SUPPORTED!", (int)__PRETTY_FUNCTION__
);
723 else if (str
== "ISRC")
725 _d(Lvl_Info
, "%s: reading ISRC", (int)__PRETTY_FUNCTION__
);
728 rc
= DT_FileMalformed
;
734 else if (str
== "PREGAP")
736 _d(Lvl_Info
, "%s: parsing pregap", (int)__PRETTY_FUNCTION__
);
739 rc
= DT_FileMalformed
;
743 nt
->setPreGap(v
[1].ToLong());
744 _d(Lvl_Info
, "%s: pregap size: %ld", (int)__PRETTY_FUNCTION__
, v
[1].ToLong());
746 else if (str
== "POSTGAP")
748 _d(Lvl_Warning
, "%s: postgaps not supported", (int)__PRETTY_FUNCTION__
);
749 rc
= DT_FileFormatNotSupported
;
753 else if (str
== "ARRANGER")
755 _d(Lvl_Info
, "%s: read arranger: %s", (int)__PRETTY_FUNCTION__
, (int)v
[1].Data());
757 nt
->setArranger(v
[1]);
761 else if (str
== "COMPOSER")
763 _d(Lvl_Info
, "%s: read composer: %s", (int)__PRETTY_FUNCTION__
, (int)v
[1].Data());
765 nt
->setComposer(v
[1]);
769 else if (str
== "DISC_ID")
771 _d(Lvl_Warning
, "%s: ignoring disc id", (int)__PRETTY_FUNCTION__
);
774 else if (str
== "GENRE")
776 _d(Lvl_Warning
, "%s: ignoring genre", (int)__PRETTY_FUNCTION__
);
778 // safe to skip this one.
780 else if (str
== "MESSAGE")
782 _d(Lvl_Info
, "%s: read message: %s", (int)__PRETTY_FUNCTION__
, (int)v
[1].Data());
784 nt
->setMessage(v
[1]);
788 else if (str
== "PERFORMER")
790 _d(Lvl_Info
, "%s: read performer: %s", (int)__PRETTY_FUNCTION__
, (int)v
[1].Data());
792 nt
->setPerformer(v
[1]);
796 else if (str
== "SONGWRITER")
798 _d(Lvl_Info
, "%s: read songwriter: %s", (int)__PRETTY_FUNCTION__
, (int)v
[1].Data());
800 nt
->setSongWriter(v
[1]);
804 else if (str
== "TITLE")
806 _d(Lvl_Info
, "%s: read title: %s", (int)__PRETTY_FUNCTION__
, (int)v
[1].Data());
812 else if (str
== "TOC_INFO")
814 _d(Lvl_Warning
, "%s: TOC INFO not supported", (int)__PRETTY_FUNCTION__
);
815 rc
= DT_FileFormatNotSupported
;
819 else if (str
== "TOC_INFO2")
821 _d(Lvl_Warning
, "%s: TOC INFO not supported", (int)__PRETTY_FUNCTION__
);
822 rc
= DT_FileFormatNotSupported
;
826 else if (str
== "UPC_EAN")
828 _d(Lvl_Warning
, "%s: ignoring UPC", (int)__PRETTY_FUNCTION__
);
831 else if (str
== "SIZE_INFO")
833 _d(Lvl_Warning
, "%s: alien SIZE INFO not supported", (int)__PRETTY_FUNCTION__
);
834 rc
= DT_FileFormatNotSupported
;
840 _d(Lvl_Error
, "%s: Malformed tag %s. Aborting.", (int)__PRETTY_FUNCTION__
, (int)v
[0].Data());
841 rc
= DT_FileMalformed
;
849 if (tracks
.Count() == 0)
851 rc
= DT_FileMalformed
;
855 for (int32 i
=0; i
<tracks
.Count(); i
++)
857 if (tracks
[i
]->getIndexCount() == 0)
860 rc
= DT_FileMalformed
;
865 _d(Lvl_Info
, "%s: fixing references...", (int)__PRETTY_FUNCTION__
);
869 uint32 len_so_far
= 0;
870 int32 sec_so_far
= -150;
873 if ((tracks
[0]->getIndex(0)->getNumber() != 0) && (tracks
[0]->getPreGap() < 150))
874 tracks
[0]->setPreGap(150);
876 for (int32 i
=0; i
<tracks
.Count(); i
++)
878 _d(Lvl_Info
, "%s: Advancing to track %ld", (int)__PRETTY_FUNCTION__
, i
+1);
879 track
*t
= tracks
[i
];
881 sec_so_far
+= t
->getPreGap();
883 for (int32 j
=0; j
<t
->getIndexCount(); j
++)
885 _d(Lvl_Info
, "%s: Checking index %ld", (int)__PRETTY_FUNCTION__
, j
+1);
886 index
*x
= t
->getIndex(j
);
888 if ((x
->getNumber() == 0) && (t
->getPreGap() != 0))
890 _d(Lvl_Warning
, "%s: CUE specifies both pregap and index 0, it's a nonsense.", (int32
)__PRETTY_FUNCTION__
);
891 rc
= DT_FileMalformed
;
896 x
->setLocation(sec_so_far
);
897 x
->setFile(files
[fno
]);
898 x
->setFileOffset(len_so_far
);
900 if (x
->getSecCnt() == 0xffffffff)
902 _d(Lvl_Info
, "%s: Altering index accordingly to track %ld", (int)__PRETTY_FUNCTION__
, fno
+1);
903 x
->setSecCnt((files
[fno
]->getFileSize() - len_so_far
) / t
->getSectorSize());
904 x
->setDataLength(x
->getSecCnt() * t
->getSectorSize());
911 x
->setDataLength(x
->getSecCnt() * t
->getSectorSize());
912 len_so_far
+= x
->getSecCnt() * t
->getSectorSize();
915 sec_so_far
+= x
->getSecCnt();
918 sec_so_far
+= t
->getPostGap();
925 for (int32 i
=0; i
<tracks
.Count(); i
++)
926 tracks
[i
]->debug(getDebug());
932 uint32
rCUESession::parseLocation(String
&s
)
936 * despite MSF holds LBA+150 in normal case,
937 * we are actually referring to data stored in a file
938 * thus there is no decrement!
946 while ((c
[i
] != ':') && (i
< c
.Length()))
948 loc
= 10 * loc
+ c
[i
]-'0';
956 while ((c
[i
] != ':') && (i
< c
.Length()))
958 loc
= 10 * loc
+ c
[i
]-'0';
962 tot
= (tot
+ loc
) * 75;
966 while ((c
[i
] != ':') && (i
< c
.Length()))
968 loc
= 10 * loc
+ c
[i
]-'0';
974 _d(Lvl_Info
, "%s: Location parsed - %s = %ld", (int)__PRETTY_FUNCTION__
, (int)s
.Data(), tot
);
979 void rCUESession::cleanUp()
983 void rCUESession::dispose()
988 bool rCUESession::isAudio()
990 return static_isAudio();
993 bool rCUESession::isData()
995 return static_isData();
998 bool rCUESession::fillOptItem(IOptItem
*disc
)
1001 if (disc
->getItemType() != Item_Disc
)
1004 disc
->setFlags(DIF_Disc_MasterizeCD
| DIF_Disc_CloseDisc
);
1005 IOptItem
*sess
= disc
->addChild();
1007 sess
->setCDTDirector(arranger
);
1008 sess
->setCDTComposer(composer
);
1009 sess
->setCDTArtist(performer
);
1010 sess
->setCDTMessage(message
);
1011 sess
->setCDTTitle(title
);
1012 sess
->setCDTLyrics(songwriter
);
1014 for (int i
=0; i
<tracks
.Count(); i
++)
1016 IOptItem
*trak
= sess
->addChild();
1017 tracks
[i
]->fillItem(trak
);
1022 const char *rCUESession::getTrackName()
1024 return static_getName();
1027 uint32
rCUESession::getBlockCount()
1032 uint16
rCUESession::getBlockSize()
1037 const char *rCUESession::static_getName()
1039 return "CUE Sheet Session";
1042 bool rCUESession::static_isAudio()
1047 bool rCUESession::static_isData()
1052 bool rCUESession::static_isSession()
1057 bool rCUESession::checkFile(const char* file
, EDtError
&rc
)
1063 rc
= DT_UnableToOpenFile
;
1065 fh
= DOS
->Open(const_cast<char*>(file
), MODE_OLDFILE
);
1071 while (DOS
->FGets(fh
, (char*)s
, sizeof(s
)) != 0)
1073 for (int i
=0; s
[i
] != 0; i
++)
1075 if ((s
[i
] != 10) && (s
[i
] != 13) && (s
[i
] != '\t') && (s
[i
]<' '))
1078 rc
= DT_FileMalformed
;
1081 if ((s
[i
] >=128) && (s
[i
]<160))
1084 rc
= DT_FileMalformed
;
1089 String
str((char*)s
);
1090 VectorT
<String
> v
= str
.Explode();
1094 str
= v
[0].UpperCase();
1096 if ((str
== "TRACK") ||
1101 (str
== "CATALOG") ||
1102 (str
== "CDTEXTFILE") ||
1104 (str
== "POSTGAP") ||
1105 (str
== "PREGAP") ||
1106 (str
== "ARRANGER") ||
1107 (str
== "COMPOSER") ||
1108 (str
== "DISC_ID") ||
1110 (str
== "MESSAGE") ||
1111 (str
== "PERFORMER") ||
1112 (str
== "SONGWRITER") ||
1114 (str
== "TOC_INFO") ||
1115 (str
== "TOC_INFO2") ||
1116 (str
== "UPC_EAN") ||
1117 (str
== "SIZE_INFO"))
1123 rc
= DT_FileMalformed
;
1135 DbgHandler
* rCUESession::getDebug()
1140 void rCUESession::setDebug(DbgHandler
* nd
)