Files
cuetools.net/CUETools.CTDB.EACPlugin/Plugin.cs

267 lines
9.2 KiB
C#
Raw Normal View History

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;
using CUETools.CTDB.EACPlugin;
2011-04-01 21:34:11 +00:00
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;
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()
{
2011-05-15 17:41:14 +00:00
return ((GuidAttribute)Attribute.GetCustomAttribute(GetType(), typeof(GuidAttribute))).Value;
2011-04-01 21:34:11 +00:00
}
// 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 "CUETools DB Plugin V2.1.2";
2011-04-01 21:34:11 +00:00
}
// 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()
{
Options opt = new Options();
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 = 0U;
ArId = AccurateRipVerify.CalculateAccurateRipId(TOC);
ar = new AccurateRipVerify(TOC, null);
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
var form = new FormSubmitParity(ctdb, "EAC" + m_data.HostVersion + " CTDB 2.1.2", m_drivename);
//ctdb.ContactDB("EAC" + m_data.HostVersion + " CTDB 2.1.2", m_drivename, false, false);
form.ShowDialog();
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)
{
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)
{
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);
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)
{
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;
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)
{
var form = new FormSubmitParity(
ctdb,
2011-04-07 22:28:06 +00:00
#if USEAR
(int)ar.WorstConfidence() + 1,
2011-04-07 22:28:06 +00:00
#else
1,
2011-04-07 22:28:06 +00:00
#endif
(arTest.Position == 0 && this.is_secure_mode) || arTest.CRC32(0) == ar.CRC32(0) ? 100 : 0,
m_data.AlbumArtist,
m_data.AlbumTitle);
form.ShowDialog();
sw.WriteLine("[CTDB TOCID: {0}] {1}{2}.",
TOC.TOCID,
ctdb.DBStatus ?? "found",
(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());
sw.WriteLine("[{0:x8}] ({1}) {2}",
entry.crc,
conf,
status);
2011-04-01 21:34:11 +00:00
}
bool canFix = false;
if (ctdb.AccResult == HttpStatusCode.OK)
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();
}
}
}