mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-07-08 18:16:24 +00:00
Add support for dumping data tracks with OmniDrive units.
This commit is contained in:
@@ -675,9 +675,8 @@ sealed partial class Dump
|
||||
foreach(int sub in _resume.BadSubchannels) subchannelExtents.Add(sub);
|
||||
|
||||
if(_resume.NextBlock < blocks)
|
||||
{
|
||||
for(ulong i = _resume.NextBlock; i < blocks; i++) subchannelExtents.Add((int)i);
|
||||
}
|
||||
for(ulong i = _resume.NextBlock; i < blocks; i++)
|
||||
subchannelExtents.Add((int)i);
|
||||
}
|
||||
|
||||
if(_resume.NextBlock > 0)
|
||||
@@ -1166,9 +1165,8 @@ sealed partial class Dump
|
||||
supportsLongSectors);
|
||||
|
||||
foreach(Tuple<ulong, ulong> leadoutExtent in leadOutExtents.ToArray())
|
||||
{
|
||||
for(ulong e = leadoutExtent.Item1; e <= leadoutExtent.Item2; e++) subchannelExtents.Remove((int)e);
|
||||
}
|
||||
for(ulong e = leadoutExtent.Item1; e <= leadoutExtent.Item2; e++)
|
||||
subchannelExtents.Remove((int)e);
|
||||
|
||||
if(subchannelExtents.Count > 0 && _retryPasses > 0 && _retrySubchannel)
|
||||
{
|
||||
@@ -1345,6 +1343,12 @@ sealed partial class Dump
|
||||
ByteSize.FromMegabytes(minSpeed).Per(_oneSecond).Humanize()));
|
||||
}
|
||||
|
||||
if(_omnidrive)
|
||||
{
|
||||
UpdateStatus?.Invoke($"{_correctSectors} sectors were correct when read.");
|
||||
UpdateStatus?.Invoke($"{_fixedSectors} sectors had to be fixed.");
|
||||
}
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core._0_sectors_could_not_be_read, _resume.BadBlocks.Count));
|
||||
|
||||
UpdateStatus?.Invoke(string.Format(Localization.Core._0_subchannels_could_not_be_read,
|
||||
|
||||
@@ -288,7 +288,7 @@ partial class Dump
|
||||
byte sectorsToReRead = 1;
|
||||
var badSectorToReRead = (uint)badSector;
|
||||
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
if((_fixOffset && audioExtents.Contains(badSector) || _omnidrive) && offsetBytes != 0)
|
||||
{
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
@@ -303,7 +303,6 @@ partial class Dump
|
||||
|
||||
if(_omnidrive)
|
||||
{
|
||||
// TODO: Data
|
||||
sense = _dev.OmniDriveReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToReRead,
|
||||
@@ -507,13 +506,12 @@ partial class Dump
|
||||
|
||||
// MEDIUM ERROR, retry with ignore error below
|
||||
if(decSense is { ASC: 0x11 })
|
||||
{
|
||||
if(!sectorsNotEvenPartial.Contains(badSector)) sectorsNotEvenPartial.Add(badSector);
|
||||
}
|
||||
if(!sectorsNotEvenPartial.Contains(badSector))
|
||||
sectorsNotEvenPartial.Add(badSector);
|
||||
}
|
||||
|
||||
// Because one block has been partially used to fix the offset
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
if((_fixOffset && audioExtents.Contains(badSector) || _omnidrive) && offsetBytes != 0)
|
||||
{
|
||||
uint blocksToRead = sectorsToReRead;
|
||||
|
||||
@@ -533,7 +531,30 @@ partial class Dump
|
||||
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
if(!audioExtents.Contains(badSector) && _paranoia)
|
||||
if(_omnidrive)
|
||||
{
|
||||
var sector = new byte[sectorSize];
|
||||
Array.Copy(cmdBuf, 0, sector, 0, sectorSize);
|
||||
|
||||
if(IsScrambledData(sector, (int)badSector, out _))
|
||||
{
|
||||
sector = Sector.Scramble(sector);
|
||||
SectorFixResult fixStatus = CdChecksums.FixSector(sector);
|
||||
if(fixStatus == SectorFixResult.Correct) _correctSectors++;
|
||||
if(fixStatus == SectorFixResult.Fixed) _fixedSectors++;
|
||||
|
||||
if(fixStatus != SectorFixResult.CouldNotFix)
|
||||
{
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
sectorsNotEvenPartial.Remove(badSector);
|
||||
}
|
||||
|
||||
Array.Copy(sector, 0, cmdBuf, 0, sectorSize);
|
||||
}
|
||||
}
|
||||
else if(!audioExtents.Contains(badSector) && _paranoia)
|
||||
{
|
||||
var sector = new byte[sectorSize];
|
||||
Array.Copy(cmdBuf, 0, sector, 0, sectorSize);
|
||||
|
||||
@@ -34,6 +34,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Aaru.Checksums;
|
||||
using Aaru.CommonTypes.AaruMetadata;
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.CommonTypes.Extents;
|
||||
@@ -148,17 +149,14 @@ partial class Dump
|
||||
continue;
|
||||
}
|
||||
|
||||
if(_fixOffset)
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
if(i == 0)
|
||||
firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
|
||||
else
|
||||
firstSectorToRead -= (uint)sectorsForOffset;
|
||||
if(i == 0)
|
||||
firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1
|
||||
else
|
||||
firstSectorToRead -= (uint)sectorsForOffset;
|
||||
|
||||
if(blocksToRead <= sectorsForOffset) blocksToRead += (uint)sectorsForOffset;
|
||||
}
|
||||
if(blocksToRead <= sectorsForOffset) blocksToRead += (uint)sectorsForOffset;
|
||||
}
|
||||
|
||||
if(speedSectorCounter > 1000)
|
||||
@@ -195,7 +193,7 @@ partial class Dump
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
// Because one block has been partially used to fix the offset
|
||||
if(_fixOffset && offsetBytes != 0)
|
||||
if(offsetBytes != 0)
|
||||
{
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
@@ -213,126 +211,114 @@ partial class Dump
|
||||
extents.Add(i, blocksToRead, true);
|
||||
_writeStopwatch.Restart();
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
var data = new byte[sectorSize * blocksToRead];
|
||||
var sub = new byte[subSize * blocksToRead];
|
||||
var sectorStatus = new SectorStatus[blocksToRead];
|
||||
var sector = new byte[sectorSize];
|
||||
List<ulong> paintBad = [];
|
||||
|
||||
for(var b = 0; b < blocksToRead; b++)
|
||||
{
|
||||
var data = new byte[sectorSize * blocksToRead];
|
||||
var sub = new byte[subSize * blocksToRead];
|
||||
var sectorStatus = new SectorStatus[blocksToRead];
|
||||
var sector = new byte[sectorSize];
|
||||
Array.Copy(cmdBuf, (int)(0 + b * blockSize), sector, 0, sectorSize);
|
||||
|
||||
if(IsScrambledData(sector, (int)(i + (ulong)b), out _))
|
||||
{
|
||||
sector = Sector.Scramble(sector);
|
||||
SectorFixResult fixStatus = CdChecksums.FixSector(sector);
|
||||
if(fixStatus == SectorFixResult.Correct) _correctSectors++;
|
||||
if(fixStatus == SectorFixResult.Fixed) _fixedSectors++;
|
||||
|
||||
if(fixStatus == SectorFixResult.CouldNotFix)
|
||||
{
|
||||
sectorStatus[b] = SectorStatus.Errored;
|
||||
_resume.BadBlocks.Add(i + (ulong)b);
|
||||
paintBad.Add(i + (ulong)b);
|
||||
}
|
||||
|
||||
Array.Copy(sector, 0, cmdBuf, (int)(0 + b * blockSize), sectorSize);
|
||||
}
|
||||
|
||||
Array.Copy(cmdBuf, (int)(0 + b * blockSize), data, sectorSize * b, sectorSize);
|
||||
|
||||
Array.Copy(cmdBuf, (int)(sectorSize + b * blockSize), sub, subSize * b, subSize);
|
||||
|
||||
// Not already set
|
||||
if(sectorStatus[b] != SectorStatus.Errored) sectorStatus[b] = SectorStatus.Dumped;
|
||||
}
|
||||
|
||||
if(supportsLongSectors)
|
||||
outputFormat.WriteSectorsLong(data, i, false, blocksToRead, sectorStatus);
|
||||
else
|
||||
{
|
||||
var cooked = new MemoryStream();
|
||||
|
||||
for(var b = 0; b < blocksToRead; b++)
|
||||
{
|
||||
Array.Copy(cmdBuf, (int)(0 + b * blockSize), data, sectorSize * b, sectorSize);
|
||||
|
||||
Array.Copy(cmdBuf, (int)(sectorSize + b * blockSize), sub, subSize * b, subSize);
|
||||
|
||||
Array.Copy(cmdBuf, (int)(0 + b * blockSize), sector, 0, sectorSize);
|
||||
|
||||
sectorStatus[b] = SectorStatus.Dumped;
|
||||
byte[] cookedSector = Sector.GetUserData(sector);
|
||||
cooked.Write(cookedSector, 0, cookedSector.Length);
|
||||
}
|
||||
|
||||
if(supportsLongSectors)
|
||||
outputFormat.WriteSectorsLong(data, i, false, blocksToRead, sectorStatus);
|
||||
else
|
||||
{
|
||||
var cooked = new MemoryStream();
|
||||
|
||||
for(var b = 0; b < blocksToRead; b++)
|
||||
{
|
||||
Array.Copy(cmdBuf, (int)(0 + b * blockSize), sector, 0, sectorSize);
|
||||
byte[] cookedSector = Sector.GetUserData(sector);
|
||||
cooked.Write(cookedSector, 0, cookedSector.Length);
|
||||
}
|
||||
|
||||
outputFormat.WriteSectors(cooked.ToArray(), i, false, blocksToRead, sectorStatus);
|
||||
}
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
i,
|
||||
blocksToRead,
|
||||
subLog,
|
||||
isrcs,
|
||||
(byte)track.Sequence,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition,
|
||||
outputFormat as IWritableOpticalImage,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out List<ulong> newPregapSectors);
|
||||
|
||||
// Set tracks and go back
|
||||
if(indexesChanged)
|
||||
{
|
||||
(outputFormat as IWritableOpticalImage).SetTracks([.. tracks]);
|
||||
|
||||
foreach(ulong newPregapSector in newPregapSectors)
|
||||
{
|
||||
if(newPregapSector == 0) continue;
|
||||
|
||||
Track sectorTrack =
|
||||
tracks.FirstOrDefault(t => t.StartSector <= newPregapSector &&
|
||||
t.EndSector >= newPregapSector);
|
||||
|
||||
if(sectorTrack.Sequence == 1) continue;
|
||||
|
||||
Track prevTrack = tracks.FirstOrDefault(t => t.Sequence == sectorTrack.Sequence - 1);
|
||||
|
||||
if(sectorTrack.Session != prevTrack.Session) continue;
|
||||
|
||||
if(sectorTrack.Type != prevTrack.Type) _resume.BadBlocks.Add(newPregapSector);
|
||||
}
|
||||
|
||||
if(i >= blocksToRead)
|
||||
i -= blocksToRead;
|
||||
else
|
||||
i = 0;
|
||||
|
||||
if(i > 0) i--;
|
||||
|
||||
foreach(Track aTrack in tracks.Where(static aTrack => aTrack.Type == TrackType.Audio))
|
||||
audioExtents.Add(aTrack.StartSector, aTrack.EndSector);
|
||||
|
||||
continue;
|
||||
}
|
||||
outputFormat.WriteSectors(cooked.ToArray(), i, false, blocksToRead, sectorStatus);
|
||||
}
|
||||
else
|
||||
|
||||
bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel,
|
||||
desiredSubchannel,
|
||||
sub,
|
||||
i,
|
||||
blocksToRead,
|
||||
subLog,
|
||||
isrcs,
|
||||
(byte)track.Sequence,
|
||||
ref mcn,
|
||||
tracks,
|
||||
subchannelExtents,
|
||||
_fixSubchannelPosition,
|
||||
outputFormat as IWritableOpticalImage,
|
||||
_fixSubchannel,
|
||||
_fixSubchannelCrc,
|
||||
UpdateStatus,
|
||||
smallestPregapLbaPerTrack,
|
||||
true,
|
||||
out List<ulong> newPregapSectors);
|
||||
|
||||
// Set tracks and go back
|
||||
if(indexesChanged)
|
||||
{
|
||||
if(supportsLongSectors)
|
||||
(outputFormat as IWritableOpticalImage).SetTracks([.. tracks]);
|
||||
|
||||
foreach(ulong newPregapSector in newPregapSectors)
|
||||
{
|
||||
outputFormat.WriteSectorsLong(cmdBuf,
|
||||
i,
|
||||
false,
|
||||
blocksToRead,
|
||||
[.. Enumerable.Repeat(SectorStatus.Dumped, (int)blocksToRead)]);
|
||||
if(newPregapSector == 0) continue;
|
||||
|
||||
Track sectorTrack =
|
||||
tracks.FirstOrDefault(t => t.StartSector <= newPregapSector &&
|
||||
t.EndSector >= newPregapSector);
|
||||
|
||||
if(sectorTrack.Sequence == 1) continue;
|
||||
|
||||
Track prevTrack = tracks.FirstOrDefault(t => t.Sequence == sectorTrack.Sequence - 1);
|
||||
|
||||
if(sectorTrack.Session != prevTrack.Session) continue;
|
||||
|
||||
if(sectorTrack.Type != prevTrack.Type) _resume.BadBlocks.Add(newPregapSector);
|
||||
}
|
||||
|
||||
if(i >= blocksToRead)
|
||||
i -= blocksToRead;
|
||||
else
|
||||
{
|
||||
var cooked = new MemoryStream();
|
||||
var sector = new byte[sectorSize];
|
||||
i = 0;
|
||||
|
||||
for(var b = 0; b < blocksToRead; b++)
|
||||
{
|
||||
Array.Copy(cmdBuf, (int)(b * sectorSize), sector, 0, sectorSize);
|
||||
byte[] cookedSector = Sector.GetUserData(sector);
|
||||
cooked.Write(cookedSector, 0, cookedSector.Length);
|
||||
}
|
||||
if(i > 0) i--;
|
||||
|
||||
outputFormat.WriteSectors(cooked.ToArray(),
|
||||
i,
|
||||
false,
|
||||
blocksToRead,
|
||||
[.. Enumerable.Repeat(SectorStatus.Dumped, (int)blocksToRead)]);
|
||||
}
|
||||
foreach(Track aTrack in tracks.Where(static aTrack => aTrack.Type == TrackType.Audio))
|
||||
audioExtents.Add(aTrack.StartSector, aTrack.EndSector);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
_mediaGraph?.PaintSectorsGood(i, blocksToRead);
|
||||
foreach(ulong b in paintBad) _mediaGraph?.PaintSectorsBad(b, 1);
|
||||
|
||||
imageWriteDuration += _writeStopwatch.Elapsed.TotalSeconds;
|
||||
}
|
||||
|
||||
@@ -188,6 +188,8 @@ partial class Dump
|
||||
|
||||
_speedStopwatch.Stop();
|
||||
|
||||
uint blocksToRead = 150;
|
||||
|
||||
// Apply offset correction if any audio sector is present in the pregap range
|
||||
if(needsOffsetCorrection || _omnidrive && offsetBytes != 0)
|
||||
{
|
||||
@@ -202,7 +204,7 @@ partial class Dump
|
||||
|
||||
if(hasAudioSectors || _omnidrive)
|
||||
{
|
||||
var blocksToRead = (uint)totalSectorsToRead;
|
||||
blocksToRead = (uint)totalSectorsToRead;
|
||||
|
||||
FixOffsetData(offsetBytes,
|
||||
2352,
|
||||
@@ -221,9 +223,9 @@ partial class Dump
|
||||
// starting at index 0, regardless of whether the offset was positive or negative.
|
||||
var firstSectorWritten = false;
|
||||
|
||||
for(int firstTrackPregapBlock = -150; firstTrackPregapBlock <= -1; firstTrackPregapBlock++)
|
||||
for(var firstTrackPregapBlock = (int)-blocksToRead; firstTrackPregapBlock <= -1; firstTrackPregapBlock++)
|
||||
{
|
||||
int bufferIndex = firstTrackPregapBlock + 150; // 0 = LBA -150, 149 = LBA -1
|
||||
var bufferIndex = (int)(firstTrackPregapBlock + blocksToRead); // 0 = LBA -150, 149 = LBA -1
|
||||
|
||||
// Map back to the pre-correction read index to check whether the read succeeded.
|
||||
// For negative offset the extra sectors were prepended, so index shifts by sectorsForOffset.
|
||||
|
||||
@@ -129,7 +129,7 @@ partial class Dump
|
||||
byte sectorsToTrim = 1;
|
||||
var badSectorToRead = (uint)badSector;
|
||||
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
if((_fixOffset && audioExtents.Contains(badSector) || _omnidrive) && offsetBytes != 0)
|
||||
{
|
||||
if(offsetBytes < 0)
|
||||
{
|
||||
@@ -144,7 +144,6 @@ partial class Dump
|
||||
|
||||
if(_omnidrive)
|
||||
{
|
||||
// TODO: Data
|
||||
sense = _dev.OmniDriveReadCd(out cmdBuf,
|
||||
out senseBuf,
|
||||
badSectorToRead,
|
||||
@@ -404,11 +403,49 @@ partial class Dump
|
||||
continue;
|
||||
}
|
||||
|
||||
// Because one block has been partially used to fix the offset
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
{
|
||||
uint blocksToRead = sectorsToTrim;
|
||||
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
sectorsForOffset,
|
||||
supportedSubchannel,
|
||||
ref blocksToRead,
|
||||
subSize,
|
||||
ref cmdBuf,
|
||||
blockSize,
|
||||
false);
|
||||
}
|
||||
|
||||
SectorStatus sectorStatus = SectorStatus.Dumped;
|
||||
|
||||
if(!sense && !_dev.Error)
|
||||
{
|
||||
if(!audioExtents.Contains(badSector) && _paranoia)
|
||||
if(_omnidrive)
|
||||
{
|
||||
var sector = new byte[sectorSize];
|
||||
Array.Copy(cmdBuf, 0, sector, 0, sectorSize);
|
||||
|
||||
if(IsScrambledData(sector, (int)badSector, out _))
|
||||
{
|
||||
sector = Sector.Scramble(sector);
|
||||
SectorFixResult fixStatus = CdChecksums.FixSector(sector);
|
||||
if(fixStatus == SectorFixResult.Correct) _correctSectors++;
|
||||
if(fixStatus == SectorFixResult.Fixed) _fixedSectors++;
|
||||
|
||||
if(fixStatus != SectorFixResult.CouldNotFix)
|
||||
{
|
||||
_resume.BadBlocks.Remove(badSector);
|
||||
extents.Add(badSector);
|
||||
_mediaGraph?.PaintSectorGood(badSector);
|
||||
}
|
||||
|
||||
Array.Copy(sector, 0, cmdBuf, 0, sectorSize);
|
||||
}
|
||||
}
|
||||
else if(!audioExtents.Contains(badSector) && _paranoia)
|
||||
{
|
||||
var sector = new byte[sectorSize];
|
||||
Array.Copy(cmdBuf, 0, sector, 0, sectorSize);
|
||||
@@ -448,23 +485,6 @@ partial class Dump
|
||||
}
|
||||
}
|
||||
|
||||
// Because one block has been partially used to fix the offset
|
||||
if(_fixOffset && audioExtents.Contains(badSector) && offsetBytes != 0)
|
||||
{
|
||||
uint blocksToRead = sectorsToTrim;
|
||||
|
||||
FixOffsetData(offsetBytes,
|
||||
sectorSize,
|
||||
sectorsForOffset,
|
||||
supportedSubchannel,
|
||||
ref blocksToRead,
|
||||
subSize,
|
||||
ref cmdBuf,
|
||||
blockSize,
|
||||
false);
|
||||
}
|
||||
|
||||
|
||||
if(supportedSubchannel != MmcSubchannel.None)
|
||||
{
|
||||
var data = new byte[sectorSize];
|
||||
|
||||
@@ -113,10 +113,12 @@ public partial class Dump
|
||||
readonly Stopwatch _trimStopwatch;
|
||||
readonly Stopwatch _writeStopwatch;
|
||||
bool _aborted;
|
||||
int _correctSectors;
|
||||
AaruContext _ctx; // Main database context
|
||||
Database.Models.Device _dbDev; // Device database entry
|
||||
bool _dumpFirstTrackPregap;
|
||||
bool _fixOffset;
|
||||
int _fixedSectors;
|
||||
uint _maximumReadable; // Maximum number of sectors drive can read at once
|
||||
IMediaGraph _mediaGraph;
|
||||
bool _missingTitleKeysDirty;
|
||||
|
||||
Reference in New Issue
Block a user