/// <param name="fromPath">The filename to be loaded</param> /// <param name="discMountPolicy">Cryptic policies to be used when mounting the disc.</param> /// <param name="discInterface"> /// The interface to be used for loading the disc. /// Usually you'll want DiscInterface.BizHawk, but others can be used for A/B testing /// </param> /// <param name="slowLoadAbortThreshold"> /// Slow-loading cues won't finish loading if this threshold is exceeded. /// Set to 10 to always load a cue /// </param> public DiscMountJob(string fromPath, DiscMountPolicy discMountPolicy, DiscInterface discInterface = DiscInterface.BizHawk, int slowLoadAbortThreshold = 10) { IN_FromPath = fromPath; IN_DiscMountPolicy = discMountPolicy; IN_DiscInterface = discInterface; IN_SlowLoadAbortThreshold = slowLoadAbortThreshold; }
} //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
static bool CompareFile(string infile, DiscInterface loadDiscInterface, DiscInterface cmpif, bool verbose, CancellationTokenSource cancelToken, StringWriter sw) { Disc src_disc = null, dst_disc = 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_FromPath = infile }; if (cmpif == DiscInterface.MednaDisc) { dmj.IN_DiscMountPolicy.CUE_PregapContradictionModeA = false; } dmj.Run(); src_disc = dmj.OUT_Disc; var dst_dmj = new DiscMountJob { IN_DiscInterface = cmpif, IN_FromPath = infile }; dst_dmj.Run(); 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.TOC; var dst_toc = dst_disc.TOC; var src_databuf = new byte[2448]; var dst_databuf = new byte[2448]; Action<DiscTOC.TOCItem> sw_dump_toc_one = (item) => { if (!item.Exists) sw.Write("(---missing---)"); else sw.Write("({0:X2} - {1})", (byte)item.Control, item.LBA); }; 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.Session1.LeadoutLBA != dst_disc.Session1.LeadoutLBA) { sw.Write("LeadoutTrack.LBA {0} vs {1}\n", src_disc.Session1.LeadoutTrack.LBA, dst_disc.Session1.LeadoutTrack.LBA); goto SKIPPO; } //verify TOC match if (src_disc.TOC.FirstRecordedTrackNumber != dst_disc.TOC.FirstRecordedTrackNumber || src_disc.TOC.LastRecordedTrackNumber != dst_disc.TOC.LastRecordedTrackNumber) { sw.WriteLine("Mismatch of RecordedTrackNumbers: {0}-{1} vs {2}-{3}", src_disc.TOC.FirstRecordedTrackNumber, src_disc.TOC.LastRecordedTrackNumber, dst_disc.TOC.FirstRecordedTrackNumber, dst_disc.TOC.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].LBA != dst_toc.TOCItems[t].LBA ) { sw.WriteLine("Mismatch in TOCItem"); sw_dump_toc(t); badToc = true; } } if (badToc) goto SKIPPO; 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++) { 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); }; //verify each sector contents int nSectors = src_disc.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; src_dsr.ReadLBA_2448(lba, src_databuf, 0); dst_dsr.ReadLBA_2448(lba, dst_databuf, 0); //check the header for (int b = 0; b < 16; 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; } } //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; } } } success = true; SKIPPO: sw.WriteLine("END COMPARE"); sw.WriteLine("-----------------------------"); return success; } finally { if (src_disc != null) src_disc.Dispose(); if (dst_disc != null) dst_disc.Dispose(); } }
public static bool HawkAndWriteFile(string inputPath, Action <string> errorCallback, DiscInterface discInterface = DiscInterface.BizHawk) { DiscMountJob job = new(inputPath, discInterface); job.Run(); var disc = job.OUT_Disc; if (job.OUT_ErrorLevel) { errorCallback(job.OUT_Log); return(false); } var baseName = Path.GetFileNameWithoutExtension(inputPath); var outfile = Path.Combine(Path.GetDirectoryName(inputPath), $"{baseName}_hawked.ccd"); CCD_Format.Dump(disc, outfile); return(true); }
/// <param name="fromPath">The filename to be loaded</param> /// <param name="discInterface"> /// The interface to be used for loading the disc. /// Usually you'll want DiscInterface.BizHawk, but others can be used for A/B testing /// </param> /// <param name="slowLoadAbortThreshold"> /// Slow-loading cues won't finish loading if this threshold is exceeded. /// Set to 10 to always load a cue /// </param> public DiscMountJob(string fromPath, DiscInterface discInterface = DiscInterface.BizHawk, int slowLoadAbortThreshold = 10) : this(fromPath, new(), discInterface, slowLoadAbortThreshold)