Add support for dumping data tracks with OmniDrive units.

This commit is contained in:
2026-07-05 02:19:28 +01:00
parent 0734705ac4
commit ff3e84c7de
6 changed files with 184 additions and 149 deletions

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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];

View File

@@ -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;