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( fromPath: infile, discMountPolicy: new DiscMountPolicy { CUE_PregapContradictionModeA = cmpif != DiscInterface.MednaDisc }, discInterface: loadDiscInterface); dmj.Run(); srcDisc = dmj.OUT_Disc; var dstDmj = new DiscMountJob(fromPath: infile, discInterface: cmpif); 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(); } }
public static void Dump(Disc disc, string path) { using (var sw = new StreamWriter(path)) { //NOTE: IsoBuster requires the A0,A1,A2 RawTocEntries to be first or else it can't do anything with the tracks //if we ever get them in a different order, we'll have to re-order them here sw.WriteLine("[CloneCD]"); sw.WriteLine("Version=3"); sw.WriteLine(); sw.WriteLine("[Disc]"); sw.WriteLine("TocEntries={0}", disc.RawTOCEntries.Count); sw.WriteLine("Sessions=1"); sw.WriteLine("DataTracksScrambled=0"); sw.WriteLine("CDTextLength=0"); //not supported anyway sw.WriteLine(); sw.WriteLine("[Session 1]"); sw.WriteLine("PreGapMode=2"); sw.WriteLine("PreGapSubC=1"); sw.WriteLine(); for (int i = 0; i < disc.RawTOCEntries.Count; i++) { var entry = disc.RawTOCEntries[i]; //ehhh something's wrong with how I track these int point = entry.QData.q_index.DecimalValue; if (point == 100) { point = 0xA0; } if (point == 101) { point = 0xA1; } if (point == 102) { point = 0xA2; } sw.WriteLine("[Entry {0}]", i); sw.WriteLine("Session=1"); sw.WriteLine("Point=0x{0:x2}", point); sw.WriteLine("ADR=0x{0:x2}", entry.QData.ADR); sw.WriteLine("Control=0x{0:x2}", (int)entry.QData.CONTROL); sw.WriteLine("TrackNo={0}", entry.QData.q_tno.DecimalValue); sw.WriteLine("AMin={0}", entry.QData.min.DecimalValue); sw.WriteLine("ASec={0}", entry.QData.sec.DecimalValue); sw.WriteLine("AFrame={0}", entry.QData.frame.DecimalValue); sw.WriteLine("ALBA={0}", entry.QData.Timestamp - 150); //remember to adapt the absolute MSF to an LBA (this field is redundant...) sw.WriteLine("Zero={0}", entry.QData.zero); sw.WriteLine("PMin={0}", entry.QData.ap_min.DecimalValue); sw.WriteLine("PSec={0}", entry.QData.ap_sec.DecimalValue); sw.WriteLine("PFrame={0}", entry.QData.ap_frame.DecimalValue); sw.WriteLine("PLBA={0}", entry.QData.AP_Timestamp - 150); //remember to adapt the absolute MSF to an LBA (this field is redundant...) sw.WriteLine(); } //this is nonsense, really. the whole CCD track list shouldn't be needed. //but in order to make a high quality CCD which can be inspected by various other tools, we need it //now, regarding the indexes.. theyre truly useless. having indexes written out with the tracks is bad news. //index information is only truly stored in subQ for (int tnum = 1; tnum <= disc.Session1.LastInformationTrack.Number; tnum++) { var track = disc.Session1.Tracks[tnum]; sw.WriteLine("[TRACK {0}]", track.Number); sw.WriteLine("MODE={0}", track.Mode); //indexes are BS, don't write them. but we certainly need an index 1 sw.WriteLine("INDEX 1={0}", track.LBA); sw.WriteLine(); } } //TODO - actually re-add //dump the img and sub //TODO - acquire disk size first string imgPath = Path.ChangeExtension(path, ".img"); string subPath = Path.ChangeExtension(path, ".sub"); var buf2448 = new byte[2448]; DiscSectorReader dsr = new DiscSectorReader(disc); using var imgFile = File.OpenWrite(imgPath); using var subFile = File.OpenWrite(subPath); int nLBA = disc.Session1.LeadoutLBA; for (int lba = 0; lba < nLBA; lba++) { dsr.ReadLBA_2448(lba, buf2448, 0); imgFile.Write(buf2448, 0, 2352); subFile.Write(buf2448, 2352, 96); } }
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(); } }