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 "rNRGSession.h"
21 #include <Generic/LibrarySpool.h>
22 #include <libclass/dos.h>
23 #include <Optical/IOptItem.h>
27 #define MAKE_ID(a,b,c,d) ((((ULONG)(a)) << 24) | \
28 (((ULONG)(b)) << 16) | \
29 (((ULONG)(c)) << 8) | \
61 static file
*newFile(const String
&filename
)
66 fileptr
= DOS
->Open(filename
.Data(), MODE_OLDFILE
);
71 f
->filename
= filename
;
84 const String
& getFileName() const
89 void seek(uint32 offset
) const
91 DOS
->Seek(fileptr
, offset
, OFFSET_BEGINNING
);
94 uint32
read(void* mem
, uint32 len
) const
96 return DOS
->Read(fileptr
, mem
, len
);
107 uint16 num
; // index number
108 uint32 loc
; // boundary: start
109 uint32 cnt
; // and count
119 static index
*newIndex(uint16 number
, uint32 location
)
121 index
*i
= new index();
123 i
->num
= (number
>> 4) * 10 + (number
& 15);
130 void setEndAddress(uint32 scnt
)
135 uint32
getSecCnt() const
140 uint32
getLocation() const
145 void setLocation(uint32 l
)
150 uint32
getNumber() const
155 void fillItem(IOptItem
* item
)
157 item
->setStartAddress(loc
);
158 item
->setItemNumber(num
);
159 item
->setDataBlockCount(cnt
);
170 EDataType type
; // type of data
171 uint32 flags
; // additional flags (like "RAW")
172 uint16 secsize
; // resulting from the two above fields
173 uint16 pregap
; // in sectors
174 String isrc
; // ISRC number
175 int16 number
; // track number
176 VectorT
<index
*> indices
; // indices
178 String arranger
; // CDText
201 static track
* newTrack(file
*f
, int32 num
)
203 track
*nt
= new track
;
205 nt
->number
= (num
>> 4) * 10 + (num
& 15);
209 const file
*getFile() const
214 void setType(EDataType dt
)
219 void setSectorSize(uint16 size
)
224 void setDataStart(uint64 pos
)
229 void setDataEnd(uint64 pos
)
231 length
= pos
- filepos
;
234 void setDataMode2(uint8 mode
)
238 EDataType
getType() const
243 void debug(DbgHandler
*__dbg
)
248 _D(Lvl_Info
, "Number: %ld", number
);
249 _D(Lvl_Info
, "File type: %ld", type
);
250 _D(Lvl_Info
, "Flags: %ld", flags
);
251 _D(Lvl_Info
, "Sector Size: %ld", secsize
);
252 _D(Lvl_Info
, "PreGap: %ld", pregap
);
253 _D(Lvl_Info
, "ISRC: %s", (int32
)isrc
.Data());
254 _D(Lvl_Info
, "Arranger: %s", (int32
)arranger
.Data());
255 _D(Lvl_Info
, "Composer: %s", (int32
)composer
.Data());
256 _D(Lvl_Info
, "Performer: %s", (int32
)performer
.Data());
257 _D(Lvl_Info
, "Message: %s", (int32
)message
.Data());
258 _D(Lvl_Info
, "Title: %s", (int32
)title
.Data());
259 _D(Lvl_Info
, "Song Writer: %s", (int32
)songwriter
.Data());
264 void setArranger(const String
&s
)
269 void setComposer(const String
&s
)
274 void setPerformer(const String
&s
)
279 void setMessage(const String
&s
)
284 void setTitle(const String
&s
)
289 void setSongWriter(const String
&s
)
294 void setPreGap(int32 gap
)
299 int32
getPreGap() const
304 int32
getNumber() const
309 void setISRC(const String
&s
)
314 void addIndex(index
*i
)
319 index
*getIndex(int32 i
)
324 int32
getIndexCount()
326 return indices
.Count();
329 int32
getSectorSize() const
334 void fillItem(IOptItem
*item
)
338 item
->setCDTDirector(arranger
);
339 item
->setCDTComposer(composer
);
340 item
->setCDTArtist(performer
);
341 item
->setCDTMessage(message
);
342 item
->setCDTTitle(title
);
343 item
->setCDTLyrics(songwriter
);
344 item
->setFlags(flags
);
345 item
->setSectorSize(secsize
);
346 item
->setPreGapSize(pregap
);
347 item
->setItemNumber(number
);
348 item
->setDataType(type
);
349 item
->setSectorSize(secsize
);
351 item
->setStartAddress(indices
[0]->getLocation() - pregap
);
352 for (int32 i
=0; i
<indices
.Count(); i
++)
354 if (indices
[i
]->isValid() == false)
357 IOptItem
*indx
= item
->addChild();
359 indices
[i
]->fillItem(indx
);
360 len
+= indices
[i
]->getSecCnt();
362 item
->setDataBlockCount(len
);
370 uint32
read(void* &mem
, uint32 len
)
379 else if ((uint64
)currpos
>= length
)
382 len
= len
< (length
- currpos
) ? len
: (length
- currpos
);
384 mem
= &((char*)mem
)[len
];
387 return len
/ secsize
;
397 uint32 type
; // 0 = mode1, 1 = ?, 2 = mode2form1, 3 = mixed mode2
398 uint32 secpos
; // without pregaps
399 uint32 dummy
; // whatever.
407 uint32 secpos
; // as above
414 uint8 track
; // BCD track number
415 uint8 index
; // index (bcd?)
417 uint32 sector
; // ...
458 uint64 pregap
; // pregap data start
459 uint64 offset
; // normal data start
460 uint64 end
; // normal data end
465 rNRGSession::rNRGSession(const char* sFileName
, EDtError
&rc
)
467 _createDebug(true, "rNRGSession");
470 analyse(sFileName
, rc
);
473 rNRGSession::~rNRGSession()
475 tracks
.ForEach(&freeTrack
);
484 bool rNRGSession::freeTrack(track
* const& t
)
490 IFileReader
* rNRGSession::openRead(const char* sFile
, EDtError
&rc
)
492 IFileReader
*out
= 0;
494 checkFile(sFile
, rc
);
497 out
= new rNRGSession(sFile
, rc
);
507 const char* rNRGSession::getName()
509 return static_getName();
512 bool rNRGSession::readData(const IOptItem
* item
, void* mem
, int len
)
514 _d(Lvl_Info
, "%s: reading -> %08lx, %ld, curr trk: %ld; bptr=%lx", (int)__PRETTY_FUNCTION__
, (int)mem
, len
, currtrk
, (int)tracks
[currtrk
]->getFile()->getBPTR());
516 if (item
->getItemType() != Item_Track
)
520 if (currtrk
== tracks
.Count())
523 ret
= tracks
[currtrk
]->read(mem
, len
);
528 ret
+= readData(item
, mem
, len
-ret
);
534 ASSERTS(false, "Functionality not yet implemented, sorry.");
539 bool rNRGSession::setUp()
541 _d(Lvl_Info
, "%s: seeking all items to the start position...", (int)__PRETTY_FUNCTION__
);
543 for (int i
=0; i
<tracks
.Count(); i
++)
554 bool rNRGSession::analyse(const char* name
, EDtError
&rc
)
564 rc
= DT_UnableToOpenFile
;
566 fh
= DOS
->Open(name
, MODE_OLDFILE
);
570 f
= file::newFile(name
);
574 DOS
->Seek(fh
, -4, OFFSET_END
);
575 DOS
->Read(fh
, &data
, 4);
578 DOS
->Seek(fh
, data
, OFFSET_BEGINNING
);
582 DOS
->Read(fh
, &data
, 4);
583 DOS
->Read(fh
, &size
, 4);
586 if (data
== MAKE_ID('C','D','T','X'))
588 _d(Lvl_Info
, "%s: CDTX Tag", (int)__PRETTY_FUNCTION__
);
589 DOS
->Seek(fh
, size
, OFFSET_CURRENT
);
591 else if (data
== MAKE_ID('C','U','E','X'))
593 _d(Lvl_Info
, "%s: CUEX Tag", (int)__PRETTY_FUNCTION__
);
594 cue1
= (nrg_cuex
*)new char[size
];
595 DOS
->Read(fh
, cue1
, size
);
597 else if (data
== MAKE_ID('C','U','E','S'))
599 _d(Lvl_Info
, "%s: CUES Tag", (int)__PRETTY_FUNCTION__
);
600 DOS
->Seek(fh
, size
, OFFSET_CURRENT
);
602 else if (data
== MAKE_ID('D','A','O','X'))
604 _d(Lvl_Info
, "%s: DAOX Tag", (int)__PRETTY_FUNCTION__
);
605 dao2
= (nrg_daox
*)new char[size
];
606 DOS
->Read(fh
, dao2
, size
);
608 else if (data
== MAKE_ID('D','A','O','I'))
610 _d(Lvl_Info
, "%s: DAOI Tag", (int)__PRETTY_FUNCTION__
);
611 dao1
= (nrg_daoi
*)new char[size
];
612 DOS
->Read(fh
, dao1
, size
);
614 _d(Lvl_Info
, "%s: DAOI: ftrk=%ld, ltrk=%ld", (int)__PRETTY_FUNCTION__
, dao1
->first_track
, dao1
->last_track
);
615 _d(Lvl_Info
, "%s: DAOI: MCN=%02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx", (int)__PRETTY_FUNCTION__
, dao1
->mcn
[0], dao1
->mcn
[1], dao1
->mcn
[2], dao1
->mcn
[3], dao1
->mcn
[4], dao1
->mcn
[5], dao1
->mcn
[6], dao1
->mcn
[7], dao1
->mcn
[8], dao1
->mcn
[9], dao1
->mcn
[10], dao1
->mcn
[11], dao1
->mcn
[12], dao1
->mcn
[13]);
617 for (int i
=0; i
<=(dao1
->last_track
-dao1
->first_track
); i
++)
619 _d(Lvl_Info
, "%s: DAOI: track=%ld secsize=%ld mode=%ld type=%ld pregap=%lx data=%lx end=%lx",
620 (int)__PRETTY_FUNCTION__
,
622 dao1
->track
[i
].secsize
,
625 dao1
->track
[i
].pregap
,
626 dao1
->track
[i
].offset
,
630 else if (data
== MAKE_ID('E','T','N','F'))
632 _d(Lvl_Info
, "%s: ETNF Tag", (int)__PRETTY_FUNCTION__
);
633 int32 num
= (size
/ sizeof(nrg_etnf
));
634 nrg_etnf
*etnf
= (nrg_etnf
*)new char[size
];
635 DOS
->Read(fh
, etnf
, size
);
637 for (int i
=0; i
<num
; i
++)
639 _d(Lvl_Info
, "%s: ETNF: start=%ld length=%ld type=%ld sector=%ld other=%lx", (int)__PRETTY_FUNCTION__
, etnf
[i
].start
, etnf
[i
].length
, etnf
[i
].type
, etnf
[i
].secpos
, etnf
[i
].dummy
);
644 else if (data
== MAKE_ID('E','T','N','2'))
646 _d(Lvl_Info
, "%s: ETN2 Tag", (int)__PRETTY_FUNCTION__
);
647 int32 num
= (size
/ sizeof(nrg_etn2
));
648 nrg_etn2
*etnf
= (nrg_etn2
*)new char[size
];
649 DOS
->Read(fh
, etnf
, size
);
651 for (int i
=0; i
<num
; i
++)
653 _d(Lvl_Info
, "%s: ETN2: start=%ld length=%ld type=%ld sector=%ld other=%lx", (int)__PRETTY_FUNCTION__
, etnf
[i
].start
, etnf
[i
].length
, etnf
[i
].type
, etnf
[i
].secpos
, etnf
[i
].dummy
);
658 else if (data
== MAKE_ID('N','E','R','O'))
660 _d(Lvl_Info
, "%s: NERO Tag", (int)__PRETTY_FUNCTION__
);
661 DOS
->Seek(fh
, size
, OFFSET_CURRENT
);
663 else if (data
== MAKE_ID('N','E','R','5'))
665 _d(Lvl_Info
, "%s: NER5 Tag", (int)__PRETTY_FUNCTION__
);
666 DOS
->Seek(fh
, size
, OFFSET_CURRENT
);
668 else if (data
== MAKE_ID('S','I','N','F'))
670 _d(Lvl_Info
, "%s: SINF Tag", (int)__PRETTY_FUNCTION__
);
671 DOS
->Seek(fh
, size
, OFFSET_CURRENT
);
673 else if (data
== MAKE_ID('M','T','Y','P'))
675 _d(Lvl_Info
, "%s: MTYP Tag", (int)__PRETTY_FUNCTION__
);
676 DOS
->Seek(fh
, size
, OFFSET_CURRENT
);
678 else if (data
== MAKE_ID('E','N','D','!'))
680 _d(Lvl_Info
, "%s: END! Tag", (int)__PRETTY_FUNCTION__
);
688 res
= buildTrackList(cue1
, rc
);
689 if ((dao2
!= 0) && (res
== true))
690 res
= updateTrackData(dao2
, rc
);
691 if ((dao1
!= 0) && (res
== true))
692 res
= updateTrackData(dao2
, rc
);
705 bool rNRGSession::buildTrackList(nrg_cuex
*cue
, EDtError
&rc
)
709 rc
= DT_FileFormatNotSupported
;
718 _d(Lvl_Info
, "%s: CUEX: type=%02lx track=%02lx adrctl=%02lx res=%02lx sector=%08lx", (int)__PRETTY_FUNCTION__
, cue
[i
].adr_ctl
, cue
[i
].track
, cue
[i
].index
, cue
[i
].res
, cue
[i
].sector
);
720 if (cue
[i
].track
== 0)
723 if (tracks
.Count() > 0)
724 tracks
[-1]->getIndex(-1)->setEndAddress(cue
[i
].sector
);
726 if (cue
[i
].track
== 0xaa)
729 for (int j
=0; j
<tracks
.Count(); j
++)
731 if (tracks
[j
]->getNumber() == ((cue
[i
].track
>> 4) * 10 + (cue
[i
].track
& 15)))
740 t
= track::newTrack(f
, cue
[i
].track
);
743 switch (cue
[i
].adr_ctl
& 0xc0)
746 t
->setType(Data_Mode1
);
749 t
->setType(Data_Audio
);
752 rc
= DT_FileFormatNotSupported
;
757 x
= index::newIndex(cue
[i
].index
, cue
[i
].sector
);
765 bool rNRGSession::updateTrackData(struct nrg_daox
* dao
, EDtError
&rc
)
769 rc
= DT_FileFormatNotSupported
;
773 _d(Lvl_Info
, "%s: DAO: ftrk=%ld, ltrk=%ld", (int)__PRETTY_FUNCTION__
, dao
->first_track
, dao
->last_track
);
774 _d(Lvl_Info
, "%s: DAO: MCN=%02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx", (int)__PRETTY_FUNCTION__
, dao
->mcn
[0], dao
->mcn
[1], dao
->mcn
[2], dao
->mcn
[3], dao
->mcn
[4], dao
->mcn
[5], dao
->mcn
[6], dao
->mcn
[7], dao
->mcn
[8], dao
->mcn
[9], dao
->mcn
[10], dao
->mcn
[11], dao
->mcn
[12], dao
->mcn
[13]);
776 for (int i
=0; i
<=(dao
->last_track
-dao
->first_track
); i
++)
780 _d(Lvl_Info
, "%s: DAO: track=%ld secsize=%ld mode=%ld type=%ld pregap=%lx data=%lx end=%lx",
781 (int)__PRETTY_FUNCTION__
,
783 dao
->track
[i
].secsize
,
786 dao
->track
[i
].pregap
,
787 dao
->track
[i
].offset
,
790 for (int j
=0; j
<tracks
.Count(); j
++)
792 if (tracks
[j
]->getNumber() == (i
+ dao
->first_track
))
801 rc
= DT_FileMalformed
;
805 t
->setSectorSize(dao
->track
[i
].secsize
);
806 switch (dao
->track
[i
].mode
)
809 t
->setType(Data_Mode1
);
812 t
->setType(Data_Mode2Form1
);
815 t
->setType(Data_Mode2Form2
);
819 t
->setType(Data_Mode2
);
822 t
->setType(Data_Audio
);
825 rc
= DT_FileFormatNotSupported
;
828 t
->setDataStart(dao
->track
[i
].pregap
);
829 t
->setDataEnd(dao
->track
[i
].end
);
830 t
->setDataMode2(dao
->track
[i
].mode
);
832 _d(Lvl_Info
, "%s: Track %ld: %ld - %ld, mode %ld", (int)__PRETTY_FUNCTION__
, i
, dao
->track
[i
].pregap
, dao
->track
[i
].end
, dao
->track
[i
].mode
);
837 bool rNRGSession::updateTrackData(struct nrg_daoi
* dao
, EDtError
&rc
)
841 rc
= DT_FileFormatNotSupported
;
845 _d(Lvl_Info
, "%s: DAO: ftrk=%ld, ltrk=%ld", (int)__PRETTY_FUNCTION__
, dao
->first_track
, dao
->last_track
);
846 _d(Lvl_Info
, "%s: DAO: MCN=%02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx", (int)__PRETTY_FUNCTION__
, dao
->mcn
[0], dao
->mcn
[1], dao
->mcn
[2], dao
->mcn
[3], dao
->mcn
[4], dao
->mcn
[5], dao
->mcn
[6], dao
->mcn
[7], dao
->mcn
[8], dao
->mcn
[9], dao
->mcn
[10], dao
->mcn
[11], dao
->mcn
[12], dao
->mcn
[13]);
848 for (int i
=0; i
<=(dao
->last_track
-dao
->first_track
); i
++)
852 _d(Lvl_Info
, "%s: DAO: track=%ld secsize=%ld mode=%ld type=%ld pregap=%lx data=%lx end=%lx",
853 (int)__PRETTY_FUNCTION__
,
855 dao
->track
[i
].secsize
,
858 dao
->track
[i
].pregap
,
859 dao
->track
[i
].offset
,
862 for (int j
=0; j
<tracks
.Count(); j
++)
864 if (tracks
[j
]->getNumber() == (i
+ dao
->first_track
))
873 rc
= DT_FileMalformed
;
877 t
->setSectorSize(dao
->track
[i
].secsize
);
878 switch (dao
->track
[i
].mode
)
881 t
->setType(Data_Mode1
);
884 t
->setType(Data_Mode2Form1
);
887 t
->setType(Data_Mode2Form2
);
891 t
->setType(Data_Mode2
);
894 t
->setType(Data_Audio
);
897 rc
= DT_FileFormatNotSupported
;
900 t
->setDataStart(dao
->track
[i
].pregap
);
901 t
->setDataEnd(dao
->track
[i
].end
);
902 t
->setDataMode2(dao
->track
[i
].mode
);
907 void rNRGSession::cleanUp()
911 void rNRGSession::dispose()
916 bool rNRGSession::isAudio()
918 return static_isAudio();
921 bool rNRGSession::isData()
923 return static_isData();
926 bool rNRGSession::fillOptItem(IOptItem
*disc
)
929 if (disc
->getItemType() != Item_Disc
)
932 disc
->setFlags(DIF_Disc_MasterizeCD
| DIF_Disc_CloseDisc
);
933 IOptItem
*sess
= disc
->addChild();
935 sess
->setCDTDirector(arranger
);
936 sess
->setCDTComposer(composer
);
937 sess
->setCDTArtist(performer
);
938 sess
->setCDTMessage(message
);
939 sess
->setCDTTitle(title
);
940 sess
->setCDTLyrics(songwriter
);
942 for (int i
=0; i
<tracks
.Count(); i
++)
944 IOptItem
*trak
= sess
->addChild();
945 tracks
[i
]->fillItem(trak
);
950 const char *rNRGSession::getTrackName()
952 return static_getName();
955 uint32
rNRGSession::getBlockCount()
960 uint16
rNRGSession::getBlockSize()
965 const char *rNRGSession::static_getName()
967 return "Nero Burning ROM Image";
970 bool rNRGSession::static_isAudio()
975 bool rNRGSession::static_isData()
980 bool rNRGSession::static_isSession()
985 bool rNRGSession::checkFile(const char* file
, EDtError
&rc
)
992 rc
= DT_UnableToOpenFile
;
994 fh
= DOS
->Open(const_cast<char*>(file
), MODE_OLDFILE
);
1000 DOS
->Seek(fh
, -4, OFFSET_END
);
1001 size
= DOS
->Seek(fh
, 0, OFFSET_CURRENT
) + 4; // fancy, innit
1003 DOS
->Read(fh
, &data
, 4);
1005 if ((size
- data
) > 4096)
1008 DOS
->Seek(fh
, data
, OFFSET_BEGINNING
);
1012 DOS
->Read(fh
, &data
, 4);
1014 if ((data
== MAKE_ID('C','D','T','X')) ||
1015 (data
== MAKE_ID('C','U','E','X')) ||
1016 (data
== MAKE_ID('C','U','E','S')) ||
1017 (data
== MAKE_ID('D','A','O','X')) ||
1018 (data
== MAKE_ID('D','A','O','I')) ||
1019 (data
== MAKE_ID('E','T','N','F')) ||
1020 (data
== MAKE_ID('E','T','N','2')) ||
1021 (data
== MAKE_ID('N','E','R','O')) ||
1022 (data
== MAKE_ID('N','E','R','5')) ||
1023 (data
== MAKE_ID('S','I','N','F')) ||
1024 (data
== MAKE_ID('M','T','Y','P')))
1026 DOS
->Read(fh
, &data
, 4);
1028 DOS
->Seek(fh
, data
, OFFSET_CURRENT
);
1030 else if (data
== MAKE_ID('E','N','D','!'))
1046 DbgHandler
* rNRGSession::getDebug()
1051 void rNRGSession::setDebug(DbgHandler
* nd
)