2011-04-01 21:34:11 +00:00
using System ;
using System.Collections.Generic ;
using System.Text ;
using HelperFunctionsLib ;
using System.Runtime.InteropServices ;
using System.Reflection ;
using System.Net ;
using System.IO ;
using CUETools.CDImage ;
using CUETools.AccurateRip ;
using CUETools.Codecs ;
using CUETools.CTDB ;
namespace AudioDataPlugIn
{
[ Guid ( "C02A1BF2-5C46-4990-80C2-78E8C395CB80" ) ,
ClassInterface ( ClassInterfaceType . None ) ,
ComSourceInterfaces ( typeof ( IAudioDataTransfer ) ) ,
]
// Our class needs to inherit the IAudioDataTransfer in
// order to be found by EAC, further the class needs
// to be named AudioDataTransfer and must be in the
// namespace AudioDataPlugIn
public class AudioDataTransfer : IAudioDataTransfer
{
int m_start_pos = 0 , m_length = 0 ;
bool m_test_mode = false ;
IMetadataLookup m_data = null ;
CDImageLayout TOC ;
string ArId ;
AccurateRipVerify ar ;
2011-04-01 22:40:22 +00:00
AccurateRipVerify arTest ;
2011-04-01 21:34:11 +00:00
CUEToolsDB ctdb ;
bool sequence_ok = true ;
2011-04-07 22:28:06 +00:00
bool is_secure_mode ;
bool is_offset_set ;
2011-04-01 21:34:11 +00:00
string m_drivename ;
// This functions just returns an unique identifier.
// For that, the string representation of the unique
// guid is used
public string GetAudioTransferPluginGuid ( )
{
// Determine the guid attribute of the given class
Attribute attrib = Attribute . GetCustomAttribute ( typeof ( AudioDataTransfer ) , typeof ( GuidAttribute ) ) ;
// Is the returned attribute a GuidAttribute as
// we asked for?
if ( attrib is GuidAttribute )
{
// Yes, so return the guid of this class
return ( ( GuidAttribute ) attrib ) . Value ;
}
else
{
// No, something went wrong. Just return
// the name of the plugin and hope that it
// is unique
return GetAudioTransferPluginName ( ) ;
}
}
// Each plugin has also an (unique) display name
// which will be used for selecting/deselecting
// the plugin and for display in the log file
public string GetAudioTransferPluginName ( )
{
// Return the name which should be
// displayed in EAC and in the log file
// including a version number
return "CUETools DB Plugin V2.1.1" ;
}
// Each plugin should have its own options page.
// Even though if there are no options, a help or
// information dialog should be displayed
public void ShowOptions ( )
{
// Create a new option dialog object
Options opt = new Options ( ) ;
// And show that dialog (return when
// the dialog is closed)
opt . ShowDialog ( ) ;
}
// Now to the audio transfer functions, the sequence how
// the functions are called is:
// StartNewSession
// StartNewTransfer
// TransferAudio
// ...
// TransferAudio
// TransferFinshed
// Then perhaps repeating StartNewTransfer to TransferFinished
// (e.g. when extracting several tracks), and finally
// EndOfSession
// This is called just before the log window will be
// shown. You can return a log output in that stage (or
// even display a window of your own - even though it should
// not annoy the user)
// StartNewSession is called once at the very beginning of an
// extraction session. It receives the CD metadata, the
// name of the used drive, the used read offset and whether
// the offset was setted by AccurateRip (so having a comparable
// offset value)
2011-04-07 22:28:06 +00:00
public void StartNewSession ( IMetadataLookup data , string drivename , int offset , bool aroffset , int mode )
2011-04-01 21:34:11 +00:00
{
// Copy the CD metadata to the object
m_data = data ;
2011-04-07 22:28:06 +00:00
var parts = drivename . Split ( ' ' ) ;
m_drivename = parts [ 0 ] . PadRight ( 8 , ' ' ) + " -" ;
for ( int i = 1 ; i < parts . Length ; i + + )
m_drivename + = " " + parts [ i ] ;
2011-04-01 21:34:11 +00:00
TOC = new CDImageLayout ( ) ;
for ( int i = 0 ; i < m_data . NumberOfTracks ; i + + )
{
uint start = m_data . GetTrackStartPosition ( i ) ;
2011-04-07 22:28:06 +00:00
uint next = m_data . GetTrackEndPosition ( i ) ;
2011-04-01 21:34:11 +00:00
TOC . AddTrack ( new CDTrack (
( uint ) i + 1 ,
start ,
next - start ,
! m_data . GetTrackDataTrack ( i ) ,
m_data . GetTrackPreemphasis ( i ) ) ) ;
}
TOC [ 1 ] [ 0 ] . Start = 0 U ;
ArId = AccurateRipVerify . CalculateAccurateRipId ( TOC ) ;
ar = new AccurateRipVerify ( TOC , null ) ;
2011-04-01 22:40:22 +00:00
arTest = new AccurateRipVerify ( TOC , null ) ;
2011-04-01 21:34:11 +00:00
ctdb = new CUEToolsDB ( TOC , null ) ;
2011-04-07 22:28:06 +00:00
#if USEAR
2011-04-01 21:34:11 +00:00
ar . ContactAccurateRip ( ArId ) ;
2011-04-07 22:28:06 +00:00
#endif
ctdb . ContactDB ( "EAC" + m_data . HostVersion + " CTDB 2.1.1: " + m_drivename ) ;
2011-04-01 21:34:11 +00:00
ctdb . Init ( true , ar ) ;
this . sequence_ok = true ;
this . m_start_pos = 0 ;
this . m_length = 0 ;
this . m_test_mode = false ;
2011-04-07 22:28:06 +00:00
this . is_offset_set = aroffset ;
this . is_secure_mode = mode > = 2 ;
2011-04-01 21:34:11 +00:00
}
// This function will be called once per session. A session
// is e.g. a file on a real extraction (or the equivalent
// for test extractions). It receives the sector startpos
// the length in sectors and whether the extraction is performed
// in test mode
public void StartNewTransfer ( int startpos , int length , bool testmode )
{
// Copy the current parameters to the objects variables
m_start_pos = startpos - ( int ) TOC [ TOC . FirstAudio ] [ 0 ] . Start ;
m_length = length ;
m_test_mode = testmode ;
if ( this . sequence_ok )
{
2011-04-01 22:40:22 +00:00
var thisAr = m_test_mode ? arTest : ar ;
if ( this . m_start_pos * 588 ! = thisAr . Position )
2011-04-01 21:34:11 +00:00
this . sequence_ok = false ;
}
}
// This function received the extracted (and
// uncompressed/unmodified audio data), but no WAV
// header. If you want to write out the WAV file
// you need to generate one yourself. It will be always
// 44.1 kHz, stereo 16 bit samples (so 4 bytes per
// stereo sample)
public void TransferAudioData ( Array audiodata )
{
2011-04-01 22:40:22 +00:00
if ( this . sequence_ok )
2011-04-01 21:34:11 +00:00
{
byte [ ] ad = ( byte [ ] ) audiodata ;
AudioBuffer buff = new AudioBuffer ( AudioPCMConfig . RedBook , ad , ad . Length / 4 ) ;
2011-04-01 22:40:22 +00:00
var thisAr = m_test_mode ? arTest : ar ;
thisAr . Write ( buff ) ;
2011-04-01 21:34:11 +00:00
}
}
// This function is called after a transfer has finished.
// We don't do here anything, because a track can be delivered
// in several transfers (index extraction) and as AccurateRip
// is only (full) track based, we don't do anything...
public void TransferFinished ( )
{
if ( this . sequence_ok )
{
2011-04-01 22:40:22 +00:00
var thisAr = m_test_mode ? arTest : ar ;
if ( ( m_start_pos + m_length ) * 588 ! = thisAr . Position )
2011-04-01 21:34:11 +00:00
this . sequence_ok = false ;
}
}
// The extraction has finished, the log dialog will
// be shown soon, so we can return a string which will
// be displayed in the log window and be written to
// the log file. Anyway, you could also return just
// an empty string, in that case no log output will be done!
public string EndOfSession ( )
{
StringWriter sw = new StringWriter ( ) ;
if ( this . sequence_ok )
{
if ( TOC . AudioLength * 588 ! = ar . Position )
this . sequence_ok = false ;
2011-04-01 22:40:22 +00:00
if ( ar . Position ! = arTest . Position & & arTest . Position > 0 )
this . sequence_ok = false ;
2011-04-01 21:34:11 +00:00
}
if ( ! this . sequence_ok )
return "" ;
if ( this . sequence_ok )
{
2011-04-07 22:28:06 +00:00
bool is_accurate = ( arTest . Position = = 0 & & this . is_secure_mode ) | | arTest . CRC32 ( 0 ) = = ar . CRC32 ( 0 ) ;
2011-04-01 21:34:11 +00:00
DBEntry confirm = null ;
2011-04-01 22:40:22 +00:00
if ( ctdb . AccResult = = HttpStatusCode . OK )
ctdb . DoVerify ( ) ;
2011-04-01 21:34:11 +00:00
if ( ( ctdb . AccResult = = HttpStatusCode . NotFound | | ctdb . AccResult = = HttpStatusCode . OK )
2011-04-07 22:28:06 +00:00
& & is_accurate )
2011-04-01 21:34:11 +00:00
{
foreach ( DBEntry entry in ctdb . Entries )
if ( entry . toc . TrackOffsets = = TOC . TrackOffsets & & ! entry . hasErrors )
confirm = entry ;
if ( confirm ! = null )
ctdb . Confirm ( confirm ) ;
else
ctdb . Submit (
2011-04-07 22:28:06 +00:00
#if USEAR
2011-04-01 21:34:11 +00:00
( int ) ar . WorstConfidence ( ) + 1 ,
2011-04-07 22:28:06 +00:00
#else
2011-04-24 21:07:50 +00:00
1 ,
2011-04-07 22:28:06 +00:00
#endif
2011-04-01 21:34:11 +00:00
m_data . AlbumArtist ,
m_data . AlbumTitle ) ;
}
2011-04-01 22:40:22 +00:00
sw . WriteLine ( "[CTDB TOCID: {0}] {1}{2}." ,
TOC . TOCID ,
ctdb . DBStatus ? ? "found" ,
( confirm ! = null | | ctdb . SubStatus = = null ) ? "" : ( ", Submit result: " + ctdb . SubStatus ) ) ;
2011-04-01 21:34:11 +00:00
foreach ( DBEntry entry in ctdb . Entries )
{
string confFormat = ( ctdb . Total < 10 ) ? "{0:0}/{1:0}" :
( ctdb . Total < 100 ) ? "{0:00}/{1:00}" : "{0:000}/{1:000}" ;
string conf = string . Format ( confFormat , entry . conf , ctdb . Total ) ;
string dataTrackInfo = ! entry . toc [ entry . toc . TrackCount ] . IsAudio ? string . Format ( "CD-Extra data track length {0}" , entry . toc [ entry . toc . TrackCount ] . LengthMSF ) :
! entry . toc [ 1 ] . IsAudio ? string . Format ( "Playstation type data track length {0}" , entry . toc [ 1 ] . LengthMSF ) : "Has no data track" ;
string status =
entry . toc . Pregap ! = TOC . Pregap ? string . Format ( "Has pregap length {0}" , CDImageLayout . TimeToString ( entry . toc . Pregap ) ) :
entry . toc . AudioLength ! = TOC . AudioLength ? string . Format ( "Has audio length {0}" , CDImageLayout . TimeToString ( entry . toc . AudioLength ) ) :
( ( entry . toc . TrackOffsets ! = TOC . TrackOffsets ) ? dataTrackInfo + ", " : "" ) +
( ( ! entry . hasErrors ) ? "Accurately ripped" :
//((!entry.hasErrors) ? string.Format("Accurately ripped, offset {0}", -entry.offset) :
entry . canRecover ? string . Format ( "Differs in {0} samples @{1}" , entry . repair . CorrectableErrors , entry . repair . AffectedSectors ) :
( entry . httpStatus = = 0 | | entry . httpStatus = = HttpStatusCode . OK ) ? "No match" :
entry . httpStatus . ToString ( ) ) ;
2011-04-01 22:40:22 +00:00
sw . WriteLine ( "[{0:x8}] ({1}) {2}{3}" ,
entry . crc ,
conf ,
status ,
( confirm ! = entry | | ctdb . SubStatus = = null ) ? "" : ( ", Submit result: " + ctdb . SubStatus ) ) ;
2011-04-01 21:34:11 +00:00
}
bool canFix = false ;
2011-04-07 22:28:06 +00:00
if ( ctdb . AccResult = = HttpStatusCode . OK & & ! is_accurate )
2011-04-01 21:34:11 +00:00
{
foreach ( DBEntry entry in ctdb . Entries )
if ( entry . hasErrors & & entry . canRecover )
canFix = true ;
}
if ( canFix )
sw . WriteLine ( "You can use CUETools to repair this rip." ) ;
2011-04-07 22:28:06 +00:00
#if USEAR
ar . GenerateFullLog ( sw , false , ArId ) ;
#endif
2011-04-01 21:34:11 +00:00
}
else
sw . WriteLine ( "Some tracks have been skipped" ) ;
return sw . ToString ( ) ;
}
}
}