Move some DiscoHawk logic to Emulation.DiscSystem
This commit is contained in:
parent
1a21765be6
commit
f431faf65c
|
@ -2,8 +2,6 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
|
@ -11,13 +9,6 @@ using BizHawk.Emulation.DiscSystem;
|
|||
|
||||
using OSTC = EXE_PROJECT.OSTailoredCode;
|
||||
|
||||
// cue format preferences notes
|
||||
|
||||
// PCEjin -
|
||||
// does not like session commands
|
||||
// it can handle binpercue
|
||||
// it seems not to be able to handle binpertrack, or maybe i am doing something wrong (still haven't ruled it out)
|
||||
|
||||
namespace BizHawk.Client.DiscoHawk
|
||||
{
|
||||
internal static class Program
|
||||
|
@ -87,7 +78,22 @@ namespace BizHawk.Client.DiscoHawk
|
|||
ffmpegPath = Path.Combine(Path.Combine(GetExeDirectoryAbsolute(), "dll"), "ffmpeg.exe");
|
||||
FFmpegService.FFmpegPath = ffmpegPath;
|
||||
AudioExtractor.FFmpegPath = ffmpegPath;
|
||||
new DiscoHawk().Run(args);
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
using var dialog = new MainDiscoForm();
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
else
|
||||
{
|
||||
DiscoHawkLogic.RunWithArgs(
|
||||
args,
|
||||
results =>
|
||||
{
|
||||
using var cr = new ComparisonResults { textBox1 = { Text = results } };
|
||||
cr.ShowDialog();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -149,446 +155,4 @@ namespace BizHawk.Client.DiscoHawk
|
|||
public MessageFilterInfo info;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DiscoHawk
|
||||
{
|
||||
private static List<string> FindCuesRecurse(string dir)
|
||||
{
|
||||
var ret = new List<string>();
|
||||
var dpTodo = new Queue<string>();
|
||||
dpTodo.Enqueue(dir);
|
||||
for (; ; )
|
||||
{
|
||||
if (dpTodo.Count == 0)
|
||||
break;
|
||||
var dpCurr = dpTodo.Dequeue();
|
||||
foreach(var fi in new DirectoryInfo(dpCurr).GetFiles("*.cue"))
|
||||
{
|
||||
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)
|
||||
{
|
||||
var dialog = new MainDiscoForm();
|
||||
dialog.ShowDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
bool scanCues = false;
|
||||
string dirArg = null;
|
||||
string infile = null;
|
||||
var loadDiscInterface = DiscInterface.BizHawk;
|
||||
var compareDiscInterfaces = new List<DiscInterface>();
|
||||
bool hawk = false;
|
||||
|
||||
int idx = 0;
|
||||
while (idx < args.Length)
|
||||
{
|
||||
string a = args[idx++];
|
||||
string au = a.ToUpperInvariant();
|
||||
if (au == "LOAD")
|
||||
loadDiscInterface = (DiscInterface)Enum.Parse(typeof(DiscInterface), args[idx++], true);
|
||||
else if (au == "COMPARE")
|
||||
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 (hawk)
|
||||
{
|
||||
if (infile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO - write it out
|
||||
var dmj = new DiscMountJob { IN_DiscInterface = loadDiscInterface, IN_FromPath = infile };
|
||||
dmj.Run();
|
||||
}
|
||||
|
||||
bool verbose = true;
|
||||
|
||||
if (scanCues)
|
||||
{
|
||||
verbose = false;
|
||||
var todo = FindCuesRecurse(dirArg);
|
||||
var po = new ParallelOptions();
|
||||
var cts = new CancellationTokenSource();
|
||||
po.CancellationToken = cts.Token;
|
||||
po.MaxDegreeOfParallelism = 1;
|
||||
if(po.MaxDegreeOfParallelism < 0) po.MaxDegreeOfParallelism = 1;
|
||||
object olock = new object();
|
||||
int ctr=0;
|
||||
bool blocked = false;
|
||||
try
|
||||
{
|
||||
Parallel.ForEach(todo, po, (fp) =>
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
if(!blocked)
|
||||
foreach (var cmpif in compareDiscInterfaces)
|
||||
{
|
||||
var sw = new StringWriter();
|
||||
bool success = CompareFile(fp, loadDiscInterface, cmpif, verbose, cts, sw);
|
||||
if (!success)
|
||||
{
|
||||
lock (Console.Out)
|
||||
Console.Out.Write(sw.ToString());
|
||||
|
||||
cts.Cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (AggregateException ae) {
|
||||
Console.WriteLine(ae.ToString());
|
||||
}
|
||||
catch (OperationCanceledException oce)
|
||||
{
|
||||
Console.WriteLine(oce.ToString());
|
||||
}
|
||||
Console.WriteLine("--TERMINATED--");
|
||||
return;
|
||||
}
|
||||
|
||||
if (compareDiscInterfaces.Count != 0)
|
||||
{
|
||||
var 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 { textBox1 = { Text = results } };
|
||||
cr.ShowDialog();
|
||||
}
|
||||
|
||||
|
||||
} //Run()
|
||||
|
||||
private static bool CompareFile(string infile, DiscInterface loadDiscInterface, DiscInterface cmpif, bool verbose, CancellationTokenSource cancelToken, StringWriter sw)
|
||||
{
|
||||
Disc srcDisc = null, dstDisc = null;
|
||||
|
||||
try
|
||||
{
|
||||
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_DiscMountPolicy = new DiscMountPolicy { CUE_PregapContradictionModeA = cmpif != DiscInterface.MednaDisc },
|
||||
IN_FromPath = infile
|
||||
};
|
||||
|
||||
dmj.Run();
|
||||
|
||||
srcDisc = dmj.OUT_Disc;
|
||||
|
||||
var dstDmj = new DiscMountJob { IN_DiscInterface = cmpif, IN_FromPath = infile };
|
||||
dstDmj.Run();
|
||||
dstDisc = dstDmj.OUT_Disc;
|
||||
|
||||
var srcDsr = new DiscSectorReader(srcDisc);
|
||||
var dstDsr = new DiscSectorReader(dstDisc);
|
||||
|
||||
var srcToc = srcDisc.TOC;
|
||||
var dstToc = dstDisc.TOC;
|
||||
|
||||
var srcDataBuf = new byte[2448];
|
||||
var dstDataBuf = new byte[2448];
|
||||
|
||||
void SwDumpTocOne(DiscTOC.TOCItem item)
|
||||
{
|
||||
if (!item.Exists)
|
||||
{
|
||||
sw.Write("(---missing---)");
|
||||
}
|
||||
else
|
||||
{
|
||||
sw.Write("({0:X2} - {1})", (byte) item.Control, item.LBA);
|
||||
}
|
||||
}
|
||||
|
||||
void SwDumpToc(int index)
|
||||
{
|
||||
sw.Write("SRC TOC#{0,3} ", index);
|
||||
SwDumpTocOne(srcToc.TOCItems[index]);
|
||||
sw.WriteLine();
|
||||
sw.Write("DST TOC#{0,3} ", index);
|
||||
SwDumpTocOne(dstToc.TOCItems[index]);
|
||||
sw.WriteLine();
|
||||
}
|
||||
|
||||
//verify sector count
|
||||
if (srcDisc.Session1.LeadoutLBA != dstDisc.Session1.LeadoutLBA)
|
||||
{
|
||||
sw.Write("LeadoutTrack.LBA {0} vs {1}\n", srcDisc.Session1.LeadoutTrack.LBA, dstDisc.Session1.LeadoutTrack.LBA);
|
||||
goto SKIPPO;
|
||||
}
|
||||
|
||||
//verify TOC match
|
||||
if (srcDisc.TOC.FirstRecordedTrackNumber != dstDisc.TOC.FirstRecordedTrackNumber
|
||||
|| srcDisc.TOC.LastRecordedTrackNumber != dstDisc.TOC.LastRecordedTrackNumber)
|
||||
{
|
||||
sw.WriteLine("Mismatch of RecordedTrackNumbers: {0}-{1} vs {2}-{3}",
|
||||
srcDisc.TOC.FirstRecordedTrackNumber, srcDisc.TOC.LastRecordedTrackNumber,
|
||||
dstDisc.TOC.FirstRecordedTrackNumber, dstDisc.TOC.LastRecordedTrackNumber
|
||||
);
|
||||
goto SKIPPO;
|
||||
}
|
||||
|
||||
bool badToc = false;
|
||||
for (int t = 0; t < 101; t++)
|
||||
{
|
||||
if (srcToc.TOCItems[t].Exists != dstToc.TOCItems[t].Exists
|
||||
|| srcToc.TOCItems[t].Control != dstToc.TOCItems[t].Control
|
||||
|| srcToc.TOCItems[t].LBA != dstToc.TOCItems[t].LBA
|
||||
)
|
||||
{
|
||||
sw.WriteLine("Mismatch in TOCItem");
|
||||
SwDumpToc(t);
|
||||
badToc = true;
|
||||
}
|
||||
}
|
||||
if (badToc)
|
||||
goto SKIPPO;
|
||||
|
||||
void SwDumpChunkOne(string comment, int lba, byte[] buf, int addr, int count)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
int[] offenders = new int[12];
|
||||
|
||||
void SwDumpChunk(int lba, int dispAddr, int addr, int count, int 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();
|
||||
SwDumpChunkOne($"SRC #{lba,6} ({new Timestamp(lba)})", lba, srcDataBuf, addr, count);
|
||||
SwDumpChunkOne($"DST #{lba,6} ({new Timestamp(lba)})", lba, dstDataBuf, addr, count);
|
||||
}
|
||||
|
||||
//verify each sector contents
|
||||
int nSectors = srcDisc.Session1.LeadoutLBA;
|
||||
for (int lba = -150; lba < nSectors; lba++)
|
||||
{
|
||||
if (verbose)
|
||||
if (lba % 1000 == 0)
|
||||
Console.WriteLine("LBA {0} of {1}", lba, nSectors);
|
||||
|
||||
if (cancelToken != null)
|
||||
if (cancelToken.Token.IsCancellationRequested)
|
||||
return false;
|
||||
|
||||
srcDsr.ReadLBA_2448(lba, srcDataBuf, 0);
|
||||
dstDsr.ReadLBA_2448(lba, dstDataBuf, 0);
|
||||
|
||||
//check the header
|
||||
for (int b = 0; b < 16; b++)
|
||||
{
|
||||
if (srcDataBuf[b] != dstDataBuf[b])
|
||||
{
|
||||
sw.WriteLine("Mismatch in sector header at byte {0}", b);
|
||||
offenders[0] = b;
|
||||
SwDumpChunk(lba, 0, 0, 16, 1);
|
||||
goto SKIPPO;
|
||||
}
|
||||
}
|
||||
|
||||
// check userData
|
||||
for (int b = 16; b < 2352; b++)
|
||||
{
|
||||
if (srcDataBuf[b] != dstDataBuf[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 (srcDataBuf[b] != dstDataBuf[b])
|
||||
{
|
||||
offenders[numOffenders++] = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (numOffenders != 0)
|
||||
{
|
||||
sw.Write("LBA {0} mismatch(es) at subchannel {1}; terminating sector cmp\n", lba, (char)('P' + c));
|
||||
SwDumpChunk(lba, 0, 2352 + c * 12, 12, numOffenders);
|
||||
goto SKIPPO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
SKIPPO:
|
||||
sw.WriteLine("END COMPARE");
|
||||
sw.WriteLine("-----------------------------");
|
||||
|
||||
return success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
srcDisc?.Dispose();
|
||||
dstDisc?.Dispose();
|
||||
}
|
||||
|
||||
} //CompareFile
|
||||
|
||||
} //class DiscoHawk
|
||||
|
||||
#if false
|
||||
/// <summary>code to test ECM</summary>
|
||||
static class Test
|
||||
{
|
||||
public static void Shuffle<T>(this IList<T> list, Random rng)
|
||||
{
|
||||
int n = list.Count;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = rng.Next(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestMain()
|
||||
{
|
||||
var plaindisc = Disc.LoadAutomagic("d:\\ecmtest\\test.cue");
|
||||
var ecmdisc = Disc.LoadAutomagic("d:\\ecmtest\\ecmtest.cue");
|
||||
|
||||
// var prefs = new CueBinPrefs
|
||||
// {
|
||||
// AnnotateCue = false,
|
||||
// OneBlobPerTrack = false,
|
||||
// ReallyDumpBin = true,
|
||||
// SingleSession = true,
|
||||
// DumpToBitbucket = true
|
||||
// };
|
||||
// var dump = ecmdisc.DumpCueBin("test", prefs);
|
||||
// dump.Dump("test", prefs);
|
||||
|
||||
// var prefs = new CueBinPrefs
|
||||
// {
|
||||
// AnnotateCue = false,
|
||||
// OneBlobPerTrack = false,
|
||||
// ReallyDumpBin = true,
|
||||
// SingleSession = true
|
||||
// };
|
||||
// var dump = ecmdisc.DumpCueBin("test", prefs);
|
||||
// dump.Dump(@"D:\ecmtest\myout", prefs);
|
||||
|
||||
int seed = 102;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
Console.WriteLine("running seed {0}", seed);
|
||||
Random r = new Random(seed);
|
||||
seed++;
|
||||
|
||||
byte[] chunkbuf_corlet = new byte[2352 * 20];
|
||||
byte[] chunkbuf_mine = new byte[2352 * 20];
|
||||
// int length = (ecmdisc._Sectors.Count - 150) * 2352; // API has changed
|
||||
var length = 0;
|
||||
int counter = 0;
|
||||
List<Tuple<int, int>> testChunks = new List<Tuple<int, int>>();
|
||||
while (counter < length)
|
||||
{
|
||||
int chunk = r.Next(1, 2352 * 20);
|
||||
if (r.Next(20) == 0)
|
||||
chunk /= 100;
|
||||
if (r.Next(40) == 0)
|
||||
chunk = 0;
|
||||
if (counter + chunk > length)
|
||||
chunk = length - counter;
|
||||
testChunks.Add(new Tuple<int, int>(counter, chunk));
|
||||
counter += chunk;
|
||||
}
|
||||
testChunks.Shuffle(r);
|
||||
|
||||
for (int t = 0; t < testChunks.Count; t++)
|
||||
{
|
||||
//Console.WriteLine("skank");
|
||||
var item = testChunks[t];
|
||||
//Console.WriteLine("chunk {0} of {3} is {1} bytes @ {2:X8}", t, item.Item2, item.Item1, testChunks.Count);
|
||||
// plaindisc.ReadLBA_2352_Flat(item.Item1, chunkbuf_corlet, 0, item.Item2); // API has changed
|
||||
// ecmdisc.ReadLBA_2352_Flat(item.Item1, chunkbuf_mine, 0, item.Item2); // API has changed
|
||||
for (int i = 0; i < item.Item2; i++)
|
||||
if (chunkbuf_corlet[i] != chunkbuf_mine[i])
|
||||
{
|
||||
Debug.Assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,357 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
/// <remarks>
|
||||
/// cue format preferences notes
|
||||
///
|
||||
/// PCEjin -
|
||||
/// does not like session commands
|
||||
/// it can handle binpercue
|
||||
/// it seems not to be able to handle binpertrack, or maybe i am doing something wrong (still haven't ruled it out)
|
||||
/// </remarks>
|
||||
public static class DiscoHawkLogic
|
||||
{
|
||||
private static bool CompareFile(string infile, DiscInterface loadDiscInterface, DiscInterface cmpif, bool verbose, CancellationTokenSource cancelToken, StringWriter sw)
|
||||
{
|
||||
Disc srcDisc = null, dstDisc = null;
|
||||
|
||||
try
|
||||
{
|
||||
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_DiscMountPolicy = new DiscMountPolicy { CUE_PregapContradictionModeA = cmpif != DiscInterface.MednaDisc },
|
||||
IN_FromPath = infile
|
||||
};
|
||||
|
||||
dmj.Run();
|
||||
|
||||
srcDisc = dmj.OUT_Disc;
|
||||
|
||||
var dstDmj = new DiscMountJob { IN_DiscInterface = cmpif, IN_FromPath = infile };
|
||||
dstDmj.Run();
|
||||
dstDisc = dstDmj.OUT_Disc;
|
||||
|
||||
var srcDsr = new DiscSectorReader(srcDisc);
|
||||
var dstDsr = new DiscSectorReader(dstDisc);
|
||||
|
||||
var srcToc = srcDisc.TOC;
|
||||
var dstToc = dstDisc.TOC;
|
||||
|
||||
var srcDataBuf = new byte[2448];
|
||||
var dstDataBuf = new byte[2448];
|
||||
|
||||
void SwDumpTocOne(DiscTOC.TOCItem item)
|
||||
{
|
||||
if (!item.Exists)
|
||||
{
|
||||
sw.Write("(---missing---)");
|
||||
}
|
||||
else
|
||||
{
|
||||
sw.Write("({0:X2} - {1})", (byte) item.Control, item.LBA);
|
||||
}
|
||||
}
|
||||
|
||||
void SwDumpToc(int index)
|
||||
{
|
||||
sw.Write("SRC TOC#{0,3} ", index);
|
||||
SwDumpTocOne(srcToc.TOCItems[index]);
|
||||
sw.WriteLine();
|
||||
sw.Write("DST TOC#{0,3} ", index);
|
||||
SwDumpTocOne(dstToc.TOCItems[index]);
|
||||
sw.WriteLine();
|
||||
}
|
||||
|
||||
//verify sector count
|
||||
if (srcDisc.Session1.LeadoutLBA != dstDisc.Session1.LeadoutLBA)
|
||||
{
|
||||
sw.Write("LeadoutTrack.LBA {0} vs {1}\n", srcDisc.Session1.LeadoutTrack.LBA, dstDisc.Session1.LeadoutTrack.LBA);
|
||||
goto SKIPPO;
|
||||
}
|
||||
|
||||
//verify TOC match
|
||||
if (srcDisc.TOC.FirstRecordedTrackNumber != dstDisc.TOC.FirstRecordedTrackNumber
|
||||
|| srcDisc.TOC.LastRecordedTrackNumber != dstDisc.TOC.LastRecordedTrackNumber)
|
||||
{
|
||||
sw.WriteLine("Mismatch of RecordedTrackNumbers: {0}-{1} vs {2}-{3}",
|
||||
srcDisc.TOC.FirstRecordedTrackNumber, srcDisc.TOC.LastRecordedTrackNumber,
|
||||
dstDisc.TOC.FirstRecordedTrackNumber, dstDisc.TOC.LastRecordedTrackNumber
|
||||
);
|
||||
goto SKIPPO;
|
||||
}
|
||||
|
||||
bool badToc = false;
|
||||
for (int t = 0; t < 101; t++)
|
||||
{
|
||||
if (srcToc.TOCItems[t].Exists != dstToc.TOCItems[t].Exists
|
||||
|| srcToc.TOCItems[t].Control != dstToc.TOCItems[t].Control
|
||||
|| srcToc.TOCItems[t].LBA != dstToc.TOCItems[t].LBA
|
||||
)
|
||||
{
|
||||
sw.WriteLine("Mismatch in TOCItem");
|
||||
SwDumpToc(t);
|
||||
badToc = true;
|
||||
}
|
||||
}
|
||||
if (badToc)
|
||||
goto SKIPPO;
|
||||
|
||||
void SwDumpChunkOne(string comment, int lba, byte[] buf, int addr, int count)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
int[] offenders = new int[12];
|
||||
|
||||
void SwDumpChunk(int lba, int dispAddr, int addr, int count, int 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();
|
||||
SwDumpChunkOne($"SRC #{lba,6} ({new Timestamp(lba)})", lba, srcDataBuf, addr, count);
|
||||
SwDumpChunkOne($"DST #{lba,6} ({new Timestamp(lba)})", lba, dstDataBuf, addr, count);
|
||||
}
|
||||
|
||||
//verify each sector contents
|
||||
int nSectors = srcDisc.Session1.LeadoutLBA;
|
||||
for (int lba = -150; lba < nSectors; lba++)
|
||||
{
|
||||
if (verbose)
|
||||
if (lba % 1000 == 0)
|
||||
Console.WriteLine("LBA {0} of {1}", lba, nSectors);
|
||||
|
||||
if (cancelToken != null)
|
||||
if (cancelToken.Token.IsCancellationRequested)
|
||||
return false;
|
||||
|
||||
srcDsr.ReadLBA_2448(lba, srcDataBuf, 0);
|
||||
dstDsr.ReadLBA_2448(lba, dstDataBuf, 0);
|
||||
|
||||
//check the header
|
||||
for (int b = 0; b < 16; b++)
|
||||
{
|
||||
if (srcDataBuf[b] != dstDataBuf[b])
|
||||
{
|
||||
sw.WriteLine("Mismatch in sector header at byte {0}", b);
|
||||
offenders[0] = b;
|
||||
SwDumpChunk(lba, 0, 0, 16, 1);
|
||||
goto SKIPPO;
|
||||
}
|
||||
}
|
||||
|
||||
// check userData
|
||||
for (int b = 16; b < 2352; b++)
|
||||
{
|
||||
if (srcDataBuf[b] != dstDataBuf[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 (srcDataBuf[b] != dstDataBuf[b])
|
||||
{
|
||||
offenders[numOffenders++] = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (numOffenders != 0)
|
||||
{
|
||||
sw.Write("LBA {0} mismatch(es) at subchannel {1}; terminating sector cmp\n", lba, (char)('P' + c));
|
||||
SwDumpChunk(lba, 0, 2352 + c * 12, 12, numOffenders);
|
||||
goto SKIPPO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
SKIPPO:
|
||||
sw.WriteLine("END COMPARE");
|
||||
sw.WriteLine("-----------------------------");
|
||||
|
||||
return success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
srcDisc?.Dispose();
|
||||
dstDisc?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> FindCuesRecurse(string dir)
|
||||
{
|
||||
var ret = new List<string>();
|
||||
var dpTodo = new Queue<string>();
|
||||
dpTodo.Enqueue(dir);
|
||||
for (; ; )
|
||||
{
|
||||
if (dpTodo.Count == 0)
|
||||
break;
|
||||
var dpCurr = dpTodo.Dequeue();
|
||||
foreach(var fi in new DirectoryInfo(dpCurr).GetFiles("*.cue"))
|
||||
{
|
||||
ret.Add(fi.FullName);
|
||||
}
|
||||
Parallel.ForEach(new DirectoryInfo(dpCurr).GetDirectories(), di =>
|
||||
{
|
||||
lock(dpTodo)
|
||||
dpTodo.Enqueue(di.FullName);
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void RunWithArgs(string[] args, Action<string> showComparisonResultsCallback)
|
||||
{
|
||||
bool scanCues = false;
|
||||
string dirArg = null;
|
||||
string infile = null;
|
||||
var loadDiscInterface = DiscInterface.BizHawk;
|
||||
var compareDiscInterfaces = new List<DiscInterface>();
|
||||
bool hawk = false;
|
||||
|
||||
int idx = 0;
|
||||
while (idx < args.Length)
|
||||
{
|
||||
string a = args[idx++];
|
||||
string au = a.ToUpperInvariant();
|
||||
if (au == "LOAD")
|
||||
loadDiscInterface = (DiscInterface)Enum.Parse(typeof(DiscInterface), args[idx++], true);
|
||||
else if (au == "COMPARE")
|
||||
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 (hawk)
|
||||
{
|
||||
if (infile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO - write it out
|
||||
var dmj = new DiscMountJob { IN_DiscInterface = loadDiscInterface, IN_FromPath = infile };
|
||||
dmj.Run();
|
||||
}
|
||||
|
||||
bool verbose = true;
|
||||
|
||||
if (scanCues)
|
||||
{
|
||||
verbose = false;
|
||||
var todo = FindCuesRecurse(dirArg);
|
||||
var po = new ParallelOptions();
|
||||
var cts = new CancellationTokenSource();
|
||||
po.CancellationToken = cts.Token;
|
||||
po.MaxDegreeOfParallelism = 1;
|
||||
if(po.MaxDegreeOfParallelism < 0) po.MaxDegreeOfParallelism = 1;
|
||||
object olock = new object();
|
||||
int ctr=0;
|
||||
bool blocked = false;
|
||||
try
|
||||
{
|
||||
Parallel.ForEach(todo, po, (fp) =>
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
if(!blocked)
|
||||
foreach (var cmpif in compareDiscInterfaces)
|
||||
{
|
||||
var sw = new StringWriter();
|
||||
bool success = CompareFile(fp, loadDiscInterface, cmpif, verbose, cts, sw);
|
||||
if (!success)
|
||||
{
|
||||
lock (Console.Out)
|
||||
Console.Out.Write(sw.ToString());
|
||||
|
||||
cts.Cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (AggregateException ae) {
|
||||
Console.WriteLine(ae.ToString());
|
||||
}
|
||||
catch (OperationCanceledException oce)
|
||||
{
|
||||
Console.WriteLine(oce.ToString());
|
||||
}
|
||||
Console.WriteLine("--TERMINATED--");
|
||||
return;
|
||||
}
|
||||
|
||||
if (compareDiscInterfaces.Count != 0)
|
||||
{
|
||||
var sw = new StringWriter();
|
||||
foreach (var cmpif in compareDiscInterfaces)
|
||||
{
|
||||
CompareFile(infile, loadDiscInterface, cmpif, verbose, null, sw);
|
||||
}
|
||||
|
||||
sw.Flush();
|
||||
string results = sw.ToString();
|
||||
showComparisonResultsCallback(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
public static class ECMTest
|
||||
{
|
||||
[Conditional("FALSE")]
|
||||
public static void TestMain()
|
||||
{
|
||||
static void Shuffle<T>(IList<T> list, Random rng)
|
||||
{
|
||||
int n = list.Count;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = rng.Next(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
|
||||
var plaindisc = Disc.LoadAutomagic("d:\\ecmtest\\test.cue");
|
||||
var ecmdisc = Disc.LoadAutomagic("d:\\ecmtest\\ecmtest.cue");
|
||||
|
||||
// var prefs = new CueBinPrefs
|
||||
// {
|
||||
// AnnotateCue = false,
|
||||
// OneBlobPerTrack = false,
|
||||
// ReallyDumpBin = true,
|
||||
// SingleSession = true,
|
||||
// DumpToBitbucket = true
|
||||
// };
|
||||
// var dump = ecmdisc.DumpCueBin("test", prefs);
|
||||
// dump.Dump("test", prefs);
|
||||
|
||||
// var prefs = new CueBinPrefs
|
||||
// {
|
||||
// AnnotateCue = false,
|
||||
// OneBlobPerTrack = false,
|
||||
// ReallyDumpBin = true,
|
||||
// SingleSession = true
|
||||
// };
|
||||
// var dump = ecmdisc.DumpCueBin("test", prefs);
|
||||
// dump.Dump(@"D:\ecmtest\myout", prefs);
|
||||
|
||||
int seed = 102;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
Console.WriteLine("running seed {0}", seed);
|
||||
Random r = new Random(seed);
|
||||
seed++;
|
||||
|
||||
byte[] chunkbuf_corlet = new byte[2352 * 20];
|
||||
byte[] chunkbuf_mine = new byte[2352 * 20];
|
||||
// int length = (ecmdisc._Sectors.Count - 150) * 2352; // API has changed
|
||||
var length = 0;
|
||||
int counter = 0;
|
||||
List<Tuple<int, int>> testChunks = new List<Tuple<int, int>>();
|
||||
while (counter < length)
|
||||
{
|
||||
int chunk = r.Next(1, 2352 * 20);
|
||||
if (r.Next(20) == 0)
|
||||
chunk /= 100;
|
||||
if (r.Next(40) == 0)
|
||||
chunk = 0;
|
||||
if (counter + chunk > length)
|
||||
chunk = length - counter;
|
||||
testChunks.Add(new Tuple<int, int>(counter, chunk));
|
||||
counter += chunk;
|
||||
}
|
||||
Shuffle(testChunks, r);
|
||||
|
||||
for (int t = 0; t < testChunks.Count; t++)
|
||||
{
|
||||
//Console.WriteLine("skank");
|
||||
var item = testChunks[t];
|
||||
//Console.WriteLine("chunk {0} of {3} is {1} bytes @ {2:X8}", t, item.Item2, item.Item1, testChunks.Count);
|
||||
// plaindisc.ReadLBA_2352_Flat(item.Item1, chunkbuf_corlet, 0, item.Item2); // API has changed
|
||||
// ecmdisc.ReadLBA_2352_Flat(item.Item1, chunkbuf_mine, 0, item.Item2); // API has changed
|
||||
for (int i = 0; i < item.Item2; i++)
|
||||
if (chunkbuf_corlet[i] != chunkbuf_mine[i])
|
||||
{
|
||||
Debug.Assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue