batch comparing capability
This commit is contained in:
parent
7dacc07a7d
commit
0d029b4030
|
@ -4,6 +4,8 @@ using System.Diagnostics;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.DiscSystem;
|
||||
|
@ -184,6 +186,33 @@ namespace BizHawk.Client.DiscoHawk
|
|||
|
||||
class DiscoHawk
|
||||
{
|
||||
static List<string> FindExtensionsRecurse(string dir, string extUppercaseWithDot)
|
||||
{
|
||||
List<string> ret = new List<string>();
|
||||
Queue<string> dpTodo = new Queue<string>();
|
||||
dpTodo.Enqueue(dir);
|
||||
for (; ; )
|
||||
{
|
||||
string dpCurr;
|
||||
if (dpTodo.Count == 0)
|
||||
break;
|
||||
dpCurr = dpTodo.Dequeue();
|
||||
Parallel.ForEach(new DirectoryInfo(dpCurr).GetFiles(), (fi) =>
|
||||
{
|
||||
if (fi.Extension.ToUpperInvariant() == extUppercaseWithDot)
|
||||
lock(ret)
|
||||
ret.Add(fi.FullName);
|
||||
});
|
||||
Parallel.ForEach(new DirectoryInfo(dpCurr).GetDirectories(), (di) =>
|
||||
{
|
||||
lock(dpTodo)
|
||||
dpTodo.Enqueue(di.FullName);
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Run(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
|
@ -193,6 +222,8 @@ namespace BizHawk.Client.DiscoHawk
|
|||
return;
|
||||
}
|
||||
|
||||
bool scanCues = false;
|
||||
string dirArg = null;
|
||||
string infile = null;
|
||||
var loadDiscInterface = DiscInterface.BizHawk;
|
||||
var compareDiscInterfaces = new List<DiscInterface> ();
|
||||
|
@ -209,192 +240,264 @@ namespace BizHawk.Client.DiscoHawk
|
|||
compareDiscInterfaces.Add((DiscInterface)Enum.Parse(typeof(DiscInterface), args[idx++], true));
|
||||
else if (au == "HAWK")
|
||||
hawk = true;
|
||||
else if (au == "CUEDIR")
|
||||
{
|
||||
dirArg = args[idx++];
|
||||
scanCues = true;
|
||||
}
|
||||
else infile = a;
|
||||
}
|
||||
|
||||
if (infile == null)
|
||||
return;
|
||||
|
||||
|
||||
if (hawk)
|
||||
{
|
||||
if (infile == null)
|
||||
return;
|
||||
|
||||
//TODO - write it out
|
||||
var dmj = new DiscMountJob { IN_DiscInterface = loadDiscInterface, IN_FromPath = infile };
|
||||
dmj.Run();
|
||||
//var disc = dmj.OUT_Disc;
|
||||
}
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
foreach (var cmpif in compareDiscInterfaces)
|
||||
bool verbose = true;
|
||||
|
||||
if (scanCues)
|
||||
{
|
||||
sw.WriteLine("BEGIN COMPARE: SRC {0} vs DST {1}", loadDiscInterface, cmpif);
|
||||
|
||||
//reload the original disc, with new policies as needed
|
||||
var dmj = new DiscMountJob { IN_DiscInterface = loadDiscInterface, IN_FromPath = infile };
|
||||
if (cmpif == DiscInterface.MednaDisc)
|
||||
verbose = false;
|
||||
var todo = FindExtensionsRecurse(dirArg, ".CUE");
|
||||
var po = new ParallelOptions();
|
||||
var cts = new CancellationTokenSource();
|
||||
po.CancellationToken = cts.Token;
|
||||
po.MaxDegreeOfParallelism = 1;
|
||||
//po.MaxDegreeOfParallelism = System.Environment.ProcessorCount - 2; //I'm disk bound, no sense hammering this
|
||||
if(po.MaxDegreeOfParallelism < 0) po.MaxDegreeOfParallelism = 1;
|
||||
object olock = new object();
|
||||
int ctr=0;
|
||||
try
|
||||
{
|
||||
dmj.IN_DiscMountPolicy.CUE_PregapContradictionModeA = false;
|
||||
}
|
||||
dmj.Run();
|
||||
var disc = dmj.OUT_Disc;
|
||||
|
||||
var src_disc = disc;
|
||||
|
||||
var dst_dmj = new DiscMountJob { IN_DiscInterface = cmpif, IN_FromPath = infile };
|
||||
dst_dmj.Run();
|
||||
var dst_disc = dst_dmj.OUT_Disc;
|
||||
|
||||
var src_dsr = new DiscSectorReader(src_disc);
|
||||
var dst_dsr = new DiscSectorReader(dst_disc);
|
||||
|
||||
var src_toc = src_disc.TOCRaw;
|
||||
var dst_toc = dst_disc.TOCRaw;
|
||||
|
||||
var src_databuf = new byte[2448];
|
||||
var dst_databuf = new byte[2448];
|
||||
|
||||
Action<DiscTOCRaw.TOCItem> sw_dump_toc_one = (item) =>
|
||||
Parallel.ForEach(todo, po, (fp) =>
|
||||
{
|
||||
if(!item.Exists)
|
||||
sw.Write("(---missing---)");
|
||||
else
|
||||
sw.Write("({0:X2} - {1})",(byte)item.Control,item.LBATimestamp);
|
||||
};
|
||||
lock (olock)
|
||||
{
|
||||
ctr++;
|
||||
int strlen = todo.Count.ToString().Length;
|
||||
string fmt = string.Format("{{0,{0}}}/{{1,{0}}} {{2}}", strlen);
|
||||
Console.WriteLine(fmt, ctr, todo.Count, Path.GetFileNameWithoutExtension(fp));
|
||||
}
|
||||
|
||||
Action<int> sw_dump_toc = (index) =>
|
||||
{
|
||||
sw.Write("SRC TOC#{0,3} ",index); sw_dump_toc_one(src_toc.TOCItems[index]); sw.WriteLine();
|
||||
sw.Write("DST TOC#{0,3} ",index); sw_dump_toc_one(dst_toc.TOCItems[index]); sw.WriteLine();
|
||||
};
|
||||
foreach (var cmpif in compareDiscInterfaces)
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
bool success = CompareFile(fp, loadDiscInterface, cmpif, verbose, cts, sw);
|
||||
if (!success)
|
||||
{
|
||||
lock (Console.Out)
|
||||
Console.Out.Write(sw.ToString());
|
||||
|
||||
//verify sector count
|
||||
if (src_disc.LBACount != dst_disc.LBACount)
|
||||
{
|
||||
sw.Write("LBACount count {0} vs {1}\n", src_disc.LBACount, dst_disc.LBACount);
|
||||
goto SKIPPO;
|
||||
cts.Cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//verify TOC match
|
||||
if (src_disc.TOCRaw.FirstRecordedTrackNumber != dst_disc.TOCRaw.FirstRecordedTrackNumber
|
||||
|| src_disc.TOCRaw.LastRecordedTrackNumber != dst_disc.TOCRaw.LastRecordedTrackNumber)
|
||||
{
|
||||
sw.WriteLine("Mismatch of RecordedTrackNumbers: {0}-{1} vs {2}-{3}",
|
||||
src_disc.TOCRaw.FirstRecordedTrackNumber, src_disc.TOCRaw.LastRecordedTrackNumber,
|
||||
dst_disc.TOCRaw.FirstRecordedTrackNumber, dst_disc.TOCRaw.LastRecordedTrackNumber
|
||||
);
|
||||
goto SKIPPO;
|
||||
catch (AggregateException ae) {
|
||||
Console.WriteLine(ae.ToString());
|
||||
}
|
||||
Console.WriteLine("--TERMINATED--");
|
||||
return;
|
||||
}
|
||||
|
||||
bool badToc = false;
|
||||
for (int t = 0; t < 101; t++)
|
||||
if (compareDiscInterfaces.Count != 0)
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
foreach (var cmpif in compareDiscInterfaces)
|
||||
CompareFile(infile, loadDiscInterface, cmpif, verbose, null, sw);
|
||||
sw.Flush();
|
||||
string results = sw.ToString();
|
||||
var cr = new ComparisonResults();
|
||||
cr.textBox1.Text = results;
|
||||
cr.ShowDialog();
|
||||
}
|
||||
|
||||
|
||||
} //Run()
|
||||
|
||||
static bool CompareFile(string infile, DiscInterface loadDiscInterface, DiscInterface cmpif, bool verbose, CancellationTokenSource cancelToken, StringWriter sw)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
sw.WriteLine("BEGIN COMPARE: {0}\nSRC {1} vs DST {2}", infile, loadDiscInterface, cmpif);
|
||||
|
||||
//reload the original disc, with new policies as needed
|
||||
var dmj = new DiscMountJob { IN_DiscInterface = loadDiscInterface, IN_FromPath = infile };
|
||||
if (cmpif == DiscInterface.MednaDisc)
|
||||
{
|
||||
dmj.IN_DiscMountPolicy.CUE_PregapContradictionModeA = false;
|
||||
}
|
||||
dmj.Run();
|
||||
var disc = dmj.OUT_Disc;
|
||||
|
||||
var src_disc = disc;
|
||||
|
||||
var dst_dmj = new DiscMountJob { IN_DiscInterface = cmpif, IN_FromPath = infile };
|
||||
dst_dmj.Run();
|
||||
var dst_disc = dst_dmj.OUT_Disc;
|
||||
|
||||
var src_dsr = new DiscSectorReader(src_disc);
|
||||
var dst_dsr = new DiscSectorReader(dst_disc);
|
||||
|
||||
var src_toc = src_disc.TOCRaw;
|
||||
var dst_toc = dst_disc.TOCRaw;
|
||||
|
||||
var src_databuf = new byte[2448];
|
||||
var dst_databuf = new byte[2448];
|
||||
|
||||
Action<DiscTOCRaw.TOCItem> sw_dump_toc_one = (item) =>
|
||||
{
|
||||
if (!item.Exists)
|
||||
sw.Write("(---missing---)");
|
||||
else
|
||||
sw.Write("({0:X2} - {1})", (byte)item.Control, item.LBATimestamp);
|
||||
};
|
||||
|
||||
|
||||
Action<int> sw_dump_toc = (index) =>
|
||||
{
|
||||
sw.Write("SRC TOC#{0,3} ", index); sw_dump_toc_one(src_toc.TOCItems[index]); sw.WriteLine();
|
||||
sw.Write("DST TOC#{0,3} ", index); sw_dump_toc_one(dst_toc.TOCItems[index]); sw.WriteLine();
|
||||
};
|
||||
|
||||
//verify sector count
|
||||
if (src_disc.LBACount != dst_disc.LBACount)
|
||||
{
|
||||
sw.Write("LBACount count {0} vs {1}\n", src_disc.LBACount, dst_disc.LBACount);
|
||||
goto SKIPPO;
|
||||
}
|
||||
|
||||
//verify TOC match
|
||||
if (src_disc.TOCRaw.FirstRecordedTrackNumber != dst_disc.TOCRaw.FirstRecordedTrackNumber
|
||||
|| src_disc.TOCRaw.LastRecordedTrackNumber != dst_disc.TOCRaw.LastRecordedTrackNumber)
|
||||
{
|
||||
sw.WriteLine("Mismatch of RecordedTrackNumbers: {0}-{1} vs {2}-{3}",
|
||||
src_disc.TOCRaw.FirstRecordedTrackNumber, src_disc.TOCRaw.LastRecordedTrackNumber,
|
||||
dst_disc.TOCRaw.FirstRecordedTrackNumber, dst_disc.TOCRaw.LastRecordedTrackNumber
|
||||
);
|
||||
goto SKIPPO;
|
||||
}
|
||||
|
||||
bool badToc = false;
|
||||
for (int t = 0; t < 101; t++)
|
||||
{
|
||||
if (src_toc.TOCItems[t].Exists != dst_toc.TOCItems[t].Exists
|
||||
|| src_toc.TOCItems[t].Control != dst_toc.TOCItems[t].Control
|
||||
|| src_toc.TOCItems[t].LBATimestamp.Sector != dst_toc.TOCItems[t].LBATimestamp.Sector
|
||||
)
|
||||
{
|
||||
if (src_toc.TOCItems[t].Exists != dst_toc.TOCItems[t].Exists
|
||||
|| src_toc.TOCItems[t].Control != dst_toc.TOCItems[t].Control
|
||||
|| src_toc.TOCItems[t].LBATimestamp.Sector != dst_toc.TOCItems[t].LBATimestamp.Sector
|
||||
)
|
||||
{
|
||||
sw.WriteLine("Mismatch in TOCItem");
|
||||
sw_dump_toc(t);
|
||||
badToc = true;
|
||||
}
|
||||
sw.WriteLine("Mismatch in TOCItem");
|
||||
sw_dump_toc(t);
|
||||
badToc = true;
|
||||
}
|
||||
if(badToc)
|
||||
goto SKIPPO;
|
||||
}
|
||||
if (badToc)
|
||||
goto SKIPPO;
|
||||
|
||||
Action<string, int, byte[], int, int> sw_dump_chunk_one = (comment, lba, buf, addr, count) =>
|
||||
Action<string, int, byte[], int, int> sw_dump_chunk_one = (comment, lba, buf, addr, count) =>
|
||||
{
|
||||
sw.Write("{0} - ", comment);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
sw.Write("{0} - ", comment);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (i + addr >= buf.Length) continue;
|
||||
sw.Write("{0:X2}{1}", buf[addr + i], (i == count - 1) ? " " : " ");
|
||||
}
|
||||
sw.WriteLine();
|
||||
};
|
||||
if (i + addr >= buf.Length) continue;
|
||||
sw.Write("{0:X2}{1}", buf[addr + i], (i == count - 1) ? " " : " ");
|
||||
}
|
||||
sw.WriteLine();
|
||||
};
|
||||
|
||||
int[] offenders = new int[12];
|
||||
Action<int, int, int, int, int> sw_dump_chunk = (lba, dispaddr, addr, count, numoffenders) =>
|
||||
{
|
||||
var hashedOffenders = new HashSet<int>();
|
||||
for (int i = 0; i < numoffenders; i++) hashedOffenders.Add(offenders[i]);
|
||||
sw.Write(" ");
|
||||
for (int i = 0; i < count; i++) sw.Write((hashedOffenders.Contains(dispaddr + i)) ? "vvv " : " ");
|
||||
sw.WriteLine();
|
||||
sw.Write(" ");
|
||||
for (int i = 0; i < count; i++) sw.Write("{0:X3} ", dispaddr + i, (i == count - 1) ? " " : " ");
|
||||
sw.WriteLine();
|
||||
sw.Write(" ");
|
||||
sw.Write(new string('-', count * 4));
|
||||
sw.WriteLine();
|
||||
sw_dump_chunk_one(string.Format("SRC #{0,6} ({1})", lba, new Timestamp(lba)), lba, src_databuf, addr, count);
|
||||
sw_dump_chunk_one(string.Format("DST #{0,6} ({1})", lba, new Timestamp(lba)), lba, dst_databuf, addr, count);
|
||||
};
|
||||
int[] offenders = new int[12];
|
||||
Action<int, int, int, int, int> sw_dump_chunk = (lba, dispaddr, addr, count, numoffenders) =>
|
||||
{
|
||||
var hashedOffenders = new HashSet<int>();
|
||||
for (int i = 0; i < numoffenders; i++) hashedOffenders.Add(offenders[i]);
|
||||
sw.Write(" ");
|
||||
for (int i = 0; i < count; i++) sw.Write((hashedOffenders.Contains(dispaddr + i)) ? "vvv " : " ");
|
||||
sw.WriteLine();
|
||||
sw.Write(" ");
|
||||
for (int i = 0; i < count; i++) sw.Write("{0:X3} ", dispaddr + i, (i == count - 1) ? " " : " ");
|
||||
sw.WriteLine();
|
||||
sw.Write(" ");
|
||||
sw.Write(new string('-', count * 4));
|
||||
sw.WriteLine();
|
||||
sw_dump_chunk_one(string.Format("SRC #{0,6} ({1})", lba, new Timestamp(lba)), lba, src_databuf, addr, count);
|
||||
sw_dump_chunk_one(string.Format("DST #{0,6} ({1})", lba, new Timestamp(lba)), lba, dst_databuf, addr, count);
|
||||
};
|
||||
|
||||
//verify each sector contents
|
||||
int nSectors = src_disc.LBACount;
|
||||
for (int lba = -150; lba < nSectors; lba++)
|
||||
{
|
||||
//verify each sector contents
|
||||
int nSectors = src_disc.LBACount;
|
||||
for (int lba = -150; lba < nSectors; lba++)
|
||||
{
|
||||
if(verbose)
|
||||
if (lba % 1000 == 0)
|
||||
Console.WriteLine("LBA {0} of {1}", lba, src_disc.LBACount);
|
||||
|
||||
src_dsr.ReadLBA_2442(lba, src_databuf, 0);
|
||||
dst_dsr.ReadLBA_2442(lba, dst_databuf, 0);
|
||||
if(cancelToken != null)
|
||||
if (cancelToken.Token.IsCancellationRequested)
|
||||
return false;
|
||||
|
||||
//check the header
|
||||
for (int b = 0; b < 16; b++)
|
||||
src_dsr.ReadLBA_2442(lba, src_databuf, 0);
|
||||
dst_dsr.ReadLBA_2442(lba, dst_databuf, 0);
|
||||
|
||||
//check the header
|
||||
for (int b = 0; b < 16; b++)
|
||||
{
|
||||
if (src_databuf[b] != dst_databuf[b])
|
||||
{
|
||||
if (src_databuf[b] != dst_databuf[b])
|
||||
{
|
||||
sw.WriteLine("Mismatch in sector header at byte {0}",b);
|
||||
offenders[0] = b;
|
||||
sw_dump_chunk(lba, 0, 0, 16, 1);
|
||||
goto SKIPPO;
|
||||
}
|
||||
sw.WriteLine("Mismatch in sector header at byte {0}", b);
|
||||
offenders[0] = b;
|
||||
sw_dump_chunk(lba, 0, 0, 16, 1);
|
||||
goto SKIPPO;
|
||||
}
|
||||
|
||||
//check userdata
|
||||
for (int b = 16; b < 2352; b++)
|
||||
{
|
||||
if (src_databuf[b] != dst_databuf[b])
|
||||
{
|
||||
sw.Write("LBA {0} mismatch at userdata byte {1}; terminating sector cmp\n", lba, b);
|
||||
goto SKIPPO;
|
||||
}
|
||||
}
|
||||
|
||||
//check subchannels
|
||||
for (int c = 0, b=2352; c < 8; c++)
|
||||
{
|
||||
int numOffenders = 0;
|
||||
for (int e = 0; e < 12; e++, b++)
|
||||
{
|
||||
if (src_databuf[b] != dst_databuf[b])
|
||||
{
|
||||
offenders[numOffenders++] = e;
|
||||
}
|
||||
}
|
||||
if (numOffenders != 0)
|
||||
{
|
||||
sw.Write("LBA {0} mismatch(es) at subchannel {1}; terminating sector cmp\n", lba, (char)('P' + c));
|
||||
sw_dump_chunk(lba, 0, 2352 + c * 12, 12, numOffenders);
|
||||
goto SKIPPO;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SKIPPO:
|
||||
sw.WriteLine("END COMPARE");
|
||||
sw.WriteLine("-----------------------------");
|
||||
//check userdata
|
||||
for (int b = 16; b < 2352; b++)
|
||||
{
|
||||
if (src_databuf[b] != dst_databuf[b])
|
||||
{
|
||||
sw.Write("LBA {0} mismatch at userdata byte {1}; terminating sector cmp\n", lba, b);
|
||||
goto SKIPPO;
|
||||
}
|
||||
}
|
||||
|
||||
//check subchannels
|
||||
for (int c = 0, b = 2352; c < 8; c++)
|
||||
{
|
||||
int numOffenders = 0;
|
||||
for (int e = 0; e < 12; e++, b++)
|
||||
{
|
||||
if (src_databuf[b] != dst_databuf[b])
|
||||
{
|
||||
offenders[numOffenders++] = e;
|
||||
}
|
||||
}
|
||||
if (numOffenders != 0)
|
||||
{
|
||||
sw.Write("LBA {0} mismatch(es) at subchannel {1}; terminating sector cmp\n", lba, (char)('P' + c));
|
||||
sw_dump_chunk(lba, 0, 2352 + c * 12, 12, numOffenders);
|
||||
goto SKIPPO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sw.Flush();
|
||||
var cr = new ComparisonResults();
|
||||
string results = sw.ToString();
|
||||
cr.textBox1.Text = results;
|
||||
cr.ShowDialog();
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
|
||||
SKIPPO:
|
||||
sw.WriteLine("END COMPARE");
|
||||
sw.WriteLine("-----------------------------");
|
||||
|
||||
return success;
|
||||
|
||||
} //CompareFile
|
||||
|
||||
} //class DiscoHawk
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue