static bool GetSectorForPregapRaw(Device dev, uint lba, Database.Models.Device dbDev, out byte[] subBuf) { byte[] cmdBuf; bool sense; subBuf = null; sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, out _); if (sense) { sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, out _); } if (!sense) { byte[] tmpBuf = new byte[96]; Array.Copy(cmdBuf, 2352, tmpBuf, 0, 96); subBuf = DeinterleaveQ(tmpBuf); } else { sense = dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, out _); if (sense) { sense = dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, out _); } if (!sense) { subBuf = DeinterleaveQ(cmdBuf); } else if (dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || dev.Manufacturer.ToLowerInvariant() == "plextor") { sense = dev.PlextorReadCdDa(out cmdBuf, out _, lba, 96, 1, PlextorSubchannel.All, dev.Timeout, out _); } { if (!sense) { subBuf = DeinterleaveQ(cmdBuf); } } } return(sense); }
static bool GetSectorForPregapQ16(Device dev, uint lba, Database.Models.Device dbDev, out byte[] subBuf, bool audioTrack) { byte[] cmdBuf; bool sense; subBuf = null; if(audioTrack) { sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); if(sense) sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); } else { sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); if(sense) sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); } if(!sense) { subBuf = new byte[16]; Array.Copy(cmdBuf, 2352, subBuf, 0, 16); } else { sense = dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); if(sense) sense = dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); if(!sense) subBuf = cmdBuf; } return sense; }
public static void SolveTrackPregaps(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel, Database.Models.Device dbDev, out bool inexactPositioning) { bool sense = true; // Sense indicator byte[] subBuf = null; int posQ; uint retries; bool? bcd = null; byte[] crc; Dictionary<uint, int> pregaps = new Dictionary<uint, int>(); inexactPositioning = false; if(!supportsPqSubchannel && !supportsRwSubchannel) return; // Check if subchannel is BCD for(retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf, false) : GetSectorForPregapQ16(dev, 11, dbDev, out subBuf, false); if(sense) continue; bcd = (subBuf[9] & 0x10) > 0; break; } AaruConsole.DebugWriteLine("Pregap calculator", bcd == true ? "Subchannel is BCD" : bcd == false ? "Subchannel is not BCD" : "Could not detect drive subchannel BCD"); if(bcd is null) { dumpLog?.WriteLine("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); updateStatus?. Invoke("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); return; } // Initialize the dictionary for(int i = 0; i < tracks.Length; i++) pregaps[tracks[i].TrackSequence] = 0; for(int t = 0; t < tracks.Length; t++) { Track track = tracks[t]; int trackRetries = 0; // First track of each session has at least 150 sectors of pregap and is not readable always if(tracks.Where(t => t.TrackSession == track.TrackSession).OrderBy(t => t.TrackSequence). FirstOrDefault().TrackSequence == track.TrackSequence) { AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.TrackSequence); if(track.TrackSequence > 1) pregaps[track.TrackSequence] = 150; continue; } if(t > 0 && tracks[t - 1].TrackType == tracks[t].TrackType) { AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.TrackSequence); continue; } AaruConsole.DebugWriteLine("Pregap calculator", "Track {0}", track.TrackSequence); int lba = (int)track.TrackStartSector - 1; bool pregapFound = false; Track previousTrack = tracks.FirstOrDefault(t => t.TrackSequence == track.TrackSequence - 1); bool goneBack = false; bool goFront = false; bool forward = false; bool crcOk = false; bool previousPregapIsPreviousTrack = false; // Check if pregap is 0 for(retries = 0; retries < 10 && !pregapFound; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf, track.TrackType == TrackType.Audio) : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf, track.TrackType == TrackType.Audio); if(sense) { AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}", lba, retries + 1, sense); continue; } if(bcd == false) BinaryToBcdQ(subBuf); CRC16CCITTContext.Data(subBuf, 10, out crc); AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q: {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; // Try to do a simple correction if(!crcOk) { // Data track cannot have 11xxb in CONTROL if((subBuf[0] & 0x40) > 0) subBuf[0] &= 0x7F; // ADR only uses two bits subBuf[0] &= 0xF3; // Don't care about other Q modes if((subBuf[0] & 0xF) == 1) { // ZERO only used in DDCD subBuf[6] = 0; // Fix BCD numbering for(int i = 1; i < 10; i++) { if((subBuf[i] & 0xF0) > 0xA0) subBuf[i] &= 0x7F; if((subBuf[i] & 0x0F) > 0x0A) subBuf[i] &= 0xF7; } } CRC16CCITTContext.Data(subBuf, 10, out crc); crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; if(crcOk) { AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); } else continue; } BcdToBinaryQ(subBuf); // Q position if((subBuf[0] & 0xF) != 1) continue; posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150; if(subBuf[1] != track.TrackSequence - 1 || subBuf[2] == 0 || posQ != lba) break; pregaps[track.TrackSequence] = 0; pregapFound = true; } if(pregapFound) continue; // Calculate pregap lba = (int)track.TrackStartSector - 150; while(lba > (int)previousTrack.TrackStartSector && lba <= (int)track.TrackStartSector) { // Some drives crash if you try to read just before the previous read, so seek away first if(!forward) sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba - 10, dbDev, out subBuf, track.TrackType == TrackType.Audio) : GetSectorForPregapQ16(dev, (uint)lba - 10, dbDev, out subBuf, track.TrackType == TrackType.Audio); for(retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf, track.TrackType == TrackType.Audio) : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf, track.TrackType == TrackType.Audio); if(sense) continue; if(bcd == false) BinaryToBcdQ(subBuf); CRC16CCITTContext.Data(subBuf, 10, out crc); AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q: {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; // Try to do a simple correction if(!crcOk) { // Data track cannot have 11xxb in CONTROL if((subBuf[0] & 0x40) > 0) subBuf[0] &= 0x7F; // ADR only uses two bits subBuf[0] &= 0xF3; // Don't care about other Q modes if((subBuf[0] & 0xF) == 1) { // ZERO only used in DDCD subBuf[6] = 0; // Fix BCD numbering for(int i = 1; i < 10; i++) { if((subBuf[i] & 0xF0) > 0xA0) subBuf[i] &= 0x7F; if((subBuf[i] & 0x0F) > 0x0A) subBuf[i] &= 0xF7; } } CRC16CCITTContext.Data(subBuf, 10, out crc); crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; if(crcOk) { AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); break; } } if(crcOk) break; } if(retries == 10) { if(sense) { trackRetries++; if(trackRetries >= 10) { if(pregaps[track.TrackSequence] == 0) { if((previousTrack.TrackType == TrackType.Audio && track.TrackType != TrackType.Audio) || (previousTrack.TrackType != TrackType.Audio && track.TrackType == TrackType.Audio)) { dumpLog?. WriteLine("Could not read subchannel for this track, supposing 150 sectors."); updateStatus?. Invoke("Could not read subchannel for this track, supposing 150 sectors."); } else { dumpLog?. WriteLine("Could not read subchannel for this track, supposing 0 sectors."); updateStatus?. Invoke("Could not read subchannel for this track, supposing 0 sectors."); } } else { dumpLog?. WriteLine($"Could not read subchannel for this track, supposing {pregaps[track.TrackSequence]} sectors."); updateStatus?. Invoke($"Could not read subchannel for this track, supposing {pregaps[track.TrackSequence]} sectors."); } break; } dumpLog?.WriteLine($"Could not read subchannel for sector {lba}"); updateStatus?.Invoke($"Could not read subchannel for sector {lba}"); lba++; forward = true; continue; } dumpLog?.WriteLine($"Could not get correct subchannel for sector {lba}"); updateStatus?.Invoke($"Could not get correct subchannel for sector {lba}"); } if(subBuf.All(b => b == 0)) { inexactPositioning = true; AaruConsole.DebugWriteLine("Pregap calculator", "All Q empty for LBA {0}", lba); break; } BcdToBinaryQ(subBuf); // If it's not Q position if((subBuf[0] & 0xF) != 1) { // This means we already searched back, so search forward if(goFront) { lba++; forward = true; if(lba == (int)previousTrack.TrackStartSector) break; continue; } // Search back goneBack = true; lba--; forward = false; continue; } // Previous track if(subBuf[1] < track.TrackSequence) { lba++; forward = true; previousPregapIsPreviousTrack = true; // Already gone back, so go forward if(goneBack) goFront = true; continue; } // Same track, but not pregap if(subBuf[1] == track.TrackSequence && subBuf[2] > 0) { lba--; forward = false; if(previousPregapIsPreviousTrack) break; continue; } previousPregapIsPreviousTrack = false; // Pregap according to Q position posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150; int diff = posQ - lba; int pregapQ = (int)track.TrackStartSector - lba; if(diff != 0) { AaruConsole.DebugWriteLine("Pregap calculator", "Invalid Q position for LBA {0}, got {1}", lba, posQ); inexactPositioning = true; } // Received a Q post the LBA we wanted, just go back. If we are already going forward, break if(posQ > lba) { if(forward) break; lba--; continue; } // Bigger than known change, otherwise we found it if(pregapQ > pregaps[track.TrackSequence]) { // If CRC is not OK, only accept pregaps less than 10 sectors longer than previously now if(crcOk || pregapQ - pregaps[track.TrackSequence] < 10) { AaruConsole.DebugWriteLine("Pregap calculator", "Pregap for track {0}: {1}", track.TrackSequence, pregapQ); pregaps[track.TrackSequence] = pregapQ; } // We are going forward, so we have already been in the previous track, so add 1 to pregap and get out of here else if(forward) { pregaps[track.TrackSequence]++; break; } } else if(pregapQ == pregaps[track.TrackSequence]) break; lba--; forward = false; } } for(int i = 0; i < tracks.Length; i++) { tracks[i].TrackPregap = (ulong)pregaps[tracks[i].TrackSequence]; tracks[i].TrackStartSector -= tracks[i].TrackPregap; #if DEBUG dumpLog?.WriteLine($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors"); updateStatus?.Invoke($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors"); #endif } }
static void DoScsiMediaInfo(bool debug, bool verbose, string outputPrefix, Devices.Device dev) { var scsiInfo = new ScsiInfo(dev); if (!scsiInfo.MediaInserted) { return; } if (scsiInfo.DeviceInfo.ScsiModeSense6 != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_scsi_modesense6.bin", "SCSI MODE SENSE (6)", scsiInfo.DeviceInfo.ScsiModeSense6); } if (scsiInfo.DeviceInfo.ScsiModeSense10 != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_scsi_modesense10.bin", "SCSI MODE SENSE (10)", scsiInfo.DeviceInfo.ScsiModeSense10); } switch (dev.ScsiType) { case PeripheralDeviceTypes.DirectAccess: case PeripheralDeviceTypes.MultiMediaDevice: case PeripheralDeviceTypes.OCRWDevice: case PeripheralDeviceTypes.OpticalDevice: case PeripheralDeviceTypes.SimplifiedDevice: case PeripheralDeviceTypes.WriteOnceDevice: if (scsiInfo.ReadCapacity != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readcapacity.bin", "SCSI READ CAPACITY", scsiInfo.ReadCapacity); } if (scsiInfo.ReadCapacity16 != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readcapacity16.bin", "SCSI READ CAPACITY(16)", scsiInfo.ReadCapacity16); } if (scsiInfo.Blocks != 0 && scsiInfo.BlockSize != 0) { AaruConsole.WriteLine("Media has {0} blocks of {1} bytes/each. (for a total of {2} bytes)", scsiInfo.Blocks, scsiInfo.BlockSize, scsiInfo.Blocks * scsiInfo.BlockSize); } break; case PeripheralDeviceTypes.SequentialAccess: if (scsiInfo.DensitySupport != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_ssc_reportdensitysupport_media.bin", "SSC REPORT DENSITY SUPPORT (MEDIA)", scsiInfo.DensitySupport); if (scsiInfo.DensitySupportHeader.HasValue) { AaruConsole.WriteLine("Densities supported by currently inserted media:"); AaruConsole.WriteLine(DensitySupport.PrettifyDensity(scsiInfo.DensitySupportHeader)); } } if (scsiInfo.MediaTypeSupport != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_ssc_reportdensitysupport_medium_media.bin", "SSC REPORT DENSITY SUPPORT (MEDIUM & MEDIA)", scsiInfo.MediaTypeSupport); if (scsiInfo.MediaTypeSupportHeader.HasValue) { AaruConsole.WriteLine("Medium types currently inserted in device:"); AaruConsole.WriteLine(DensitySupport.PrettifyMediumType(scsiInfo.MediaTypeSupportHeader)); } AaruConsole.WriteLine(DensitySupport.PrettifyMediumType(scsiInfo.MediaTypeSupport)); } break; } if (dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { if (scsiInfo.MmcConfiguration != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_getconfiguration_current.bin", "SCSI GET CONFIGURATION", scsiInfo.MmcConfiguration); } if (scsiInfo.RecognizedFormatLayers != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_formatlayers.bin", "SCSI READ DISC STRUCTURE", scsiInfo.RecognizedFormatLayers); } if (scsiInfo.WriteProtectionStatus != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_writeprotection.bin", "SCSI READ DISC STRUCTURE", scsiInfo.WriteProtectionStatus); } if (scsiInfo.DvdPfi != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_pfi.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdPfi); if (scsiInfo.DecodedPfi.HasValue) { AaruConsole.WriteLine("PFI:\n{0}", PFI.Prettify(scsiInfo.DecodedPfi)); } } if (scsiInfo.DvdDmi != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_dmi.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdDmi); if (DMI.IsXbox(scsiInfo.DvdDmi)) { AaruConsole.WriteLine("Xbox DMI:\n{0}", DMI.PrettifyXbox(scsiInfo.DvdDmi)); } else if (DMI.IsXbox360(scsiInfo.DvdDmi)) { AaruConsole.WriteLine("Xbox 360 DMI:\n{0}", DMI.PrettifyXbox360(scsiInfo.DvdDmi)); } } if (scsiInfo.DvdCmi != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_cmi.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdCmi); AaruConsole.WriteLine("Lead-In CMI:\n{0}", CSS_CPRM.PrettifyLeadInCopyright(scsiInfo.DvdCmi)); } if (scsiInfo.DvdBca != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_bca.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdBca); } if (scsiInfo.DvdAacs != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_aacs.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdAacs); } if (scsiInfo.DvdRamDds != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdram_dds.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdRamDds); AaruConsole.WriteLine("Disc Definition Structure:\n{0}", DDS.Prettify(scsiInfo.DvdRamDds)); } if (scsiInfo.DvdRamCartridgeStatus != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdram_status.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdRamCartridgeStatus); AaruConsole.WriteLine("Medium Status:\n{0}", Cartridge.Prettify(scsiInfo.DvdRamCartridgeStatus)); } if (scsiInfo.DvdRamSpareArea != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdram_spare.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdRamSpareArea); AaruConsole.WriteLine("Spare Area Information:\n{0}", Spare.Prettify(scsiInfo.DvdRamSpareArea)); } if (scsiInfo.LastBorderOutRmd != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_lastrmd.bin", "SCSI READ DISC STRUCTURE", scsiInfo.LastBorderOutRmd); } if (scsiInfo.DvdPreRecordedInfo != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_pri.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdPreRecordedInfo); } if (scsiInfo.DvdrMediaIdentifier != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdr_mediaid.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdrMediaIdentifier); } if (scsiInfo.DvdrPhysicalInformation != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdr_pfi.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdrPhysicalInformation); } if (scsiInfo.DvdPlusAdip != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd+_adip.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdPlusAdip); } if (scsiInfo.DvdPlusDcb != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd+_dcb.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdPlusDcb); } if (scsiInfo.HddvdCopyrightInformation != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_hddvd_cmi.bin", "SCSI READ DISC STRUCTURE", scsiInfo.HddvdCopyrightInformation); } if (scsiInfo.HddvdrMediumStatus != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_hddvdr_status.bin", "SCSI READ DISC STRUCTURE", scsiInfo.HddvdrMediumStatus); } if (scsiInfo.HddvdrLastRmd != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_hddvdr_lastrmd.bin", "SCSI READ DISC STRUCTURE", scsiInfo.HddvdrLastRmd); } if (scsiInfo.DvdrLayerCapacity != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvdr_layercap.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdrLayerCapacity); } if (scsiInfo.DvdrDlMiddleZoneStart != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_mzs.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdrDlMiddleZoneStart); } if (scsiInfo.DvdrDlJumpIntervalSize != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_jis.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdrDlJumpIntervalSize); } if (scsiInfo.DvdrDlManualLayerJumpStartLba != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_manuallj.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdrDlManualLayerJumpStartLba); } if (scsiInfo.DvdrDlRemapAnchorPoint != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_remapanchor.bin", "SCSI READ DISC STRUCTURE", scsiInfo.DvdrDlRemapAnchorPoint); } if (scsiInfo.BlurayDiscInformation != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_di.bin", "SCSI READ DISC STRUCTURE", scsiInfo.BlurayDiscInformation); AaruConsole.WriteLine("Blu-ray Disc Information:\n{0}", DI.Prettify(scsiInfo.BlurayDiscInformation)); } if (scsiInfo.BlurayPac != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_pac.bin", "SCSI READ DISC STRUCTURE", scsiInfo.BlurayPac); } if (scsiInfo.BlurayBurstCuttingArea != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_bca.bin", "SCSI READ DISC STRUCTURE", scsiInfo.BlurayBurstCuttingArea); AaruConsole.WriteLine("Blu-ray Burst Cutting Area:\n{0}", BCA.Prettify(scsiInfo.BlurayBurstCuttingArea)); } if (scsiInfo.BlurayDds != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_dds.bin", "SCSI READ DISC STRUCTURE", scsiInfo.BlurayDds); AaruConsole.WriteLine("Blu-ray Disc Definition Structure:\n{0}", Decoders.Bluray.DDS.Prettify(scsiInfo.BlurayDds)); } if (scsiInfo.BlurayCartridgeStatus != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_cartstatus.bin", "SCSI READ DISC STRUCTURE", scsiInfo.BlurayCartridgeStatus); AaruConsole.WriteLine("Blu-ray Cartridge Status:\n{0}", Decoders.Bluray.Cartridge.Prettify(scsiInfo.BlurayCartridgeStatus)); } if (scsiInfo.BluraySpareAreaInformation != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_spare.bin", "SCSI READ DISC STRUCTURE", scsiInfo.BluraySpareAreaInformation); AaruConsole.WriteLine("Blu-ray Spare Area Information:\n{0}", Decoders.Bluray.Spare.Prettify(scsiInfo.BluraySpareAreaInformation)); } if (scsiInfo.BlurayRawDfl != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_bd_dfl.bin", "SCSI READ DISC STRUCTURE", scsiInfo.BlurayRawDfl); } if (scsiInfo.BlurayTrackResources != null) { AaruConsole.WriteLine("Track Resources Information:\n{0}", DiscInformation.Prettify(scsiInfo.BlurayTrackResources)); DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscinformation_001b.bin", "SCSI READ DISC INFORMATION", scsiInfo.BlurayTrackResources); } if (scsiInfo.BlurayPowResources != null) { AaruConsole.WriteLine("POW Resources Information:\n{0}", DiscInformation.Prettify(scsiInfo.BlurayPowResources)); DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscinformation_010b.bin", "SCSI READ DISC INFORMATION", scsiInfo.BlurayPowResources); } if (scsiInfo.Toc != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_toc.bin", "SCSI READ TOC/PMA/ATIP", scsiInfo.Toc); if (scsiInfo.DecodedToc.HasValue) { AaruConsole.WriteLine("TOC:\n{0}", TOC.Prettify(scsiInfo.DecodedToc)); } } if (scsiInfo.Atip != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_atip.bin", "SCSI READ TOC/PMA/ATIP", scsiInfo.Atip); if (scsiInfo.DecodedAtip.HasValue) { AaruConsole.WriteLine("ATIP:\n{0}", ATIP.Prettify(scsiInfo.DecodedAtip)); } } if (scsiInfo.CompactDiscInformation != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscinformation_000b.bin", "SCSI READ DISC INFORMATION", scsiInfo.CompactDiscInformation); if (scsiInfo.DecodedCompactDiscInformation.HasValue) { AaruConsole.WriteLine("Standard Disc Information:\n{0}", DiscInformation.Prettify000b(scsiInfo.DecodedCompactDiscInformation)); } } if (scsiInfo.Session != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_session.bin", "SCSI READ TOC/PMA/ATIP", scsiInfo.Session); if (scsiInfo.DecodedSession.HasValue) { AaruConsole.WriteLine("Session information:\n{0}", Session.Prettify(scsiInfo.DecodedSession)); } } if (scsiInfo.RawToc != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_rawtoc.bin", "SCSI READ TOC/PMA/ATIP", scsiInfo.RawToc); if (scsiInfo.FullToc.HasValue) { AaruConsole.WriteLine("Raw TOC:\n{0}", FullTOC.Prettify(scsiInfo.RawToc)); } } if (scsiInfo.Pma != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_pma.bin", "SCSI READ TOC/PMA/ATIP", scsiInfo.Pma); AaruConsole.WriteLine("PMA:\n{0}", PMA.Prettify(scsiInfo.Pma)); } if (scsiInfo.CdTextLeadIn != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_cdtext.bin", "SCSI READ TOC/PMA/ATIP", scsiInfo.CdTextLeadIn); if (scsiInfo.DecodedCdTextLeadIn.HasValue) { AaruConsole.WriteLine("CD-TEXT on Lead-In:\n{0}", CDTextOnLeadIn.Prettify(scsiInfo.DecodedCdTextLeadIn)); } } if (!string.IsNullOrEmpty(scsiInfo.Mcn)) { AaruConsole.WriteLine("MCN: {0}", scsiInfo.Mcn); } if (scsiInfo.Isrcs != null) { foreach (KeyValuePair <byte, string> isrc in scsiInfo.Isrcs) { AaruConsole.WriteLine("Track's {0} ISRC: {1}", isrc.Key, isrc.Value); } } if (scsiInfo.XboxSecuritySector != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_xbox_ss.bin", "KREON EXTRACT SS", scsiInfo.XboxSecuritySector); } if (scsiInfo.DecodedXboxSecuritySector.HasValue) { AaruConsole.WriteLine("Xbox Security Sector:\n{0}", SS.Prettify(scsiInfo.DecodedXboxSecuritySector)); } if (scsiInfo.XgdInfo != null) { AaruConsole.WriteLine("Video layer 0 size: {0} sectors", scsiInfo.XgdInfo.L0Video); AaruConsole.WriteLine("Video layer 1 size: {0} sectors", scsiInfo.XgdInfo.L1Video); AaruConsole.WriteLine("Middle zone size: {0} sectors", scsiInfo.XgdInfo.MiddleZone); AaruConsole.WriteLine("Game data size: {0} sectors", scsiInfo.XgdInfo.GameSize); AaruConsole.WriteLine("Total size: {0} sectors", scsiInfo.XgdInfo.TotalSize); AaruConsole.WriteLine("Real layer break: {0}", scsiInfo.XgdInfo.LayerBreak); AaruConsole.WriteLine(); } } if (scsiInfo.MediaSerialNumber != null) { DataFile.WriteTo("Media-Info command", outputPrefix, "_mediaserialnumber.bin", "SCSI READ MEDIA SERIAL NUMBER", scsiInfo.MediaSerialNumber); AaruConsole.Write("Media Serial Number: "); for (int i = 4; i < scsiInfo.MediaSerialNumber.Length; i++) { AaruConsole.Write("{0:X2}", scsiInfo.MediaSerialNumber[i]); } AaruConsole.WriteLine(); } AaruConsole.WriteLine("Media identified as {0}", scsiInfo.MediaType); Statistics.AddMedia(scsiInfo.MediaType, true); if (scsiInfo.Toc != null || scsiInfo.RawToc != null) { uint blockSize = 2352; Track[] tracks = Dump.GetCdTracks(ref blockSize, dev, scsiInfo.MediaType, null, false, out long lastSector, null, null, null, TrackSubchannelType.None, out _, null, null); if (tracks != null) { bool supportsPqSubchannel = Dump.SupportsPqSubchannel(dev, null, null); bool supportsRwSubchannel = Dump.SupportsRwSubchannel(dev, null, null); // Open master database var ctx = AaruContext.Create(Settings.Settings.MasterDbPath); // Search for device in master database Database.Models.Device dbDev = ctx.Devices.FirstOrDefault(d => d.Manufacturer == dev.Manufacturer && d.Model == dev.Model && d.Revision == dev.FirmwareRevision); Dump.SolveTrackPregaps(dev, null, null, tracks, supportsPqSubchannel, supportsRwSubchannel, dbDev, out bool inexactPositioning); for (int t = 1; t < tracks.Length; t++) { tracks[t - 1].TrackEndSector = tracks[t].TrackStartSector - 1; } tracks[tracks.Length - 1].TrackEndSector = (ulong)lastSector; AaruConsole.WriteLine(); AaruConsole.WriteLine("Track calculations:"); if (inexactPositioning) { AaruConsole. WriteLine("WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect."); } foreach (Track track in tracks) { AaruConsole. WriteLine("Track {0} starts at LBA {1}, ends at LBA {2}, has a pregap of {3} sectors and is of type {4}", track.TrackSequence, track.TrackStartSector, track.TrackEndSector, track.TrackPregap, track.TrackType); } AaruConsole.WriteLine(); AaruConsole.WriteLine("Offsets:"); CdOffset cdOffset = null; // Search for read offset in master database cdOffset = ctx.CdOffsets.FirstOrDefault(d => d.Manufacturer == dev.Manufacturer && d.Model == dev.Model); CompactDisc.GetOffset(cdOffset, dbDev, debug, dev, scsiInfo.MediaType, null, tracks, null, out int?driveOffset, out int?combinedOffset, out _); if (combinedOffset is null) { if (driveOffset is null) { AaruConsole.WriteLine("Drive reading offset not found in database."); AaruConsole.WriteLine("Disc offset cannot be calculated."); } else { AaruConsole. WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); AaruConsole.WriteLine("Disc write offset is unknown."); } } else { int offsetBytes = combinedOffset.Value; if (driveOffset is null) { AaruConsole.WriteLine("Drive reading offset not found in database."); AaruConsole. WriteLine($"Combined disc and drive offset are {offsetBytes} bytes ({offsetBytes / 4} samples)."); } else { AaruConsole. WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); AaruConsole. WriteLine($"Combined offset is {offsetBytes} bytes ({offsetBytes / 4} samples)"); int?discOffset = offsetBytes - driveOffset; AaruConsole.WriteLine($"Disc offset is {discOffset} bytes ({discOffset / 4} samples)"); } } } } dev.Close(); }
/// <summary>Starts dumping with the stablished fields and autodetecting the device type</summary> public void Start() { // Open master database _ctx = DicContext.Create(Settings.Settings.MasterDbPath); // Search for device in master database _dbDev = _ctx.Devices.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model && d.Revision == _dev.Revision); if (_dbDev is null) { _dumpLog.WriteLine("Device not in database, please create a device report and attach it to a Github issue."); UpdateStatus?. Invoke("Device not in database, please create a device report and attach it to a Github issue."); } else { _dumpLog.WriteLine($"Device in database since {_dbDev.LastSynchronized}."); UpdateStatus?.Invoke($"Device in database since {_dbDev.LastSynchronized}."); if (_dbDev.OptimalMultipleSectorsRead > 0) { _maximumReadable = (uint)_dbDev.OptimalMultipleSectorsRead; } } if (_dev.IsUsb && _dev.UsbVendorId == 0x054C && (_dev.UsbProductId == 0x01C8 || _dev.UsbProductId == 0x01C9 || _dev.UsbProductId == 0x02D2)) { PlayStationPortable(); } else { switch (_dev.Type) { case DeviceType.ATA: Ata(); break; case DeviceType.MMC: case DeviceType.SecureDigital: SecureDigital(); break; case DeviceType.NVMe: NVMe(); break; case DeviceType.ATAPI: case DeviceType.SCSI: Scsi(); break; default: _dumpLog.WriteLine("Unknown device type."); _dumpLog.Close(); StoppingErrorMessage?.Invoke("Unknown device type."); return; } } _dumpLog.Close(); if (_resume == null || !_doResume) { return; } _resume.LastWriteDate = DateTime.UtcNow; _resume.BadBlocks.Sort(); if (File.Exists(_outputPrefix + ".resume.xml")) { File.Delete(_outputPrefix + ".resume.xml"); } var fs = new FileStream(_outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite); var xs = new XmlSerializer(_resume.GetType()); xs.Serialize(fs, _resume); fs.Close(); }
public static void SolveTrackPregaps(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel, Database.Models.Device dbDev, out bool inexactPositioning) { bool sense; // Sense indicator byte[] subBuf; int posQ; uint retries; bool? bcd = null; byte[] crc; Dictionary <uint, int> pregaps = new Dictionary <uint, int>(); inexactPositioning = false; if (!supportsPqSubchannel && !supportsRwSubchannel) { return; } // Check if subchannel is BCD for (retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf) : GetSectorForPregapQ16(dev, 11, dbDev, out subBuf); if (sense) { continue; } bcd = (subBuf[9] & 0x10) > 0; break; } if (bcd is null) { dumpLog?.WriteLine("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); updateStatus?. Invoke("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); return; } // Initialize the dictionary for (int i = 0; i < tracks.Length; i++) { pregaps[tracks[i].TrackSequence] = 0; } foreach (Track track in tracks) { if (track.TrackSequence <= 1) { continue; } int lba = (int)track.TrackStartSector - 1; bool pregapFound = false; Track previousTrack = tracks.FirstOrDefault(t => t.TrackSequence == track.TrackSequence - 1); bool goneBack = false; bool goFront = false; // Check if pregap is 0 for (retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf) : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf); if (sense) { continue; } if (bcd == false) { BinaryToBcdQ(subBuf); } CRC16CCITTContext.Data(subBuf, 10, out crc); if (crc[0] != subBuf[10] || crc[1] != subBuf[11]) { continue; } BcdToBinaryQ(subBuf); // Q position if ((subBuf[0] & 0xF) != 1) { continue; } posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150; if (subBuf[1] != track.TrackSequence - 1 || subBuf[2] == 0 || posQ != lba) { break; } pregaps[track.TrackSequence] = 0; pregapFound = true; } if (pregapFound) { continue; } // Calculate pregap lba = (int)track.TrackStartSector - 150; while (lba > (int)previousTrack.TrackStartSector) { // Some drives crash if you try to read just before the previous read, so seek away first sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba - 10, dbDev, out subBuf) : GetSectorForPregapQ16(dev, (uint)lba - 10, dbDev, out subBuf); for (retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf) : GetSectorForPregapQ16(dev, (uint)lba, dbDev, out subBuf); if (sense) { continue; } if (bcd == false) { BinaryToBcdQ(subBuf); } CRC16CCITTContext.Data(subBuf, 10, out crc); if (crc[0] == subBuf[10] && crc[1] == subBuf[11]) { break; } } if (retries == 10) { dumpLog?.WriteLine($"Could not get correct subchannel for sector {lba}"); updateStatus?.Invoke($"Could not get correct subchannel for sector {lba}"); } BcdToBinaryQ(subBuf); // If it's not Q position if ((subBuf[0] & 0xF) != 1) { // This means we already searched back, so search forward if (goFront) { lba++; if (lba == (int)previousTrack.TrackStartSector) { break; } continue; } // Search back goneBack = true; lba--; continue; } // Previous track if (subBuf[1] < track.TrackSequence) { lba++; // Already gone back, so go forward if (goneBack) { goFront = true; } continue; } // Same track, but not pregap if (subBuf[1] == track.TrackSequence && subBuf[2] > 0) { lba--; continue; } // Pregap according to Q position int pregapQ = (subBuf[3] * 60 * 75) + (subBuf[4] * 75) + subBuf[5] + 1; posQ = ((subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9]) - 150; int diff = posQ - lba; if (diff != 0) { inexactPositioning = true; } // Bigger than known change, otherwise we found it if (pregapQ > pregaps[track.TrackSequence]) { pregaps[track.TrackSequence] = pregapQ; } else if (pregapQ == pregaps[track.TrackSequence]) { break; } lba--; } } for (int i = 0; i < tracks.Length; i++) { tracks[i].TrackPregap = (ulong)pregaps[tracks[i].TrackSequence]; tracks[i].TrackStartSector -= tracks[i].TrackPregap; #if DEBUG dumpLog?.WriteLine($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors"); updateStatus?.Invoke($"Track {tracks[i].TrackSequence} pregap is {tracks[i].TrackPregap} sectors"); #endif } }