diff --git a/BizHawk.Client.DiscoHawk/DiscoHawk.cs b/BizHawk.Client.DiscoHawk/DiscoHawk.cs index 884bc461a4..a68d8c2f53 100644 --- a/BizHawk.Client.DiscoHawk/DiscoHawk.cs +++ b/BizHawk.Client.DiscoHawk/DiscoHawk.cs @@ -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 FindExtensionsRecurse(string dir, string extUppercaseWithDot) + { + List ret = new List(); + Queue dpTodo = new Queue(); + 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 (); @@ -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 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 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 sw_dump_toc_one = (item) => + { + if (!item.Exists) + sw.Write("(---missing---)"); + else + sw.Write("({0:X2} - {1})", (byte)item.Control, item.LBATimestamp); + }; + + + Action 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 sw_dump_chunk_one = (comment, lba, buf, addr, count) => + Action 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 sw_dump_chunk = (lba, dispaddr, addr, count, numoffenders) => - { - var hashedOffenders = new HashSet(); - 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 sw_dump_chunk = (lba, dispaddr, addr, count, numoffenders) => + { + var hashedOffenders = new HashSet(); + 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 }