Files
Aaru/Aaru.Images/AaruFormat/Read.cs

2371 lines
106 KiB
C#
Raw Normal View History

// /***************************************************************************
2020-02-27 12:31:25 +00:00
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Read.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disk image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Reads Aaru Format disk 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/>.
//
// ----------------------------------------------------------------------------
2022-02-18 10:02:53 +00:00
// Copyright © 2011-2022 Natalia Portillo
// Copyright © 2020-2022 Rebecca Wallander
// ****************************************************************************/
2022-03-07 07:36:44 +00:00
namespace Aaru.DiscImages;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
2019-03-15 22:07:10 +00:00
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
2020-02-27 00:33:26 +00:00
using Aaru.Checksums;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
using Aaru.Compression;
2020-02-27 00:33:26 +00:00
using Aaru.Console;
using Aaru.Decoders.CD;
using Aaru.Helpers;
using Schemas;
2020-02-27 00:33:26 +00:00
using Marshal = Aaru.Helpers.Marshal;
using Session = Aaru.CommonTypes.Structs.Session;
2020-02-27 00:33:26 +00:00
using TrackType = Aaru.CommonTypes.Enums.TrackType;
2022-03-06 13:29:38 +00:00
public sealed partial class AaruFormat
{
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber Open(IFilter imageFilter)
{
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
_imageStream = imageFilter.GetDataForkStream();
_imageStream.Seek(0, SeekOrigin.Begin);
2022-03-06 13:29:38 +00:00
if(_imageStream.Length < Marshal.SizeOf<AaruHeader>())
return ErrorNumber.InvalidArgument;
2022-03-06 13:29:38 +00:00
_structureBytes = new byte[Marshal.SizeOf<AaruHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
_header = Marshal.ByteArrayToStructureLittleEndian<AaruHeader>(_structureBytes);
2022-03-06 13:29:38 +00:00
if(_header.imageMajorVersion > AARUFMT_VERSION)
{
AaruConsole.ErrorWriteLine($"Image version {_header.imageMajorVersion} not recognized.");
2022-03-06 13:29:38 +00:00
return ErrorNumber.NotSupported;
}
_imageInfo.Application = _header.application;
_imageInfo.ApplicationVersion = $"{_header.applicationMajorVersion}.{_header.applicationMinorVersion}";
_imageInfo.Version = $"{_header.imageMajorVersion}.{_header.imageMinorVersion}";
_imageInfo.MediaType = _header.mediaType;
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
// Read the index header
_imageStream.Position = (long)_header.indexOffset;
_structureBytes = new byte[Marshal.SizeOf<IndexHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
IndexHeader idxHeader = Marshal.SpanToStructureLittleEndian<IndexHeader>(_structureBytes);
2022-03-06 13:29:38 +00:00
if(idxHeader.identifier != BlockType.Index &&
idxHeader.identifier != BlockType.Index2)
{
AaruConsole.ErrorWriteLine("Index not found!");
2022-03-06 13:29:38 +00:00
return ErrorNumber.InvalidArgument;
}
2022-03-06 13:29:38 +00:00
if(idxHeader.identifier == BlockType.Index2)
{
2020-07-20 21:11:32 +01:00
_imageStream.Position = (long)_header.indexOffset;
2022-03-06 13:29:38 +00:00
_structureBytes = new byte[Marshal.SizeOf<IndexHeader2>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
IndexHeader2 idxHeader2 = Marshal.SpanToStructureLittleEndian<IndexHeader2>(_structureBytes);
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", _header.indexOffset,
idxHeader2.entries);
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
// Fill in-memory index
_index = new List<IndexEntry>();
for(ulong i = 0; i < idxHeader2.entries; i++)
{
2022-03-06 13:29:38 +00:00
_structureBytes = new byte[Marshal.SizeOf<IndexEntry>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
IndexEntry entry = Marshal.SpanToStructureLittleEndian<IndexEntry>(_structureBytes);
AaruConsole.DebugWriteLine("Aaru Format plugin",
2022-03-07 07:36:44 +00:00
"Block type {0} with data type {1} is indexed to be at {2}", entry.blockType,
entry.dataType, entry.offset);
2022-03-06 13:29:38 +00:00
_index.Add(entry);
}
2022-03-06 13:29:38 +00:00
}
else
{
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", _header.indexOffset,
idxHeader.entries);
2022-03-06 13:29:38 +00:00
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
// Fill in-memory index
_index = new List<IndexEntry>();
2022-03-06 13:29:38 +00:00
for(ushort i = 0; i < idxHeader.entries; i++)
{
2022-03-06 13:29:38 +00:00
_structureBytes = new byte[Marshal.SizeOf<IndexEntry>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
IndexEntry entry = Marshal.SpanToStructureLittleEndian<IndexEntry>(_structureBytes);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin",
2022-03-07 07:36:44 +00:00
"Block type {0} with data type {1} is indexed to be at {2}", entry.blockType,
entry.dataType, entry.offset);
2022-03-06 13:29:38 +00:00
_index.Add(entry);
}
}
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
_imageInfo.ImageSize = 0;
2022-03-07 07:36:44 +00:00
var foundUserDataDdt = false;
2022-03-06 13:29:38 +00:00
_mediaTags = new Dictionary<MediaTagType, byte[]>();
List<CompactDiscIndexEntry> compactDiscIndexes = null;
2022-03-06 13:29:38 +00:00
foreach(IndexEntry entry in _index)
{
_imageStream.Position = (long)entry.offset;
2022-03-06 13:29:38 +00:00
switch(entry.blockType)
{
case BlockType.DataBlock:
// NOP block, skip
if(entry.dataType == DataType.NoData)
break;
2022-03-06 13:29:38 +00:00
_imageStream.Position = (long)entry.offset;
2022-03-06 13:29:38 +00:00
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
BlockHeader blockHeader = Marshal.SpanToStructureLittleEndian<BlockHeader>(_structureBytes);
_imageInfo.ImageSize += blockHeader.cmpLength;
2022-03-06 13:29:38 +00:00
// Unused, skip
if(entry.dataType == DataType.UserData)
{
if(blockHeader.sectorSize > _imageInfo.SectorSize)
_imageInfo.SectorSize = blockHeader.sectorSize;
2022-03-06 13:29:38 +00:00
break;
}
2019-03-22 21:25:45 +00:00
2022-03-06 13:29:38 +00:00
if(blockHeader.identifier != entry.blockType)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
2022-03-07 07:36:44 +00:00
"Incorrect identifier for data block at position {0}", entry.offset);
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
if(blockHeader.type != entry.dataType)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Expected block with data type {0} at position {1} but found data type {2}",
entry.dataType, entry.offset, blockHeader.type);
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
byte[] data;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found data block type {0} at position {1}",
entry.dataType, entry.offset);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
// Decompress media tag
2022-03-16 11:47:00 +00:00
if(blockHeader.compression is CompressionType.Lzma
or CompressionType.LzmaClauniaSubchannelTransform)
2022-03-06 13:29:38 +00:00
{
if(blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform &&
entry.dataType != DataType.CdSectorSubchannel)
{
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Invalid compression type {0} for block with data type {1}, continuing...",
blockHeader.compression, entry.dataType);
break;
}
2022-03-06 13:29:38 +00:00
DateTime startDecompress = DateTime.Now;
2022-03-07 07:36:44 +00:00
var compressedTag = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH];
var lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH];
_imageStream.EnsureRead(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH);
_imageStream.EnsureRead(compressedTag, 0, compressedTag.Length);
2022-03-06 13:29:38 +00:00
data = new byte[blockHeader.length];
int decompressedLength = LZMA.DecodeBuffer(compressedTag, data, lzmaProperties);
if(decompressedLength != blockHeader.length)
{
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin",
2022-03-06 13:29:38 +00:00
"Error decompressing block, should be {0} bytes but got {1} bytes.",
blockHeader.length, decompressedLength);
2022-03-06 13:29:38 +00:00
return ErrorNumber.InOutError;
}
2022-03-06 13:29:38 +00:00
if(blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform)
data = ClauniaSubchannelUntransform(data);
2022-03-06 13:29:38 +00:00
DateTime endDecompress = DateTime.Now;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to decompress block",
(endDecompress - startDecompress).TotalSeconds);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
}
else if(blockHeader.compression == CompressionType.None)
{
data = new byte[blockHeader.length];
_imageStream.EnsureRead(data, 0, (int)blockHeader.length);
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
2020-02-29 18:03:35 +00:00
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
}
else
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Found unknown compression type {0}, continuing...",
(ushort)blockHeader.compression);
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
// Check CRC, if not correct, skip it
Crc64Context.Data(data, out byte[] blockCrc);
2022-03-06 13:29:38 +00:00
if(BitConverter.ToUInt64(blockCrc, 0) != blockHeader.crc64 &&
blockHeader.crc64 != 0)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...",
BitConverter.ToUInt64(blockCrc, 0), blockHeader.crc64);
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
// Check if it's not a media tag, but a sector tag, and fill the appropriate table then
switch(entry.dataType)
{
case DataType.CdSectorPrefix:
case DataType.CdSectorPrefixCorrected:
if(entry.dataType == DataType.CdSectorPrefixCorrected)
{
_sectorPrefixMs ??= new MemoryStream();
_sectorPrefixMs.Write(data, 0, data.Length);
}
2022-03-06 13:29:38 +00:00
else
_sectorPrefix = data;
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
2020-02-29 18:03:35 +00:00
GC.GetTotalMemory(false));
break;
2022-03-06 13:29:38 +00:00
case DataType.CdSectorSuffix:
case DataType.CdSectorSuffixCorrected:
if(entry.dataType == DataType.CdSectorSuffixCorrected)
{
_sectorSuffixMs ??= new MemoryStream();
_sectorSuffixMs.Write(data, 0, data.Length);
}
else
_sectorSuffix = data;
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader);
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEcc))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEcc);
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccP))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccP);
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccQ))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccQ);
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc);
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
break;
2022-03-06 13:29:38 +00:00
case DataType.CdSectorSubchannel:
_sectorSubchannel = data;
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
case DataType.AppleProfileTag:
case DataType.AppleSonyTag:
case DataType.PriamDataTowerTag:
_sectorSubchannel = data;
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag))
_imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
case DataType.CompactDiscMode2Subheader:
_mode2Subheaders = data;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
case DataType.DvdSectorCpiMai:
_sectorCpiMai = data;
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdCmi))
_imageInfo.ReadableSectorTags.Add(SectorTagType.DvdCmi);
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdTitleKey))
_imageInfo.ReadableSectorTags.Add(SectorTagType.DvdTitleKey);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
case DataType.DvdSectorTitleKeyDecrypted:
_sectorDecryptedTitleKey = data;
2022-03-06 13:29:38 +00:00
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdTitleKeyDecrypted))
_imageInfo.ReadableSectorTags.Add(SectorTagType.DvdTitleKeyDecrypted);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
default:
MediaTagType mediaTagType = GetMediaTagTypeForDataType(blockHeader.type);
2022-03-06 13:29:38 +00:00
if(_mediaTags.ContainsKey(mediaTagType))
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Media tag type {0} duplicated, removing previous entry...",
mediaTagType);
2022-03-06 13:29:38 +00:00
_mediaTags.Remove(mediaTagType);
}
2022-03-06 13:29:38 +00:00
_mediaTags.Add(mediaTagType, data);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
break;
case BlockType.DeDuplicationTable:
_structureBytes = new byte[Marshal.SizeOf<DdtHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
DdtHeader ddtHeader = Marshal.SpanToStructureLittleEndian<DdtHeader>(_structureBytes);
_imageInfo.ImageSize += ddtHeader.cmpLength;
2022-03-06 13:29:38 +00:00
if(ddtHeader.identifier != BlockType.DeDuplicationTable)
break;
2022-03-06 13:29:38 +00:00
switch(entry.dataType)
{
case DataType.UserData:
_imageInfo.Sectors = ddtHeader.entries;
_shift = ddtHeader.shift;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
// Check for DDT compression
switch(ddtHeader.compression)
{
case CompressionType.Lzma:
AaruConsole.DebugWriteLine("Aaru Format plugin", "Decompressing DDT...");
DateTime ddtStart = DateTime.UtcNow;
2022-03-07 07:36:44 +00:00
var compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH];
var lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH];
_imageStream.EnsureRead(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH);
_imageStream.EnsureRead(compressedDdt, 0, compressedDdt.Length);
2022-03-07 07:36:44 +00:00
var decompressedDdt = new byte[ddtHeader.length];
2022-03-06 13:29:38 +00:00
2022-03-07 07:36:44 +00:00
var decompressedLength =
2022-03-06 13:29:38 +00:00
(ulong)LZMA.DecodeBuffer(compressedDdt, decompressedDdt, lzmaProperties);
if(decompressedLength != ddtHeader.length)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Error decompressing DDT, should be {0} bytes but got {1} bytes.",
ddtHeader.length, decompressedLength);
2022-03-06 13:29:38 +00:00
return ErrorNumber.InOutError;
}
2022-03-06 13:29:38 +00:00
_userDataDdt = MemoryMarshal.Cast<byte, ulong>(decompressedDdt).ToArray();
DateTime ddtEnd = DateTime.UtcNow;
_inMemoryDdt = true;
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin",
2022-03-06 13:29:38 +00:00
"Took {0} seconds to decompress DDT",
(ddtEnd - ddtStart).TotalSeconds);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
case CompressionType.None:
_inMemoryDdt = false;
_outMemoryDdtPosition = (long)entry.offset;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
default:
AaruConsole.
ErrorWriteLine($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}");
2022-03-06 13:29:38 +00:00
return ErrorNumber.NotSupported;
}
2022-03-06 13:29:38 +00:00
foundUserDataDdt = true;
2022-03-06 13:29:38 +00:00
break;
case DataType.CdSectorPrefixCorrected:
case DataType.CdSectorSuffixCorrected:
2019-03-22 21:25:45 +00:00
{
2022-03-07 07:36:44 +00:00
var decompressedDdt = new byte[ddtHeader.length];
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
// Check for DDT compression
switch(ddtHeader.compression)
{
case CompressionType.Lzma:
AaruConsole.DebugWriteLine("Aaru Format plugin", "Decompressing DDT...");
DateTime ddtStart = DateTime.UtcNow;
2022-03-07 07:36:44 +00:00
var compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH];
var lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH];
_imageStream.EnsureRead(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH);
_imageStream.EnsureRead(compressedDdt, 0, compressedDdt.Length);
2022-03-07 07:36:44 +00:00
var decompressedLength =
2022-03-06 13:29:38 +00:00
(ulong)LZMA.DecodeBuffer(compressedDdt, decompressedDdt, lzmaProperties);
2022-03-06 13:29:38 +00:00
DateTime ddtEnd = DateTime.UtcNow;
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
if(decompressedLength != ddtHeader.length)
{
2020-07-22 13:20:25 +01:00
AaruConsole.DebugWriteLine("Aaru Format plugin",
2022-03-06 13:29:38 +00:00
"Error decompressing DDT, should be {0} bytes but got {1} bytes.",
ddtHeader.length, decompressedLength);
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
return ErrorNumber.InOutError;
}
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Took {0} seconds to decompress DDT",
(ddtEnd - ddtStart).TotalSeconds);
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
break;
case CompressionType.None:
_imageStream.EnsureRead(decompressedDdt, 0, decompressedDdt.Length);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
default:
AaruConsole.
ErrorWriteLine($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}");
2022-03-06 13:29:38 +00:00
return ErrorNumber.NotSupported;
}
2022-03-06 13:29:38 +00:00
uint[] cdDdt = MemoryMarshal.Cast<byte, uint>(decompressedDdt).ToArray();
2022-03-06 13:29:38 +00:00
switch(entry.dataType)
{
case DataType.CdSectorPrefixCorrected:
_sectorPrefixDdt = cdDdt;
_sectorPrefixMs = new MemoryStream();
2022-03-06 13:29:38 +00:00
break;
case DataType.CdSectorSuffixCorrected:
_sectorSuffixDdt = cdDdt;
_sectorSuffixMs = new MemoryStream();
2022-03-06 13:29:38 +00:00
break;
}
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
}
}
2022-03-06 13:29:38 +00:00
break;
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
// Logical geometry block. It doesn't have a CRC coz, well, it's not so important
case BlockType.GeometryBlock:
_structureBytes = new byte[Marshal.SizeOf<GeometryBlock>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
_geometryBlock = Marshal.SpanToStructureLittleEndian<GeometryBlock>(_structureBytes);
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
if(_geometryBlock.identifier == BlockType.GeometryBlock)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Geometry set to {0} cylinders {1} heads {2} sectors per track",
_geometryBlock.cylinders, _geometryBlock.heads,
_geometryBlock.sectorsPerTrack);
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
_imageInfo.Cylinders = _geometryBlock.cylinders;
_imageInfo.Heads = _geometryBlock.heads;
_imageInfo.SectorsPerTrack = _geometryBlock.sectorsPerTrack;
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
}
2022-03-06 13:29:38 +00:00
break;
2022-03-06 13:29:38 +00:00
// Metadata block
case BlockType.MetadataBlock:
_structureBytes = new byte[Marshal.SizeOf<MetadataBlock>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-07 07:36:44 +00:00
MetadataBlock metadataBlock = Marshal.SpanToStructureLittleEndian<MetadataBlock>(_structureBytes);
2022-03-06 13:29:38 +00:00
if(metadataBlock.identifier != entry.blockType)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
2022-03-07 07:36:44 +00:00
"Incorrect identifier for data block at position {0}", entry.offset);
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found metadata block at position {0}",
entry.offset);
2022-03-07 07:36:44 +00:00
var metadata = new byte[metadataBlock.blockSize];
2022-03-06 13:29:38 +00:00
_imageStream.Position = (long)entry.offset;
_imageStream.EnsureRead(metadata, 0, metadata.Length);
2022-03-06 13:29:38 +00:00
if(metadataBlock.mediaSequence > 0 &&
metadataBlock.lastMediaSequence > 0)
{
_imageInfo.MediaSequence = metadataBlock.mediaSequence;
_imageInfo.LastMediaSequence = metadataBlock.lastMediaSequence;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media sequence as {0} of {1}",
_imageInfo.MediaSequence, _imageInfo.LastMediaSequence);
}
2022-03-06 13:29:38 +00:00
if(metadataBlock.creatorLength > 0 &&
metadataBlock.creatorLength + metadataBlock.creatorOffset <= metadata.Length)
{
_imageInfo.Creator = Encoding.Unicode.GetString(metadata, (int)metadataBlock.creatorOffset,
(int)(metadataBlock.creatorLength - 2));
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting creator: {0}", _imageInfo.Creator);
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(metadataBlock.commentsOffset > 0 &&
metadataBlock.commentsLength + metadataBlock.commentsOffset <= metadata.Length)
{
2022-03-07 07:36:44 +00:00
_imageInfo.Comments = Encoding.Unicode.GetString(metadata, (int)metadataBlock.commentsOffset,
(int)(metadataBlock.commentsLength - 2));
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting comments: {0}", _imageInfo.Comments);
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(metadataBlock.mediaTitleOffset > 0 &&
metadataBlock.mediaTitleLength + metadataBlock.mediaTitleOffset <= metadata.Length)
{
_imageInfo.MediaTitle =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaTitleOffset,
(int)(metadataBlock.mediaTitleLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media title: {0}",
_imageInfo.MediaTitle);
}
2022-03-07 07:36:44 +00:00
if(metadataBlock.mediaManufacturerOffset > 0 &&
metadataBlock.mediaManufacturerLength + metadataBlock.mediaManufacturerOffset <= metadata.Length)
2022-03-06 13:29:38 +00:00
{
_imageInfo.MediaManufacturer =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaManufacturerOffset,
(int)(metadataBlock.mediaManufacturerLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media manufacturer: {0}",
_imageInfo.MediaManufacturer);
}
2022-03-06 13:29:38 +00:00
if(metadataBlock.mediaModelOffset > 0 &&
metadataBlock.mediaModelLength + metadataBlock.mediaModelOffset <= metadata.Length)
{
_imageInfo.MediaModel =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaModelOffset,
(int)(metadataBlock.mediaModelLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media model: {0}",
_imageInfo.MediaModel);
}
2022-03-07 07:36:44 +00:00
if(metadataBlock.mediaSerialNumberOffset > 0 &&
metadataBlock.mediaSerialNumberLength + metadataBlock.mediaSerialNumberOffset <= metadata.Length)
2022-03-06 13:29:38 +00:00
{
_imageInfo.MediaSerialNumber =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaSerialNumberOffset,
(int)(metadataBlock.mediaSerialNumberLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media serial number: {0}",
_imageInfo.MediaSerialNumber);
}
2022-03-06 13:29:38 +00:00
if(metadataBlock.mediaBarcodeOffset > 0 &&
metadataBlock.mediaBarcodeLength + metadataBlock.mediaBarcodeOffset <= metadata.Length)
{
_imageInfo.MediaBarcode =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaBarcodeOffset,
(int)(metadataBlock.mediaBarcodeLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media barcode: {0}",
_imageInfo.MediaBarcode);
}
2022-03-06 13:29:38 +00:00
if(metadataBlock.mediaPartNumberOffset > 0 &&
metadataBlock.mediaPartNumberLength + metadataBlock.mediaPartNumberOffset <= metadata.Length)
{
_imageInfo.MediaPartNumber =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaPartNumberOffset,
(int)(metadataBlock.mediaPartNumberLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media part number: {0}",
_imageInfo.MediaPartNumber);
}
2022-03-07 07:36:44 +00:00
if(metadataBlock.driveManufacturerOffset > 0 &&
metadataBlock.driveManufacturerLength + metadataBlock.driveManufacturerOffset <= metadata.Length)
2022-03-06 13:29:38 +00:00
{
_imageInfo.DriveManufacturer =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveManufacturerOffset,
(int)(metadataBlock.driveManufacturerLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive manufacturer: {0}",
_imageInfo.DriveManufacturer);
}
2022-03-06 13:29:38 +00:00
if(metadataBlock.driveModelOffset > 0 &&
metadataBlock.driveModelLength + metadataBlock.driveModelOffset <= metadata.Length)
{
_imageInfo.DriveModel =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveModelOffset,
(int)(metadataBlock.driveModelLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive model: {0}",
_imageInfo.DriveModel);
}
2022-03-07 07:36:44 +00:00
if(metadataBlock.driveSerialNumberOffset > 0 &&
metadataBlock.driveSerialNumberLength + metadataBlock.driveSerialNumberOffset <= metadata.Length)
2022-03-06 13:29:38 +00:00
{
_imageInfo.DriveSerialNumber =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveSerialNumberOffset,
(int)(metadataBlock.driveSerialNumberLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive serial number: {0}",
_imageInfo.DriveSerialNumber);
}
2022-03-06 13:29:38 +00:00
if(metadataBlock.driveFirmwareRevisionOffset > 0 &&
metadataBlock.driveFirmwareRevisionLength + metadataBlock.driveFirmwareRevisionOffset <=
metadata.Length)
{
_imageInfo.DriveFirmwareRevision =
Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveFirmwareRevisionOffset,
(int)(metadataBlock.driveFirmwareRevisionLength - 2));
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive firmware revision: {0}",
_imageInfo.DriveFirmwareRevision);
}
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
2022-03-06 13:29:38 +00:00
// Optical disc tracks block
case BlockType.TracksBlock:
_structureBytes = new byte[Marshal.SizeOf<TracksHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
TracksHeader tracksHeader = Marshal.SpanToStructureLittleEndian<TracksHeader>(_structureBytes);
2022-03-06 13:29:38 +00:00
if(tracksHeader.identifier != BlockType.TracksBlock)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Incorrect identifier for tracks block at position {0}",
entry.offset);
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
_structureBytes = new byte[Marshal.SizeOf<TrackEntry>() * tracksHeader.entries];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
Crc64Context.Data(_structureBytes, out byte[] trksCrc);
2022-03-06 13:29:38 +00:00
if(BitConverter.ToUInt64(trksCrc, 0) != tracksHeader.crc64)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...",
BitConverter.ToUInt64(trksCrc, 0), tracksHeader.crc64);
break;
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
_imageStream.Position -= _structureBytes.Length;
2022-03-06 13:29:38 +00:00
Tracks = new List<Track>();
_trackFlags = new Dictionary<byte, byte>();
_trackIsrcs = new Dictionary<byte, string>();
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found {0} tracks at position {0}",
tracksHeader.entries, entry.offset);
2022-03-06 13:29:38 +00:00
for(ushort i = 0; i < tracksHeader.entries; i++)
{
_structureBytes = new byte[Marshal.SizeOf<TrackEntry>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-07 07:36:44 +00:00
TrackEntry trackEntry = Marshal.ByteArrayToStructureLittleEndian<TrackEntry>(_structureBytes);
2022-03-06 13:29:38 +00:00
Tracks.Add(new Track
{
2022-03-06 13:29:38 +00:00
Sequence = trackEntry.sequence,
Type = trackEntry.type,
StartSector = (ulong)trackEntry.start,
EndSector = (ulong)trackEntry.end,
Pregap = (ulong)trackEntry.pregap,
Session = trackEntry.session,
File = imageFilter.Filename,
FileType = "BINARY",
Filter = imageFilter
});
if(trackEntry.type == TrackType.Data)
continue;
2022-03-06 13:29:38 +00:00
_trackFlags.Add(trackEntry.sequence, trackEntry.flags);
2022-03-06 13:29:38 +00:00
if(!string.IsNullOrEmpty(trackEntry.isrc))
_trackIsrcs.Add(trackEntry.sequence, trackEntry.isrc);
}
2022-03-06 13:29:38 +00:00
if(_trackFlags.Count > 0 &&
!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackFlags))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackFlags);
2022-03-06 13:29:38 +00:00
if(_trackIsrcs.Count > 0 &&
!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackIsrc))
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackIsrc);
2022-03-06 13:29:38 +00:00
_imageInfo.HasPartitions = true;
_imageInfo.HasSessions = true;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
2022-03-06 13:29:38 +00:00
// CICM XML metadata block
case BlockType.CicmBlock:
_structureBytes = new byte[Marshal.SizeOf<CicmMetadataBlock>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
CicmMetadataBlock cicmBlock =
Marshal.SpanToStructureLittleEndian<CicmMetadataBlock>(_structureBytes);
if(cicmBlock.identifier != BlockType.CicmBlock)
break;
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found CICM XML metadata block at position {0}",
entry.offset);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-07 07:36:44 +00:00
var cicmBytes = new byte[cicmBlock.length];
_imageStream.EnsureRead(cicmBytes, 0, cicmBytes.Length);
2022-03-06 13:29:38 +00:00
var cicmMs = new MemoryStream(cicmBytes);
var cicmXs = new XmlSerializer(typeof(CICMMetadataType));
2022-03-06 13:29:38 +00:00
try
{
var sr = new StreamReader(cicmMs);
CicmMetadata = (CICMMetadataType)cicmXs.Deserialize(sr);
sr.Close();
}
catch(XmlException ex)
{
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin",
2022-03-06 13:29:38 +00:00
"Exception {0} processing CICM XML metadata block", ex.Message);
2022-03-06 13:29:38 +00:00
CicmMetadata = null;
}
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
2022-03-06 13:29:38 +00:00
// Dump hardware block
case BlockType.DumpHardwareBlock:
_structureBytes = new byte[Marshal.SizeOf<DumpHardwareHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
DumpHardwareHeader dumpBlock =
Marshal.SpanToStructureLittleEndian<DumpHardwareHeader>(_structureBytes);
2022-03-06 13:29:38 +00:00
if(dumpBlock.identifier != BlockType.DumpHardwareBlock)
break;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found dump hardware block at position {0}",
entry.offset);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
_structureBytes = new byte[dumpBlock.length];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
Crc64Context.Data(_structureBytes, out byte[] dumpCrc);
2022-03-06 13:29:38 +00:00
if(BitConverter.ToUInt64(dumpCrc, 0) != dumpBlock.crc64)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...",
BitConverter.ToUInt64(dumpCrc, 0), dumpBlock.crc64);
2022-03-06 13:29:38 +00:00
break;
}
_imageStream.Position -= _structureBytes.Length;
2022-03-06 13:29:38 +00:00
DumpHardware = new List<DumpHardwareType>();
for(ushort i = 0; i < dumpBlock.entries; i++)
{
_structureBytes = new byte[Marshal.SizeOf<DumpHardwareEntry>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
DumpHardwareEntry dumpEntry =
Marshal.SpanToStructureLittleEndian<DumpHardwareEntry>(_structureBytes);
var dump = new DumpHardwareType
{
2022-03-06 13:29:38 +00:00
Software = new SoftwareType(),
Extents = new ExtentType[dumpEntry.extents]
};
2022-03-06 13:29:38 +00:00
byte[] tmp;
if(dumpEntry.manufacturerLength > 0)
{
tmp = new byte[dumpEntry.manufacturerLength - 1];
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
_imageStream.Position += 1;
dump.Manufacturer = Encoding.UTF8.GetString(tmp);
}
2022-03-06 13:29:38 +00:00
if(dumpEntry.modelLength > 0)
{
tmp = new byte[dumpEntry.modelLength - 1];
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
_imageStream.Position += 1;
dump.Model = Encoding.UTF8.GetString(tmp);
}
2022-03-06 13:29:38 +00:00
if(dumpEntry.revisionLength > 0)
{
tmp = new byte[dumpEntry.revisionLength - 1];
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
_imageStream.Position += 1;
dump.Revision = Encoding.UTF8.GetString(tmp);
}
2022-03-06 13:29:38 +00:00
if(dumpEntry.firmwareLength > 0)
{
2022-03-06 13:29:38 +00:00
tmp = new byte[dumpEntry.firmwareLength - 1];
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
_imageStream.Position += 1;
dump.Firmware = Encoding.UTF8.GetString(tmp);
}
2022-03-06 13:29:38 +00:00
if(dumpEntry.serialLength > 0)
{
tmp = new byte[dumpEntry.serialLength - 1];
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
_imageStream.Position += 1;
dump.Serial = Encoding.UTF8.GetString(tmp);
}
2022-03-06 13:29:38 +00:00
if(dumpEntry.softwareNameLength > 0)
{
tmp = new byte[dumpEntry.softwareNameLength - 1];
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
_imageStream.Position += 1;
dump.Software.Name = Encoding.UTF8.GetString(tmp);
}
2022-03-06 13:29:38 +00:00
if(dumpEntry.softwareVersionLength > 0)
{
tmp = new byte[dumpEntry.softwareVersionLength - 1];
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
_imageStream.Position += 1;
dump.Software.Version = Encoding.UTF8.GetString(tmp);
}
2022-03-06 13:29:38 +00:00
if(dumpEntry.softwareOperatingSystemLength > 0)
{
tmp = new byte[dumpEntry.softwareOperatingSystemLength - 1];
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
_imageStream.Position += 1;
dump.Software.OperatingSystem = Encoding.UTF8.GetString(tmp);
}
2022-03-06 13:29:38 +00:00
tmp = new byte[16];
2022-03-06 13:29:38 +00:00
for(uint j = 0; j < dumpEntry.extents; j++)
{
_imageStream.EnsureRead(tmp, 0, tmp.Length);
2022-03-06 13:29:38 +00:00
dump.Extents[j] = new ExtentType
{
2022-03-06 13:29:38 +00:00
Start = BitConverter.ToUInt64(tmp, 0),
End = BitConverter.ToUInt64(tmp, 8)
};
}
2022-03-06 13:29:38 +00:00
dump.Extents = dump.Extents.OrderBy(t => t.Start).ToArray();
2022-03-06 13:29:38 +00:00
if(dump.Extents.Length > 0)
DumpHardware.Add(dump);
}
2022-03-06 13:29:38 +00:00
if(DumpHardware.Count == 0)
DumpHardware = null;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
2022-03-06 13:29:38 +00:00
// Tape partition block
case BlockType.TapePartitionBlock:
_structureBytes = new byte[Marshal.SizeOf<TapePartitionHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
TapePartitionHeader partitionHeader =
Marshal.SpanToStructureLittleEndian<TapePartitionHeader>(_structureBytes);
2022-03-06 13:29:38 +00:00
if(partitionHeader.identifier != BlockType.TapePartitionBlock)
break;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found tape partition block at position {0}",
entry.offset);
2022-03-07 07:36:44 +00:00
var tapePartitionBytes = new byte[partitionHeader.length];
_imageStream.EnsureRead(tapePartitionBytes, 0, tapePartitionBytes.Length);
2022-03-06 13:29:38 +00:00
Span<TapePartitionEntry> tapePartitions =
MemoryMarshal.Cast<byte, TapePartitionEntry>(tapePartitionBytes);
2022-03-06 13:29:38 +00:00
TapePartitions = new List<TapePartition>();
2022-03-06 13:29:38 +00:00
foreach(TapePartitionEntry tapePartition in tapePartitions)
TapePartitions.Add(new TapePartition
{
FirstBlock = tapePartition.FirstBlock,
LastBlock = tapePartition.LastBlock,
Number = tapePartition.Number
});
2022-03-06 13:29:38 +00:00
IsTape = true;
2022-03-06 13:29:38 +00:00
break;
2022-03-06 13:29:38 +00:00
// Tape file block
case BlockType.TapeFileBlock:
_structureBytes = new byte[Marshal.SizeOf<TapeFileHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-07 07:36:44 +00:00
TapeFileHeader fileHeader = Marshal.SpanToStructureLittleEndian<TapeFileHeader>(_structureBytes);
2022-03-06 13:29:38 +00:00
if(fileHeader.identifier != BlockType.TapeFileBlock)
break;
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found tape file block at position {0}",
entry.offset);
2022-03-07 07:36:44 +00:00
var tapeFileBytes = new byte[fileHeader.length];
_imageStream.EnsureRead(tapeFileBytes, 0, tapeFileBytes.Length);
2022-03-06 13:29:38 +00:00
Span<TapeFileEntry> tapeFiles = MemoryMarshal.Cast<byte, TapeFileEntry>(tapeFileBytes);
Files = new List<TapeFile>();
2022-03-06 13:29:38 +00:00
foreach(TapeFileEntry file in tapeFiles)
Files.Add(new TapeFile
{
FirstBlock = file.FirstBlock,
LastBlock = file.LastBlock,
Partition = file.Partition,
File = file.File
});
2022-03-06 13:29:38 +00:00
IsTape = true;
2022-03-06 13:29:38 +00:00
break;
2020-07-20 21:11:32 +01:00
2022-03-06 13:29:38 +00:00
// Optical disc tracks block
case BlockType.CompactDiscIndexesBlock:
_structureBytes = new byte[Marshal.SizeOf<CompactDiscIndexesHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
CompactDiscIndexesHeader indexesHeader =
Marshal.SpanToStructureLittleEndian<CompactDiscIndexesHeader>(_structureBytes);
2022-03-06 13:29:38 +00:00
if(indexesHeader.identifier != BlockType.CompactDiscIndexesBlock)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Incorrect identifier for compact disc indexes block at position {0}",
2020-02-29 18:03:35 +00:00
entry.offset);
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
_structureBytes = new byte[Marshal.SizeOf<CompactDiscIndexEntry>() * indexesHeader.entries];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
Crc64Context.Data(_structureBytes, out byte[] idsxCrc);
2022-03-06 13:29:38 +00:00
if(BitConverter.ToUInt64(idsxCrc, 0) != indexesHeader.crc64)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...",
BitConverter.ToUInt64(idsxCrc, 0), indexesHeader.crc64);
break;
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
_imageStream.Position -= _structureBytes.Length;
2022-03-06 13:29:38 +00:00
compactDiscIndexes = new List<CompactDiscIndexEntry>();
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found {0} compact disc indexes at position {0}",
2022-03-06 13:29:38 +00:00
indexesHeader.entries, entry.offset);
2022-03-06 13:29:38 +00:00
for(ushort i = 0; i < indexesHeader.entries; i++)
{
_structureBytes = new byte[Marshal.SizeOf<CompactDiscIndexEntry>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
compactDiscIndexes.Add(Marshal.
ByteArrayToStructureLittleEndian<
CompactDiscIndexEntry>(_structureBytes));
}
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(!foundUserDataDdt)
{
AaruConsole.ErrorWriteLine("Could not find user data deduplication table.");
2022-03-06 13:29:38 +00:00
return ErrorNumber.InvalidArgument;
}
2022-03-06 13:29:38 +00:00
_imageInfo.CreationTime = DateTime.FromFileTimeUtc(_header.creationTime);
AaruConsole.DebugWriteLine("Aaru Format plugin", "Image created on {0}", _imageInfo.CreationTime);
_imageInfo.LastModificationTime = DateTime.FromFileTimeUtc(_header.lastWrittenTime);
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Image last written on {0}", _imageInfo.LastModificationTime);
2022-03-06 13:29:38 +00:00
_imageInfo.XmlMediaType = GetXmlMediaType(_header.mediaType);
2019-03-22 21:25:45 +00:00
2022-03-06 13:29:38 +00:00
if(_geometryBlock.identifier != BlockType.GeometryBlock &&
_imageInfo.XmlMediaType == XmlMediaType.BlockMedia)
{
_imageInfo.Cylinders = (uint)(_imageInfo.Sectors / 16 / 63);
_imageInfo.Heads = 16;
_imageInfo.SectorsPerTrack = 63;
}
2022-03-06 13:29:38 +00:00
_imageInfo.ReadableMediaTags.AddRange(_mediaTags.Keys);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
// Initialize caches
_blockCache = new Dictionary<ulong, byte[]>();
_blockHeaderCache = new Dictionary<ulong, BlockHeader>();
_currentCacheSize = 0;
2022-03-06 13:29:38 +00:00
if(!_inMemoryDdt)
_ddtEntryCache = new Dictionary<ulong, ulong>();
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
// Initialize tracks, sessions and partitions
if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc)
{
if(Tracks != null)
{
2022-03-07 07:36:44 +00:00
var leadOutFixed = false;
var sessionPregapFixed = false;
2022-03-06 13:29:38 +00:00
if(_mediaTags.TryGetValue(MediaTagType.CD_FullTOC, out byte[] fullToc))
{
2022-03-07 07:36:44 +00:00
var tmp = new byte[fullToc.Length + 2];
2022-03-06 13:29:38 +00:00
Array.Copy(fullToc, 0, tmp, 2, fullToc.Length);
tmp[0] = (byte)(fullToc.Length >> 8);
tmp[1] = (byte)(fullToc.Length & 0xFF);
2022-03-06 13:29:38 +00:00
FullTOC.CDFullTOC? decodedFullToc = FullTOC.Decode(tmp);
2022-03-06 13:29:38 +00:00
if(decodedFullToc.HasValue)
{
Dictionary<int, long> leadOutStarts = new(); // Lead-out starts
2022-03-06 13:29:38 +00:00
foreach(FullTOC.TrackDataDescriptor trk in
2022-03-16 11:47:00 +00:00
decodedFullToc.Value.TrackDescriptors.Where(trk => trk.ADR is 1 or 4 &&
trk.POINT == 0xA2))
2022-03-06 13:29:38 +00:00
{
int phour, pmin, psec, pframe;
if(trk.PFRAME == 0)
{
2022-03-06 13:29:38 +00:00
pframe = 74;
2022-03-06 13:29:38 +00:00
if(trk.PSEC == 0)
{
2022-03-06 13:29:38 +00:00
psec = 59;
2022-03-06 13:29:38 +00:00
if(trk.PMIN == 0)
{
2022-03-06 13:29:38 +00:00
pmin = 59;
phour = trk.PHOUR - 1;
}
else
{
2022-03-06 13:29:38 +00:00
pmin = trk.PMIN - 1;
phour = trk.PHOUR;
}
}
else
{
2022-03-06 13:29:38 +00:00
psec = trk.PSEC - 1;
pmin = trk.PMIN;
phour = trk.PHOUR;
}
}
2022-03-06 13:29:38 +00:00
else
{
2022-03-06 13:29:38 +00:00
pframe = trk.PFRAME - 1;
psec = trk.PSEC;
pmin = trk.PMIN;
phour = trk.PHOUR;
}
2022-03-06 13:29:38 +00:00
2022-03-07 07:36:44 +00:00
int lastSector = phour * 3600 * 75 + pmin * 60 * 75 + psec * 75 + pframe - 150;
leadOutStarts?.Add(trk.SessionNumber, lastSector + 1);
}
2022-03-06 13:29:38 +00:00
foreach(KeyValuePair<int, long> leadOuts in leadOutStarts)
{
2022-03-06 13:29:38 +00:00
var lastTrackInSession = new Track();
2022-03-06 13:29:38 +00:00
foreach(Track trk in Tracks.Where(trk => trk.Session == leadOuts.Key).
Where(trk => trk.Sequence > lastTrackInSession.Sequence))
lastTrackInSession = trk;
2022-03-06 13:29:38 +00:00
if(lastTrackInSession.Sequence == 0 ||
lastTrackInSession.EndSector == (ulong)leadOuts.Value - 1)
continue;
2022-03-06 13:29:38 +00:00
lastTrackInSession.EndSector = (ulong)leadOuts.Value - 1;
leadOutFixed = true;
}
}
}
2022-03-06 13:29:38 +00:00
if(_header.imageMajorVersion <= 1)
foreach(Track track in Tracks)
{
2022-03-06 13:29:38 +00:00
if(track.Sequence <= 1)
continue;
2022-03-06 13:29:38 +00:00
uint firstTrackNumberInSameSession = Tracks.
Where(t => t.Session == track.Session).
Min(t => t.Sequence);
2022-03-06 13:29:38 +00:00
if(firstTrackNumberInSameSession != track.Sequence)
continue;
2019-03-22 21:25:45 +00:00
2022-03-06 13:29:38 +00:00
if(track.Pregap == 150)
continue;
2022-03-06 13:29:38 +00:00
long dif = (long)track.Pregap - 150;
track.Pregap = (ulong)((long)track.Pregap - dif);
track.StartSector = (ulong)((long)track.StartSector + dif);
2022-03-06 13:29:38 +00:00
sessionPregapFixed = true;
}
2022-03-06 13:29:38 +00:00
if(leadOutFixed)
AaruConsole.ErrorWriteLine("This image has a corrupted track list, convert will fix it.");
2022-03-06 13:29:38 +00:00
if(sessionPregapFixed)
AaruConsole.
ErrorWriteLine("This image has a corrupted track list, a best effort has been tried but may require manual editing or redump.");
}
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
if(Tracks == null ||
Tracks.Count == 0)
{
Tracks = new List<Track>
{
new()
{
2022-03-06 13:29:38 +00:00
BytesPerSector = (int)_imageInfo.SectorSize,
EndSector = _imageInfo.Sectors - 1,
File = imageFilter.Filename,
FileType = "BINARY",
Filter = imageFilter,
RawBytesPerSector = (int)_imageInfo.SectorSize,
Session = 1,
Sequence = 1,
Type = TrackType.Data
}
2022-03-06 13:29:38 +00:00
};
2022-03-06 13:29:38 +00:00
_trackFlags = new Dictionary<byte, byte>
{
{
2022-03-06 13:29:38 +00:00
1, (byte)CdFlags.DataTrack
}
};
2022-03-06 13:29:38 +00:00
_trackIsrcs = new Dictionary<byte, string>();
}
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
Sessions = new List<Session>();
2022-03-07 07:36:44 +00:00
for(var i = 1; i <= Tracks.Max(t => t.Session); i++)
2022-03-06 13:29:38 +00:00
Sessions.Add(new Session
{
Sequence = (ushort)i,
StartTrack = Tracks.Where(t => t.Session == i).Min(t => t.Sequence),
EndTrack = Tracks.Where(t => t.Session == i).Max(t => t.Sequence),
StartSector = Tracks.Where(t => t.Session == i).Min(t => t.StartSector),
EndSector = Tracks.Where(t => t.Session == i).Max(t => t.EndSector)
});
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
foreach(Track track in Tracks.OrderBy(t => t.StartSector))
{
if(track.Sequence == 1)
{
track.Pregap = 150;
track.Indexes[0] = -150;
track.Indexes[1] = (int)track.StartSector;
2022-03-06 13:29:38 +00:00
continue;
}
2019-03-22 21:25:45 +00:00
2022-03-06 13:29:38 +00:00
if(track.Pregap > 0)
{
2022-03-06 13:29:38 +00:00
track.Indexes[0] = (int)track.StartSector;
track.Indexes[1] = (int)(track.StartSector + track.Pregap);
}
2022-03-06 13:29:38 +00:00
else
track.Indexes[1] = (int)track.StartSector;
}
2022-03-06 13:29:38 +00:00
ulong currentTrackOffset = 0;
Partitions = new List<Partition>();
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
foreach(Track track in Tracks.OrderBy(t => t.StartSector))
2020-07-22 13:20:25 +01:00
{
2022-03-06 13:29:38 +00:00
Partitions.Add(new Partition
{
2022-03-06 13:29:38 +00:00
Sequence = track.Sequence,
Type = track.Type.ToString(),
Name = $"Track {track.Sequence}",
Offset = currentTrackOffset,
Start = (ulong)track.Indexes[1],
Size = (track.EndSector - (ulong)track.Indexes[1] + 1) * (ulong)track.BytesPerSector,
Length = track.EndSector - (ulong)track.Indexes[1] + 1,
Scheme = "Optical disc track"
});
currentTrackOffset += (track.EndSector - track.StartSector + 1) * (ulong)track.BytesPerSector;
2020-07-22 13:20:25 +01:00
}
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
Track[] tracks = Tracks.ToArray();
2022-03-06 13:29:38 +00:00
foreach(Track trk in tracks)
{
ErrorNumber errno = ReadSector(trk.StartSector, out byte[] sector);
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
continue;
2022-03-06 13:29:38 +00:00
trk.BytesPerSector = sector.Length;
2022-03-06 13:29:38 +00:00
trk.RawBytesPerSector =
2022-03-07 07:36:44 +00:00
_sectorPrefix != null && _sectorSuffix != null ||
_sectorPrefixDdt != null && _sectorSuffixDdt != null ? 2352 : sector.Length;
2022-03-06 13:29:38 +00:00
if(_sectorSubchannel == null)
continue;
2022-03-06 13:29:38 +00:00
trk.SubchannelFile = trk.File;
trk.SubchannelFilter = trk.Filter;
trk.SubchannelType = TrackSubchannelType.Raw;
}
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
Tracks = tracks.ToList();
2022-03-07 07:36:44 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
if(compactDiscIndexes != null)
foreach(CompactDiscIndexEntry compactDiscIndex in compactDiscIndexes.OrderBy(i => i.Track).
ThenBy(i => i.Index))
{
Track track = Tracks.FirstOrDefault(t => t.Sequence == compactDiscIndex.Track);
2022-03-06 13:29:38 +00:00
if(track is null)
continue;
2022-03-06 13:29:38 +00:00
track.Indexes[compactDiscIndex.Index] = compactDiscIndex.Lba;
}
}
else
{
Tracks = null;
Sessions = null;
Partitions = null;
}
2022-03-06 13:29:38 +00:00
SetMetadataFromTags();
2022-03-06 13:29:38 +00:00
if(_sectorSuffixDdt != null)
EccInit();
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
return ErrorNumber.NoError;
2022-03-16 11:47:00 +00:00
if(_imageInfo.MediaType is MediaType.CD or MediaType.CDDA or MediaType.CDG or MediaType.CDEG or MediaType.CDI
or MediaType.CDROM or MediaType.CDROMXA or MediaType.CDPLUS or MediaType.CDMO
or MediaType.CDR or MediaType.CDRW or MediaType.CDMRW or MediaType.VCD or MediaType.SVCD
or MediaType.PCD or MediaType.DTSCD or MediaType.CDMIDI or MediaType.CDV
or MediaType.CDIREADY or MediaType.FMTOWNS or MediaType.PS1CD or MediaType.PS2CD
or MediaType.MEGACD or MediaType.SATURNCD or MediaType.GDROM or MediaType.GDR
or MediaType.MilCD or MediaType.SuperCDROM2 or MediaType.JaguarCD or MediaType.ThreeDO
or MediaType.PCFX or MediaType.NeoGeoCD or MediaType.CDTV or MediaType.CD32
or MediaType.Playdia or MediaType.Pippin or MediaType.VideoNow
or MediaType.VideoNowColor or MediaType.VideoNowXp or MediaType.CVD)
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
2022-03-06 13:29:38 +00:00
{
foreach(Track track in Tracks)
{
track.Pregap = 0;
track.Indexes?.Clear();
}
}
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer)
{
buffer = null;
2022-03-06 13:29:38 +00:00
return _mediaTags.TryGetValue(tag, out buffer) ? ErrorNumber.NoError : ErrorNumber.NoData;
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSector(ulong sectorAddress, out byte[] buffer)
{
buffer = null;
2022-03-06 13:29:38 +00:00
if(sectorAddress > _imageInfo.Sectors - 1)
return ErrorNumber.OutOfRange;
2022-03-06 13:29:38 +00:00
ulong ddtEntry = GetDdtEntry(sectorAddress);
2022-03-07 07:36:44 +00:00
var offsetMask = (uint)((1 << _shift) - 1);
2022-03-06 13:29:38 +00:00
ulong offset = ddtEntry & offsetMask;
ulong blockOffset = ddtEntry >> _shift;
2022-03-06 13:29:38 +00:00
// Partially written image... as we can't know the real sector size just assume it's common :/
if(ddtEntry == 0)
{
buffer = new byte[_imageInfo.SectorSize];
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-03-06 13:29:38 +00:00
// Check if block is cached
if(_blockCache.TryGetValue(blockOffset, out byte[] block) &&
_blockHeaderCache.TryGetValue(blockOffset, out BlockHeader blockHeader))
{
buffer = new byte[blockHeader.sectorSize];
Array.Copy(block, (long)(offset * blockHeader.sectorSize), buffer, 0, blockHeader.sectorSize);
return ErrorNumber.NoError;
}
2022-03-06 13:29:38 +00:00
// Read block header
_imageStream.Position = (long)blockOffset;
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
_imageStream.EnsureRead(_structureBytes, 0, _structureBytes.Length);
2022-03-06 13:29:38 +00:00
blockHeader = Marshal.SpanToStructureLittleEndian<BlockHeader>(_structureBytes);
// Decompress block
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
ulong decompressedLength;
switch(blockHeader.compression)
{
2022-03-06 13:29:38 +00:00
case CompressionType.None:
block = new byte[blockHeader.length];
_imageStream.EnsureRead(block, 0, (int)blockHeader.length);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
case CompressionType.Lzma:
2022-03-07 07:36:44 +00:00
var compressedBlock = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH];
var lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH];
_imageStream.EnsureRead(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH);
_imageStream.EnsureRead(compressedBlock, 0, compressedBlock.Length);
2022-03-06 13:29:38 +00:00
block = new byte[blockHeader.length];
decompressedLength = (ulong)LZMA.DecodeBuffer(compressedBlock, block, lzmaProperties);
2022-03-06 13:29:38 +00:00
if(decompressedLength != blockHeader.length)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Error decompressing block, should be {0} bytes but got {1} bytes.",
blockHeader.length, decompressedLength);
2022-03-06 13:29:38 +00:00
return ErrorNumber.InOutError;
}
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
break;
case CompressionType.Flac:
2022-03-07 07:36:44 +00:00
var flacBlock = new byte[blockHeader.cmpLength];
_imageStream.EnsureRead(flacBlock, 0, flacBlock.Length);
2022-03-06 13:29:38 +00:00
block = new byte[blockHeader.length];
decompressedLength = (ulong)FLAC.DecodeBuffer(flacBlock, block);
if(decompressedLength != blockHeader.length)
{
AaruConsole.DebugWriteLine("Aaru Format plugin",
"Error decompressing block, should be {0} bytes but got {1} bytes.",
blockHeader.length, decompressedLength);
2022-03-06 13:29:38 +00:00
return ErrorNumber.InOutError;
}
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
break;
default: return ErrorNumber.NotSupported;
}
2022-03-06 13:29:38 +00:00
// Check if cache needs to be emptied
if(_currentCacheSize + blockHeader.length >= MAX_CACHE_SIZE)
{
2022-03-06 13:29:38 +00:00
_currentCacheSize = 0;
_blockHeaderCache = new Dictionary<ulong, BlockHeader>();
_blockCache = new Dictionary<ulong, byte[]>();
}
2022-03-06 13:29:38 +00:00
// Add block to cache
_currentCacheSize += blockHeader.length;
_blockHeaderCache.Add(blockOffset, blockHeader);
_blockCache.Add(blockOffset, block);
2022-03-06 13:29:38 +00:00
buffer = new byte[blockHeader.sectorSize];
Array.Copy(block, (long)(offset * blockHeader.sectorSize), buffer, 0, blockHeader.sectorSize);
2022-03-06 13:29:38 +00:00
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false));
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectorTag(ulong sectorAddress, SectorTagType tag, out byte[] buffer) =>
ReadSectorsTag(sectorAddress, 1, tag, out buffer);
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSector(ulong sectorAddress, uint track, out byte[] buffer)
{
buffer = null;
2022-03-06 13:29:38 +00:00
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
return ErrorNumber.NotSupported;
2022-03-06 13:29:38 +00:00
Track trk = Tracks.FirstOrDefault(t => t.Sequence == track);
2022-03-06 13:29:38 +00:00
return trk?.Sequence != track ? ErrorNumber.SectorNotFound
: ReadSector(trk.StartSector + sectorAddress, out buffer);
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag, out byte[] buffer)
{
buffer = null;
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
return ErrorNumber.NotSupported;
2022-03-06 13:29:38 +00:00
Track trk = Tracks.FirstOrDefault(t => t.Sequence == track);
2022-03-06 13:29:38 +00:00
return trk?.Sequence != track ? ErrorNumber.SectorNotFound
: ReadSectorTag(trk.StartSector + sectorAddress, tag, out buffer);
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectors(ulong sectorAddress, uint length, out byte[] buffer)
{
buffer = null;
2022-03-06 13:29:38 +00:00
if(sectorAddress > _imageInfo.Sectors - 1)
return ErrorNumber.OutOfRange;
2022-03-06 13:29:38 +00:00
if(sectorAddress + length > _imageInfo.Sectors)
return ErrorNumber.OutOfRange;
2022-03-06 13:29:38 +00:00
var ms = new MemoryStream();
2022-03-06 13:29:38 +00:00
for(uint i = 0; i < length; i++)
{
ErrorNumber errno = ReadSector(sectorAddress + i, out byte[] sector);
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
return errno;
2022-03-06 13:29:38 +00:00
ms.Write(sector, 0, sector.Length);
}
2022-03-06 13:29:38 +00:00
buffer = ms.ToArray();
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag, out byte[] buffer)
{
uint sectorOffset;
uint sectorSize;
uint sectorSkip;
byte[] dataSource;
buffer = null;
2022-03-06 13:29:38 +00:00
if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc)
{
Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && sectorAddress <= t.EndSector);
2022-03-06 13:29:38 +00:00
if(trk is null)
return ErrorNumber.SectorNotFound;
2022-03-06 13:29:38 +00:00
if(trk.Sequence == 0 &&
trk.StartSector == 0 &&
trk.EndSector == 0)
return ErrorNumber.SectorNotFound;
2022-03-06 13:29:38 +00:00
if(trk.Type == TrackType.Data)
return ErrorNumber.NotSupported;
2022-03-06 13:29:38 +00:00
switch(tag)
{
case SectorTagType.CdSectorEcc:
case SectorTagType.CdSectorEccP:
case SectorTagType.CdSectorEccQ:
case SectorTagType.CdSectorEdc:
case SectorTagType.CdSectorHeader:
case SectorTagType.CdSectorSubchannel:
case SectorTagType.CdSectorSubHeader:
case SectorTagType.CdSectorSync:
case SectorTagType.DvdCmi:
case SectorTagType.DvdTitleKey:
case SectorTagType.DvdTitleKeyDecrypted: break;
case SectorTagType.CdTrackFlags:
if(!_trackFlags.TryGetValue((byte)sectorAddress, out byte flags))
return ErrorNumber.NoData;
buffer = new[]
{
flags
};
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
case SectorTagType.CdTrackIsrc:
if(!_trackIsrcs.TryGetValue((byte)sectorAddress, out string isrc))
return ErrorNumber.NoData;
2022-03-06 13:29:38 +00:00
buffer = Encoding.UTF8.GetBytes(isrc);
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
default: return ErrorNumber.NotSupported;
}
2022-03-06 13:29:38 +00:00
switch(trk.Type)
{
case TrackType.CdMode1:
switch(tag)
{
2022-03-06 13:29:38 +00:00
case SectorTagType.CdSectorSync:
{
2022-03-06 13:29:38 +00:00
sectorOffset = 0;
sectorSize = 12;
sectorSkip = 4;
dataSource = _sectorPrefix;
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
case SectorTagType.CdSectorHeader:
{
sectorOffset = 12;
sectorSize = 4;
sectorSkip = 2336;
dataSource = _sectorPrefix;
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
case SectorTagType.CdSectorSubHeader: return ErrorNumber.NotSupported;
case SectorTagType.CdSectorEcc:
{
sectorOffset = 12;
sectorSize = 276;
sectorSkip = 0;
dataSource = _sectorSuffix;
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
case SectorTagType.CdSectorEccP:
{
sectorOffset = 12;
sectorSize = 172;
sectorSkip = 104;
dataSource = _sectorSuffix;
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
case SectorTagType.CdSectorEccQ:
{
sectorOffset = 184;
sectorSize = 104;
sectorSkip = 0;
dataSource = _sectorSuffix;
break;
}
case SectorTagType.CdSectorEdc:
{
sectorOffset = 0;
sectorSize = 4;
sectorSkip = 284;
dataSource = _sectorSuffix;
break;
}
case SectorTagType.CdSectorSubchannel:
{
sectorOffset = 0;
sectorSize = 96;
sectorSkip = 0;
dataSource = _sectorSubchannel;
break;
}
default: return ErrorNumber.NotSupported;
}
2022-03-06 13:29:38 +00:00
break;
case TrackType.CdMode2Formless:
case TrackType.CdMode2Form1:
case TrackType.CdMode2Form2:
{
switch(tag)
{
2022-03-06 13:29:38 +00:00
case SectorTagType.CdSectorSync:
{
2022-03-06 13:29:38 +00:00
sectorOffset = 0;
sectorSize = 12;
sectorSkip = 4;
dataSource = _sectorPrefix;
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
case SectorTagType.CdSectorHeader:
{
sectorOffset = 12;
sectorSize = 4;
sectorSkip = 0;
dataSource = _sectorPrefix;
break;
}
2022-03-06 13:29:38 +00:00
case SectorTagType.CdSectorSubHeader:
{
sectorOffset = 0;
sectorSize = 8;
sectorSkip = 0;
dataSource = _mode2Subheaders;
break;
}
// These could be implemented
case SectorTagType.CdSectorEcc:
case SectorTagType.CdSectorEccP:
case SectorTagType.CdSectorEccQ:
case SectorTagType.CdSectorEdc: return ErrorNumber.NotSupported;
case SectorTagType.CdSectorSubchannel:
{
sectorOffset = 0;
sectorSize = 96;
sectorSkip = 0;
dataSource = _sectorSubchannel;
break;
}
default: return ErrorNumber.NotSupported;
}
2022-03-06 13:29:38 +00:00
break;
}
case TrackType.Audio:
{
switch(tag)
{
case SectorTagType.CdSectorSubchannel:
{
sectorOffset = 0;
sectorSize = 96;
sectorSkip = 0;
dataSource = _sectorSubchannel;
break;
}
default: return ErrorNumber.NotSupported;
}
break;
}
case TrackType.Data:
{
if(_imageInfo.MediaType == MediaType.DVDROM)
switch(tag)
{
2022-03-06 13:29:38 +00:00
case SectorTagType.DvdCmi:
{
2022-03-06 13:29:38 +00:00
sectorOffset = 0;
sectorSize = 1;
sectorSkip = 5;
dataSource = _sectorCpiMai;
2022-03-06 13:29:38 +00:00
break;
}
case SectorTagType.DvdTitleKey:
{
sectorOffset = 1;
sectorSize = 5;
sectorSkip = 0;
dataSource = _sectorCpiMai;
2022-03-06 13:29:38 +00:00
break;
}
case SectorTagType.DvdTitleKeyDecrypted:
{
sectorOffset = 0;
sectorSize = 5;
sectorSkip = 0;
dataSource = _sectorDecryptedTitleKey;
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
default: return ErrorNumber.NotSupported;
}
2022-03-06 13:29:38 +00:00
else
return ErrorNumber.NotSupported;
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
default: return ErrorNumber.NotSupported;
}
}
else
return ErrorNumber.NoData;
2022-03-06 13:29:38 +00:00
if(dataSource == null)
return ErrorNumber.NotSupported;
2022-03-06 13:29:38 +00:00
buffer = new byte[sectorSize * length];
2022-03-06 13:29:38 +00:00
if(sectorOffset == 0 &&
sectorSkip == 0)
{
Array.Copy(dataSource, (long)(sectorAddress * sectorSize), buffer, 0, length * sectorSize);
return ErrorNumber.NoError;
}
2022-03-07 07:36:44 +00:00
for(var i = 0; i < length; i++)
2022-03-06 13:29:38 +00:00
Array.Copy(dataSource, (long)(sectorAddress * (sectorOffset + sectorSize + sectorSkip)), buffer,
i * sectorSize, sectorSize);
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectors(ulong sectorAddress, uint length, uint track, out byte[] buffer)
{
buffer = null;
2022-03-06 13:29:38 +00:00
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
return ErrorNumber.NotSupported;
2022-03-06 13:29:38 +00:00
Track trk = Tracks.FirstOrDefault(t => t.Sequence == track);
2022-03-06 13:29:38 +00:00
if(trk?.Sequence != track)
return ErrorNumber.SectorNotFound;
2022-03-06 13:29:38 +00:00
return trk.StartSector + sectorAddress + length > trk.EndSector + 1 ? ErrorNumber.OutOfRange
: ReadSectors(trk.StartSector + sectorAddress, length, out buffer);
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag,
out byte[] buffer)
{
buffer = null;
2022-03-06 13:29:38 +00:00
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
return ErrorNumber.NotSupported;
2022-03-06 13:29:38 +00:00
Track trk = Tracks.FirstOrDefault(t => t.Sequence == track);
2022-03-06 13:29:38 +00:00
return trk?.Sequence != track
? ErrorNumber.SectorNotFound
: trk.StartSector + sectorAddress + length > trk.EndSector + 1
? ErrorNumber.OutOfRange
: ReadSectorsTag(trk.StartSector + sectorAddress, length, tag, out buffer);
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectorLong(ulong sectorAddress, out byte[] buffer)
{
buffer = null;
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
switch(_imageInfo.XmlMediaType)
{
case XmlMediaType.OpticalDisc:
2022-03-07 07:36:44 +00:00
Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && sectorAddress <= t.EndSector);
2022-03-06 13:29:38 +00:00
if(trk is null)
return ErrorNumber.SectorNotFound;
2022-03-06 13:29:38 +00:00
if(trk.Sequence == 0 &&
trk.StartSector == 0 &&
trk.EndSector == 0)
return ErrorNumber.SectorNotFound;
2022-03-06 13:29:38 +00:00
if((_sectorSuffix == null || _sectorPrefix == null) &&
(_sectorSuffixDdt == null || _sectorPrefixDdt == null))
return ReadSector(sectorAddress, out buffer);
2022-03-06 13:29:38 +00:00
buffer = new byte[2352];
2022-11-13 19:16:14 +00:00
ErrorNumber errno = ReadSector(sectorAddress, out byte[] data);
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
return errno;
switch(trk.Type)
{
case TrackType.Audio:
case TrackType.Data:
buffer = data;
return ErrorNumber.NoError;
case TrackType.CdMode1:
Array.Copy(data, 0, buffer, 16, 2048);
2022-03-06 13:29:38 +00:00
if(_sectorPrefix != null)
Array.Copy(_sectorPrefix, (int)sectorAddress * 16, buffer, 0, 16);
else if(_sectorPrefixDdt != null)
{
if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct)
ReconstructPrefix(ref buffer, trk.Type, (long)sectorAddress);
2022-03-07 07:36:44 +00:00
else if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.NotDumped ||
_sectorPrefixDdt[sectorAddress] == 0)
{
2022-03-06 13:29:38 +00:00
// Do nothing
}
else
{
uint prefixPosition = ((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16;
2022-03-06 13:29:38 +00:00
if(prefixPosition > _sectorPrefixMs.Length)
return ErrorNumber.InvalidArgument;
2022-03-06 13:29:38 +00:00
_sectorPrefixMs.Position = prefixPosition;
_sectorPrefixMs.EnsureRead(buffer, 0, 16);
}
2022-03-06 13:29:38 +00:00
}
else
return ErrorNumber.InvalidArgument;
2022-03-06 13:29:38 +00:00
if(_sectorSuffix != null)
Array.Copy(_sectorSuffix, (int)sectorAddress * 288, buffer, 2064, 288);
else if(_sectorSuffixDdt != null)
{
if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct)
ReconstructEcc(ref buffer, trk.Type);
2022-03-07 07:36:44 +00:00
else if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.NotDumped ||
_sectorSuffixDdt[sectorAddress] == 0)
{
2022-03-06 13:29:38 +00:00
// Do nothing
}
else
{
uint suffixPosition = ((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288;
2022-03-06 13:29:38 +00:00
if(suffixPosition > _sectorSuffixMs.Length)
return ErrorNumber.InvalidArgument;
2022-03-06 13:29:38 +00:00
_sectorSuffixMs.Position = suffixPosition;
_sectorSuffixMs.EnsureRead(buffer, 2064, 288);
}
2022-03-06 13:29:38 +00:00
}
else
return ErrorNumber.InvalidArgument;
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
case TrackType.CdMode2Formless:
case TrackType.CdMode2Form1:
case TrackType.CdMode2Form2:
if(_sectorPrefix != null)
Array.Copy(_sectorPrefix, (int)sectorAddress * 16, buffer, 0, 16);
else if(_sectorPrefixMs != null)
{
if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct)
ReconstructPrefix(ref buffer, trk.Type, (long)sectorAddress);
2022-03-07 07:36:44 +00:00
else if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.NotDumped ||
_sectorPrefixDdt[sectorAddress] == 0)
{
2022-03-06 13:29:38 +00:00
// Do nothing
}
else
{
uint prefixPosition = ((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16;
2022-03-06 13:29:38 +00:00
if(prefixPosition > _sectorPrefixMs.Length)
return ErrorNumber.InvalidArgument;
2022-03-06 13:29:38 +00:00
_sectorPrefixMs.Position = prefixPosition;
_sectorPrefixMs.EnsureRead(buffer, 0, 16);
}
2022-03-06 13:29:38 +00:00
}
else
return ErrorNumber.InvalidArgument;
2022-03-06 13:29:38 +00:00
if(_mode2Subheaders != null &&
_sectorSuffixDdt != null)
{
Array.Copy(_mode2Subheaders, (int)sectorAddress * 8, buffer, 16, 8);
switch(_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK)
{
2022-03-06 13:29:38 +00:00
case (uint)CdFixFlags.Mode2Form1Ok:
Array.Copy(data, 0, buffer, 24, 2048);
ReconstructEcc(ref buffer, TrackType.CdMode2Form1);
2022-03-06 13:29:38 +00:00
break;
case (uint)CdFixFlags.Mode2Form2Ok:
case (uint)CdFixFlags.Mode2Form2NoCrc:
{
2022-03-06 13:29:38 +00:00
Array.Copy(data, 0, buffer, 24, 2324);
2022-03-06 13:29:38 +00:00
if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) ==
(uint)CdFixFlags.Mode2Form2Ok)
ReconstructEcc(ref buffer, TrackType.CdMode2Form2);
2022-03-06 13:29:38 +00:00
break;
}
default:
{
2022-03-07 07:36:44 +00:00
if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.NotDumped ||
_sectorSuffixDdt[sectorAddress] == 0)
2022-03-06 13:29:38 +00:00
{
// Do nothing
}
2022-03-06 13:29:38 +00:00
else // Mode 2 where EDC failed
{
2022-03-06 13:29:38 +00:00
// Incorrectly written images
if(data.Length == 2328)
Array.Copy(data, 0, buffer, 24, 2328);
else
{
2022-03-06 13:29:38 +00:00
bool form2 = (buffer[18] & 0x20) == 0x20 || (buffer[22] & 0x20) == 0x20;
uint suffixPosition =
((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288;
if(suffixPosition > _sectorSuffixMs.Length)
return ErrorNumber.InvalidArgument;
2022-03-06 13:29:38 +00:00
_sectorSuffixMs.Position = suffixPosition;
_sectorSuffixMs.EnsureRead(buffer, form2 ? 2348 : 2072, form2 ? 4 : 280);
2022-03-06 13:29:38 +00:00
Array.Copy(data, 0, buffer, 24, form2 ? 2324 : 2048);
}
}
2022-03-06 13:29:38 +00:00
break;
}
}
2022-03-06 13:29:38 +00:00
}
else if(_mode2Subheaders != null)
{
Array.Copy(_mode2Subheaders, (int)sectorAddress * 8, buffer, 16, 8);
Array.Copy(data, 0, buffer, 24, 2328);
}
else
Array.Copy(data, 0, buffer, 16, 2336);
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-03-06 13:29:38 +00:00
break;
case XmlMediaType.BlockMedia:
switch(_imageInfo.MediaType)
{
case MediaType.AppleFileWare:
case MediaType.AppleProfile:
case MediaType.AppleSonySS:
case MediaType.AppleSonyDS:
case MediaType.AppleWidget:
case MediaType.PriamDataTower: return ReadSectorsLong(sectorAddress, 1, out buffer);
}
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
return ErrorNumber.NotSupported;
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectorLong(ulong sectorAddress, uint track, out byte[] buffer)
{
buffer = null;
2022-03-06 13:29:38 +00:00
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
return ErrorNumber.NotSupported;
2022-03-06 13:29:38 +00:00
Track trk = Tracks.FirstOrDefault(t => t.Sequence == track);
2022-03-06 13:29:38 +00:00
return trk?.Sequence != track ? ErrorNumber.SectorNotFound
: ReadSectorLong(trk.StartSector + sectorAddress, out buffer);
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public ErrorNumber ReadSectorsLong(ulong sectorAddress, uint length, out byte[] buffer)
{
byte[] data;
ErrorNumber errno;
buffer = null;
2020-07-22 13:20:25 +01:00
2022-03-06 13:29:38 +00:00
switch(_imageInfo.XmlMediaType)
{
case XmlMediaType.OpticalDisc:
2022-03-07 07:36:44 +00:00
Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && sectorAddress <= t.EndSector);
2022-03-06 13:29:38 +00:00
if(trk is null)
return ErrorNumber.SectorNotFound;
2022-03-06 13:29:38 +00:00
if(trk.Sequence == 0 &&
trk.StartSector == 0 &&
trk.EndSector == 0)
return ErrorNumber.SectorNotFound;
2022-03-06 13:29:38 +00:00
if(sectorAddress + length > trk.EndSector + 1)
return ErrorNumber.OutOfRange;
2022-03-06 13:29:38 +00:00
switch(trk.Type)
{
// These types only contain user data
case TrackType.Audio:
case TrackType.Data: return ReadSectors(sectorAddress, length, out buffer);
2022-03-06 13:29:38 +00:00
// Join prefix (sync, header) with user data with suffix (edc, ecc p, ecc q)
case TrackType.CdMode1:
if(_sectorPrefix != null &&
_sectorSuffix != null)
{
buffer = new byte[2352 * length];
errno = ReadSectors(sectorAddress, length, out data);
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
return errno;
2022-03-06 13:29:38 +00:00
for(uint i = 0; i < length; i++)
{
2022-03-07 07:36:44 +00:00
Array.Copy(_sectorPrefix, (int)((sectorAddress + i) * 16), buffer, (int)(i * 2352), 16);
2022-03-06 13:29:38 +00:00
Array.Copy(data, (int)(i * 2048), buffer, (int)(i * 2352) + 16, 2048);
2022-03-06 13:29:38 +00:00
Array.Copy(_sectorSuffix, (int)((sectorAddress + i) * 288), buffer,
(int)(i * 2352) + 2064, 288);
}
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-11-14 00:59:59 +00:00
2022-11-15 01:35:06 +00:00
if(_sectorPrefixDdt == null ||
_sectorSuffixDdt == null)
return ReadSectors(sectorAddress, length, out buffer);
2022-11-15 01:35:06 +00:00
buffer = new byte[2352 * length];
2022-11-15 01:35:06 +00:00
for(uint i = 0; i < length; i++)
{
errno = ReadSectorLong(sectorAddress + i, out byte[] temp);
2022-11-15 01:35:06 +00:00
if(errno != ErrorNumber.NoError)
return errno;
2022-11-15 01:35:06 +00:00
Array.Copy(temp, 0, buffer, 2352 * i, 2352);
2022-03-06 13:29:38 +00:00
}
2022-11-14 00:59:59 +00:00
2022-11-15 01:35:06 +00:00
return ErrorNumber.NoError;
2022-03-06 13:29:38 +00:00
// Join prefix (sync, header) with user data
case TrackType.CdMode2Formless:
case TrackType.CdMode2Form1:
case TrackType.CdMode2Form2:
if(_sectorPrefix != null &&
_sectorSuffix != null)
{
buffer = new byte[2352 * length];
errno = ReadSectors(sectorAddress, length, out data);
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
return errno;
2022-03-06 13:29:38 +00:00
for(uint i = 0; i < length; i++)
{
2022-03-07 07:36:44 +00:00
Array.Copy(_sectorPrefix, (int)((sectorAddress + i) * 16), buffer, (int)(i * 2352), 16);
2022-03-06 13:29:38 +00:00
Array.Copy(data, (int)(i * 2336), buffer, (int)(i * 2352) + 16, 2336);
}
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-11-14 00:59:59 +00:00
2022-11-15 01:35:06 +00:00
if(_sectorPrefixDdt == null ||
_sectorSuffixDdt == null)
return ReadSectors(sectorAddress, length, out buffer);
2022-11-15 01:35:06 +00:00
buffer = new byte[2352 * length];
2022-11-15 01:35:06 +00:00
for(uint i = 0; i < length; i++)
{
errno = ReadSectorLong(sectorAddress + i, out byte[] temp);
2022-11-15 01:35:06 +00:00
if(errno != ErrorNumber.NoError)
return errno;
2022-11-15 01:35:06 +00:00
Array.Copy(temp, 0, buffer, 2352 * i, 2352);
2022-03-06 13:29:38 +00:00
}
2022-11-15 01:35:06 +00:00
return ErrorNumber.NoError;
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
break;
case XmlMediaType.BlockMedia:
switch(_imageInfo.MediaType)
{
// Join user data with tags
case MediaType.AppleFileWare:
case MediaType.AppleProfile:
case MediaType.AppleSonySS:
case MediaType.AppleSonyDS:
case MediaType.AppleWidget:
case MediaType.PriamDataTower:
if(_sectorSubchannel == null)
return ReadSector(sectorAddress, out buffer);
uint tagSize = 0;
switch(_imageInfo.MediaType)
{
case MediaType.AppleFileWare:
case MediaType.AppleProfile:
case MediaType.AppleWidget:
tagSize = 20;
2022-03-06 13:29:38 +00:00
break;
case MediaType.AppleSonySS:
case MediaType.AppleSonyDS:
tagSize = 12;
2022-03-06 13:29:38 +00:00
break;
case MediaType.PriamDataTower:
tagSize = 24;
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
uint sectorSize = 512 + tagSize;
errno = ReadSectors(sectorAddress, length, out data);
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
return errno;
2022-03-06 13:29:38 +00:00
buffer = new byte[(sectorSize + 512) * length];
2022-03-06 13:29:38 +00:00
for(uint i = 0; i < length; i++)
{
Array.Copy(_sectorSubchannel, (int)((sectorAddress + i) * tagSize), buffer,
2022-03-07 07:36:44 +00:00
(int)(i * sectorSize + 512), tagSize);
2022-03-06 13:29:38 +00:00
Array.Copy(data, (int)((sectorAddress + i) * 512), buffer, (int)(i * 512), 512);
}
2022-03-06 13:29:38 +00:00
return ErrorNumber.NoError;
}
2022-03-06 13:29:38 +00:00
break;
}
2022-03-06 13:29:38 +00:00
return ErrorNumber.NotSupported;
}
/// <inheritdoc />
public ErrorNumber ReadSectorsLong(ulong sectorAddress, uint length, uint track, out byte[] buffer)
{
buffer = null;
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
return ErrorNumber.NotSupported;
Track trk = Tracks.FirstOrDefault(t => t.Sequence == track);
2022-03-06 13:29:38 +00:00
return trk?.Sequence != track
? ErrorNumber.SectorNotFound
: trk.StartSector + sectorAddress + length > trk.EndSector + 1
? ErrorNumber.OutOfRange
: ReadSectorsLong(trk.StartSector + sectorAddress, length, out buffer);
}
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
2022-03-07 07:36:44 +00:00
public List<Track> GetSessionTracks(Session session) => Tracks.Where(t => t.Sequence == session.Sequence).ToList();
2022-03-06 13:29:38 +00:00
/// <inheritdoc />
public List<Track> GetSessionTracks(ushort session) => Tracks.Where(t => t.Sequence == session).ToList();
}