Files
Aaru/Aaru.Images/A2R/Read.cs

504 lines
20 KiB
C#
Raw Normal View History

2023-07-06 00:04:46 +02:00
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Read.cs
// Author(s) : Rebecca Wallander <sakcheen@gmail.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Reads A2R flux images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2023 Rebecca Wallander
// ****************************************************************************/
using System;
using System.Collections.Generic;
2023-10-05 01:05:23 +01:00
using System.Diagnostics.CodeAnalysis;
2023-07-06 00:04:46 +02:00
using System.IO;
using System.Linq;
using System.Text;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.Console;
using Aaru.Helpers;
namespace Aaru.Images;
2023-07-06 00:04:46 +02:00
2023-10-05 01:05:23 +01:00
[SuppressMessage("ReSharper", "UnusedType.Global")]
2023-07-06 00:04:46 +02:00
public sealed partial class A2R
{
2023-10-03 23:34:59 +01:00
#region IFluxImage Members
2023-07-06 00:04:46 +02:00
/// <inheritdoc />
public ErrorNumber Open(IFilter imageFilter)
{
2023-10-05 01:52:48 +01:00
_a2RStream = imageFilter.GetDataForkStream();
_a2RStream.Seek(0, SeekOrigin.Begin);
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
_a2RFilter = imageFilter;
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
var hdr = new byte[Marshal.SizeOf<A2RHeader>()];
_a2RStream.EnsureRead(hdr, 0, Marshal.SizeOf<A2RHeader>());
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
_header = Marshal.ByteArrayToStructureLittleEndian<A2RHeader>(hdr);
2023-07-06 00:04:46 +02:00
AaruConsole.DebugWriteLine(MODULE_NAME, "header.signature = \"{0}\"",
2023-10-05 01:52:48 +01:00
StringHandlers.CToString(_header.signature));
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
AaruConsole.DebugWriteLine(MODULE_NAME, "header.version = {0}", _header.version);
AaruConsole.DebugWriteLine(MODULE_NAME, "header.highBitTest = {0:X2}", _header.highBitTest);
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
AaruConsole.DebugWriteLine(MODULE_NAME, "header.lineTest = {0:X2} {1:X2} {2:X2}", _header.lineTest[0],
_header.lineTest[1], _header.lineTest[2]);
2023-07-06 00:04:46 +02:00
2023-10-03 23:34:59 +01:00
var infoMagic = new byte[4];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(infoMagic, 0, 4);
2023-07-06 00:04:46 +02:00
// There must be an INFO chunk after the header (at byte 16)
if(!_infoChunkSignature.SequenceEqual(infoMagic))
return ErrorNumber.UnrecognizedFormat;
2023-10-05 01:52:48 +01:00
_a2RStream.Seek(-4, SeekOrigin.Current);
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
switch(_header.version)
2023-07-06 00:04:46 +02:00
{
case 0x32:
{
2023-10-03 23:34:59 +01:00
var infoChnk = new byte[Marshal.SizeOf<InfoChunkV2>()];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(infoChnk, 0, Marshal.SizeOf<InfoChunkV2>());
2023-07-06 00:04:46 +02:00
_infoChunkV2 = Marshal.ByteArrayToStructureLittleEndian<InfoChunkV2>(infoChnk);
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.header.chunkId = \"{0}\"",
2023-07-06 00:04:46 +02:00
StringHandlers.CToString(_infoChunkV2.header.chunkId));
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.header.chunkSize = {0}",
2023-07-06 00:04:46 +02:00
_infoChunkV2.header.chunkSize);
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.version = {0}", _infoChunkV2.version);
2023-07-06 00:04:46 +02:00
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.creator = \"{0}\"",
2023-07-06 00:04:46 +02:00
StringHandlers.CToString(_infoChunkV2.creator).TrimEnd());
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.diskType = {0}", _infoChunkV2.diskType);
2023-07-06 00:04:46 +02:00
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.writeProtected = {0}", _infoChunkV2.writeProtected);
2023-07-06 00:04:46 +02:00
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.synchronized = {0}", _infoChunkV2.synchronized);
2023-07-06 00:04:46 +02:00
_imageInfo.Creator = Encoding.ASCII.GetString(_infoChunkV2.creator).TrimEnd();
switch(_infoChunkV2.diskType)
{
2023-10-05 01:52:48 +01:00
case A2RDiskType._35:
2023-07-06 00:04:46 +02:00
_imageInfo.Heads = 2;
_imageInfo.Cylinders = 80;
_imageInfo.MediaType = MediaType.AppleSonyDS;
_imageInfo.SectorsPerTrack = 10;
break;
2023-10-05 01:52:48 +01:00
case A2RDiskType._525:
2023-07-06 00:04:46 +02:00
_imageInfo.Heads = 1;
_imageInfo.Cylinders = 40;
_imageInfo.MediaType = MediaType.Apple32SS;
break;
2023-10-03 23:34:59 +01:00
default:
return ErrorNumber.OutOfRange;
2023-07-06 00:04:46 +02:00
}
break;
}
case 0x33:
{
2023-10-03 23:34:59 +01:00
var infoChk = new byte[Marshal.SizeOf<InfoChunkV3>()];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(infoChk, 0, Marshal.SizeOf<InfoChunkV3>());
2023-07-06 00:04:46 +02:00
_infoChunkV3 = Marshal.ByteArrayToStructureLittleEndian<InfoChunkV3>(infoChk);
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.header.chunkId = \"{0}\"",
2023-07-06 00:04:46 +02:00
StringHandlers.CToString(_infoChunkV3.header.chunkId));
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.header.chunkSize = {0}",
2023-07-06 00:04:46 +02:00
_infoChunkV3.header.chunkSize);
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.version = {0}", _infoChunkV3.version);
2023-07-06 00:04:46 +02:00
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.creator = \"{0}\"",
2023-07-06 00:04:46 +02:00
StringHandlers.CToString(_infoChunkV3.creator).TrimEnd());
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.driveType = {0}", _infoChunkV3.driveType);
2023-07-06 00:04:46 +02:00
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.writeProtected = {0}", _infoChunkV3.writeProtected);
2023-07-06 00:04:46 +02:00
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.synchronized = {0}", _infoChunkV3.synchronized);
2023-07-06 00:04:46 +02:00
AaruConsole.DebugWriteLine(MODULE_NAME, "infoChunk.hardSectorCount = {0}",
2023-07-06 00:04:46 +02:00
_infoChunkV3.hardSectorCount);
_imageInfo.Creator = Encoding.ASCII.GetString(_infoChunkV3.creator).TrimEnd();
switch(_infoChunkV3.driveType)
{
case A2rDriveType.SS_525_40trk_quarterStep:
_imageInfo.Heads = 1;
_imageInfo.Cylinders = 40;
_imageInfo.MediaType = MediaType.Apple32SS;
break;
case A2rDriveType.DS_35_80trk_appleCLV:
_imageInfo.Heads = 2;
_imageInfo.Cylinders = 80;
_imageInfo.MediaType = MediaType.AppleSonyDS;
_imageInfo.SectorsPerTrack = 10;
break;
case A2rDriveType.DS_525_80trk:
_imageInfo.Heads = 2;
_imageInfo.Cylinders = 80;
_imageInfo.MediaType = MediaType.DOS_525_HD;
break;
case A2rDriveType.DS_525_40trk:
_imageInfo.Heads = 2;
_imageInfo.Cylinders = 40;
_imageInfo.MediaType = MediaType.DOS_525_DS_DD_9;
_imageInfo.SectorsPerTrack = 9;
break;
case A2rDriveType.DS_35_80trk:
_imageInfo.Heads = 2;
_imageInfo.Cylinders = 80;
_imageInfo.MediaType = MediaType.DOS_35_HD;
_imageInfo.SectorsPerTrack = 18;
break;
case A2rDriveType.DS_8:
_imageInfo.Heads = 2;
_imageInfo.Cylinders = 40;
break;
2023-10-03 23:34:59 +01:00
default:
return ErrorNumber.OutOfRange;
2023-07-06 00:04:46 +02:00
}
break;
}
}
2023-10-05 01:52:48 +01:00
_a2RCaptures = new List<StreamCapture>();
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
while(_a2RStream.Position < _a2RStream.Length)
2023-07-06 00:04:46 +02:00
{
2023-10-03 23:34:59 +01:00
var chunkHdr = new byte[Marshal.SizeOf<ChunkHeader>()];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(chunkHdr, 0, Marshal.SizeOf<ChunkHeader>());
2023-07-06 00:04:46 +02:00
ChunkHeader chunkHeader = Marshal.ByteArrayToStructureLittleEndian<ChunkHeader>(chunkHdr);
2023-10-05 01:52:48 +01:00
_a2RStream.Seek(-Marshal.SizeOf<ChunkHeader>(), SeekOrigin.Current);
2023-07-06 00:04:46 +02:00
switch(chunkHeader.chunkId)
{
case var rwcp when rwcp.SequenceEqual(_rwcpChunkSignature):
2023-10-03 23:34:59 +01:00
var rwcpBuffer = new byte[Marshal.SizeOf<RwcpChunkHeader>()];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(rwcpBuffer, 0, Marshal.SizeOf<RwcpChunkHeader>());
2023-07-06 00:04:46 +02:00
RwcpChunkHeader rwcpChunk = Marshal.ByteArrayToStructureLittleEndian<RwcpChunkHeader>(rwcpBuffer);
2023-10-05 01:52:48 +01:00
while(_a2RStream.ReadByte() == 0x43)
2023-07-06 00:04:46 +02:00
{
var capture = new StreamCapture
{
mark = 0x43,
2023-10-05 01:52:48 +01:00
captureType = (byte)_a2RStream.ReadByte()
2023-07-06 00:04:46 +02:00
};
2023-10-03 23:34:59 +01:00
var location = new byte[2];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(location, 0, 2);
2023-07-06 00:04:46 +02:00
capture.location = BitConverter.ToUInt16(location);
2023-10-05 01:52:48 +01:00
A2RLocationToHeadTrackSub(capture.location, _imageInfo.MediaType, out capture.head,
2023-07-06 00:04:46 +02:00
out capture.track, out capture.subTrack);
if(capture.head + 1 > _imageInfo.Heads)
_imageInfo.Heads = capture.head + 1;
if(capture.track + 1 > _imageInfo.Cylinders)
_imageInfo.Cylinders = (uint)(capture.track + 1);
2023-10-05 01:52:48 +01:00
capture.numberOfIndexSignals = (byte)_a2RStream.ReadByte();
2023-07-06 00:04:46 +02:00
capture.indexSignals = new uint[capture.numberOfIndexSignals];
2023-10-03 23:34:59 +01:00
for(var i = 0; capture.numberOfIndexSignals > i; i++)
2023-07-06 00:04:46 +02:00
{
2023-10-03 23:34:59 +01:00
var index = new byte[4];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(index, 0, 4);
2023-07-06 00:04:46 +02:00
capture.indexSignals[i] = BitConverter.ToUInt32(index);
}
2023-10-03 23:34:59 +01:00
var dataSize = new byte[4];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(dataSize, 0, 4);
2023-07-06 00:04:46 +02:00
capture.captureDataSize = BitConverter.ToUInt32(dataSize);
2023-10-05 01:52:48 +01:00
capture.dataOffset = _a2RStream.Position;
2023-07-06 00:04:46 +02:00
capture.resolution = rwcpChunk.resolution;
2023-10-05 01:52:48 +01:00
_a2RCaptures.Add(capture);
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
_a2RStream.Seek(capture.captureDataSize, SeekOrigin.Current);
2023-07-06 00:04:46 +02:00
}
break;
case var meta when meta.SequenceEqual(_metaChunkSignature):
2023-10-05 01:52:48 +01:00
_meta = new Dictionary<string, string>();
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
_a2RStream.Seek(Marshal.SizeOf<ChunkHeader>(), SeekOrigin.Current);
2023-07-06 00:04:46 +02:00
2023-10-03 23:34:59 +01:00
var metadataBuffer = new byte[chunkHeader.chunkSize];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(metadataBuffer, 0, (int)chunkHeader.chunkSize);
2023-07-06 00:04:46 +02:00
string metaData = Encoding.UTF8.GetString(metadataBuffer);
string[] metaFields = metaData.Split('\n');
foreach(string[] keyValue in metaFields.Select(field => field.Split('\t')).
Where(keyValue => keyValue.Length == 2))
2023-10-05 01:52:48 +01:00
_meta.Add(keyValue[0], keyValue[1]);
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
if(_meta.TryGetValue("image_date", out string imageDate))
2023-07-06 00:04:46 +02:00
_imageInfo.CreationTime = DateTime.Parse(imageDate);
2023-10-05 01:52:48 +01:00
if(_meta.TryGetValue("title", out string title))
2023-07-06 00:04:46 +02:00
_imageInfo.MediaTitle = title;
break;
2023-10-03 23:34:59 +01:00
case var slvd when slvd.SequenceEqual(_slvdChunkSignature):
return ErrorNumber.NotImplemented;
2023-07-06 00:04:46 +02:00
case var strm when strm.SequenceEqual(_strmChunkSignature):
2023-10-03 23:34:59 +01:00
var strmBuffer = new byte[Marshal.SizeOf<ChunkHeader>()];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(strmBuffer, 0, Marshal.SizeOf<ChunkHeader>());
2023-07-06 00:04:46 +02:00
ChunkHeader strmChunk = Marshal.ByteArrayToStructureLittleEndian<ChunkHeader>(strmBuffer);
2023-10-05 01:52:48 +01:00
long end = strmChunk.chunkSize + _a2RStream.Position - 1;
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
while(_a2RStream.Position < end)
2023-07-06 00:04:46 +02:00
{
var capture = new StreamCapture
{
indexSignals = new uint[1],
2023-10-05 01:52:48 +01:00
location = (byte)_a2RStream.ReadByte(),
captureType = (byte)_a2RStream.ReadByte(),
2023-07-06 00:04:46 +02:00
resolution = 125000,
numberOfIndexSignals = 1
};
2023-10-05 01:52:48 +01:00
A2RLocationToHeadTrackSub(capture.location, _imageInfo.MediaType, out capture.head,
2023-07-06 00:04:46 +02:00
out capture.track, out capture.subTrack);
if(capture.head + 1 > _imageInfo.Heads)
_imageInfo.Heads = capture.head + 1;
if(capture.track + 1 > _imageInfo.Cylinders)
_imageInfo.Cylinders = (uint)(capture.track + 1);
2023-10-03 23:34:59 +01:00
var dataSize = new byte[4];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(dataSize, 0, 4);
2023-07-06 00:04:46 +02:00
capture.captureDataSize = BitConverter.ToUInt32(dataSize);
2023-10-03 23:34:59 +01:00
var index = new byte[4];
2023-10-05 01:52:48 +01:00
_a2RStream.EnsureRead(index, 0, 4);
2023-07-06 00:04:46 +02:00
capture.indexSignals[0] = BitConverter.ToUInt32(index);
2023-10-05 01:52:48 +01:00
capture.dataOffset = _a2RStream.Position;
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
_a2RCaptures.Add(capture);
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
_a2RStream.Seek(capture.captureDataSize, SeekOrigin.Current);
2023-07-06 00:04:46 +02:00
}
2023-10-05 01:52:48 +01:00
_a2RStream.ReadByte();
2023-07-06 00:04:46 +02:00
break;
}
}
return ErrorNumber.NoError;
}
/// <inheritdoc />
public ErrorNumber CapturesLength(uint head, ushort track, byte subTrack, out uint length)
{
2023-10-05 01:52:48 +01:00
long index = HeadTrackSubToA2RLocation(head, track, subTrack, _imageInfo.MediaType);
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
length = (uint)_a2RCaptures.FindAll(capture => index == capture.location).Count;
2023-07-06 00:04:46 +02:00
return ErrorNumber.NoError;
}
/// <inheritdoc />
2023-10-03 23:34:59 +01:00
public ErrorNumber ReadFluxIndexResolution(uint head, ushort track, byte subTrack, uint captureIndex,
2023-07-06 00:04:46 +02:00
out ulong resolution)
{
resolution = StreamCaptureAtIndex(head, track, subTrack, captureIndex).resolution;
return ErrorNumber.NoError;
}
/// <inheritdoc />
2023-10-03 23:34:59 +01:00
public ErrorNumber ReadFluxDataResolution(uint head, ushort track, byte subTrack, uint captureIndex,
2023-07-06 00:04:46 +02:00
out ulong resolution)
{
resolution = StreamCaptureAtIndex(head, track, subTrack, captureIndex).resolution;
return ErrorNumber.NoError;
}
/// <inheritdoc />
2023-10-03 23:34:59 +01:00
public ErrorNumber ReadFluxResolution(uint head, ushort track, byte subTrack, uint captureIndex,
2023-07-06 00:04:46 +02:00
out ulong indexResolution, out ulong dataResolution)
{
indexResolution = dataResolution = StreamCaptureAtIndex(head, track, subTrack, captureIndex).resolution;
return ErrorNumber.NoError;
}
/// <inheritdoc />
2023-10-03 23:34:59 +01:00
public ErrorNumber ReadFluxCapture(uint head, ushort track, byte subTrack, uint captureIndex,
out ulong indexResolution, out ulong dataResolution, out byte[] indexBuffer,
2023-07-06 00:04:46 +02:00
out byte[] dataBuffer)
{
dataBuffer = indexBuffer = null;
ErrorNumber error =
ReadFluxResolution(head, track, subTrack, captureIndex, out indexResolution, out dataResolution);
if(error != ErrorNumber.NoError)
return error;
error = ReadFluxDataCapture(head, track, subTrack, captureIndex, out dataBuffer);
if(error != ErrorNumber.NoError)
return error;
error = ReadFluxIndexCapture(head, track, subTrack, captureIndex, out indexBuffer);
return error;
}
/// <inheritdoc />
2023-10-03 23:34:59 +01:00
public ErrorNumber ReadFluxIndexCapture(uint head, ushort track, byte subTrack, uint captureIndex,
2023-07-06 00:04:46 +02:00
out byte[] buffer)
{
buffer = null;
List<byte> tmpBuffer = new()
{
// A2R always starts at index signal
0
};
StreamCapture capture = StreamCaptureAtIndex(head, track, subTrack, captureIndex);
uint previousTicks = 0;
2023-10-03 23:34:59 +01:00
for(var i = 0; i < capture.numberOfIndexSignals; i++)
2023-07-06 00:04:46 +02:00
{
uint ticks = capture.indexSignals[i] - previousTicks;
tmpBuffer.AddRange(UInt32ToFluxRepresentation(ticks));
previousTicks = capture.indexSignals[i];
}
buffer = tmpBuffer.ToArray();
return ErrorNumber.NoError;
}
/// <inheritdoc />
public ErrorNumber ReadFluxDataCapture(uint head, ushort track, byte subTrack, uint captureIndex, out byte[] buffer)
{
buffer = null;
StreamCapture capture = StreamCaptureAtIndex(head, track, subTrack, captureIndex);
if(capture.captureType == 2)
return ErrorNumber.NotImplemented;
2023-10-05 01:52:48 +01:00
Stream stream = _a2RFilter.GetDataForkStream();
2023-07-06 00:04:46 +02:00
var br = new BinaryReader(stream);
br.BaseStream.Seek(capture.dataOffset, SeekOrigin.Begin);
buffer = br.ReadBytes((int)capture.captureDataSize);
return ErrorNumber.NoError;
}
/// <inheritdoc />
public ErrorNumber SubTrackLength(uint head, ushort track, out byte length)
{
length = 0;
2023-10-05 01:52:48 +01:00
List<StreamCapture> captures = _a2RCaptures.FindAll(c => c.head == head && c.track == track);
2023-07-06 00:04:46 +02:00
if(captures.Count <= 0)
return ErrorNumber.OutOfRange;
length = (byte)(captures.Max(static c => c.subTrack) + 1);
return ErrorNumber.NoError;
}
2023-10-03 23:34:59 +01:00
#endregion
#region IMediaImage Members
2023-07-06 00:04:46 +02:00
/// <inheritdoc />
public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer) => throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber ReadSector(ulong sectorAddress, out byte[] buffer) => throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber ReadSectorLong(ulong sectorAddress, out byte[] buffer) => throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber ReadSectors(ulong sectorAddress, uint length, out byte[] buffer) =>
throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber ReadSectorsLong(ulong sectorAddress, uint length, out byte[] buffer) =>
throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag, out byte[] buffer) =>
throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber ReadSectorTag(ulong sectorAddress, SectorTagType tag, out byte[] buffer) =>
throw new NotImplementedException();
2023-10-03 23:34:59 +01:00
#endregion
2023-07-06 00:04:46 +02:00
StreamCapture StreamCaptureAtIndex(uint head, ushort track, byte subTrack, uint captureIndex)
{
2023-10-05 01:52:48 +01:00
long index = HeadTrackSubToA2RLocation(head, track, subTrack, _imageInfo.MediaType);
2023-07-06 00:04:46 +02:00
2023-10-05 01:52:48 +01:00
return _a2RCaptures.FindAll(capture => index == capture.location)[(int)captureIndex];
2023-07-06 00:04:46 +02:00
}
}