2018-12-01 19:38:43 +00:00
|
|
|
// /***************************************************************************
|
2020-02-27 12:31:25 +00:00
|
|
|
// Aaru Data Preservation Suite
|
2018-07-23 23:25:43 +01:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Filename : Write.cs
|
|
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
|
|
|
//
|
|
|
|
|
// Component : Disk image plugins.
|
|
|
|
|
//
|
|
|
|
|
// --[ Description ] ----------------------------------------------------------
|
|
|
|
|
//
|
2020-02-27 14:01:13 +00:00
|
|
|
// Writes Aaru Format disk images.
|
2018-07-23 23:25:43 +01:00
|
|
|
//
|
|
|
|
|
// --[ 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/>.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2020-12-31 23:08:23 +00:00
|
|
|
// Copyright © 2011-2021 Natalia Portillo
|
2021-01-15 22:20:18 +01:00
|
|
|
// Copyright © 2020-2021 Rebecca Wallander
|
2018-07-23 23:25:43 +01:00
|
|
|
// ****************************************************************************/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
2019-03-15 22:07:10 +00:00
|
|
|
using System.Runtime.InteropServices;
|
2018-07-23 23:25:43 +01:00
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
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.Exceptions;
|
|
|
|
|
using Aaru.CommonTypes.Structs;
|
|
|
|
|
using Aaru.Console;
|
|
|
|
|
using Aaru.Decoders;
|
2020-07-20 15:43:52 +01:00
|
|
|
using Aaru.Helpers;
|
2020-02-29 18:03:35 +00:00
|
|
|
using CUETools.Codecs;
|
2020-04-22 17:44:05 +01:00
|
|
|
using CUETools.Codecs.Flake;
|
2018-07-23 23:25:43 +01:00
|
|
|
using Schemas;
|
|
|
|
|
using SharpCompress.Compressors.LZMA;
|
2020-02-27 00:33:26 +00:00
|
|
|
using Marshal = Aaru.Helpers.Marshal;
|
|
|
|
|
using TrackType = Aaru.CommonTypes.Enums.TrackType;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-02-27 00:33:26 +00:00
|
|
|
namespace Aaru.DiscImages
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
public sealed partial class AaruFormat
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
public bool Create(string path, MediaType mediaType, Dictionary<string, string> options, ulong sectors,
|
2020-01-12 01:02:06 +00:00
|
|
|
uint sectorSize)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
uint sectorsPerBlock;
|
|
|
|
|
uint dictionary;
|
|
|
|
|
uint maxDdtSize;
|
|
|
|
|
bool doMd5;
|
|
|
|
|
bool doSha1;
|
|
|
|
|
bool doSha256;
|
|
|
|
|
bool doSpamsum;
|
|
|
|
|
|
|
|
|
|
if(options != null)
|
|
|
|
|
{
|
|
|
|
|
if(options.TryGetValue("sectors_per_block", out string tmpValue))
|
|
|
|
|
{
|
|
|
|
|
if(!uint.TryParse(tmpValue, out sectorsPerBlock))
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Invalid value for sectors_per_block option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
|
|
|
|
sectorsPerBlock = 4096;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(options.TryGetValue("dictionary", out tmpValue))
|
|
|
|
|
{
|
|
|
|
|
if(!uint.TryParse(tmpValue, out dictionary))
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Invalid value for dictionary option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
|
|
|
|
dictionary = 1 << 25;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(options.TryGetValue("max_ddt_size", out tmpValue))
|
|
|
|
|
{
|
|
|
|
|
if(!uint.TryParse(tmpValue, out maxDdtSize))
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Invalid value for max_ddt_size option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
|
|
|
|
maxDdtSize = 256;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(options.TryGetValue("md5", out tmpValue))
|
|
|
|
|
{
|
|
|
|
|
if(!bool.TryParse(tmpValue, out doMd5))
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Invalid value for md5 option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-08-28 02:06:20 +01:00
|
|
|
doMd5 = true;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(options.TryGetValue("sha1", out tmpValue))
|
|
|
|
|
{
|
|
|
|
|
if(!bool.TryParse(tmpValue, out doSha1))
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Invalid value for sha1 option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-08-28 02:06:20 +01:00
|
|
|
doSha1 = true;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(options.TryGetValue("sha256", out tmpValue))
|
|
|
|
|
{
|
|
|
|
|
if(!bool.TryParse(tmpValue, out doSha256))
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Invalid value for sha256 option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-08-28 02:06:20 +01:00
|
|
|
doSha256 = true;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(options.TryGetValue("spamsum", out tmpValue))
|
|
|
|
|
{
|
|
|
|
|
if(!bool.TryParse(tmpValue, out doSpamsum))
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Invalid value for spamsum option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
|
|
|
|
doSpamsum = false;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(options.TryGetValue("deduplicate", out tmpValue))
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!bool.TryParse(tmpValue, out _deduplicate))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = "Invalid value for deduplicate option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-07-20 21:11:32 +01:00
|
|
|
_deduplicate = true;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-03-10 00:04:19 +00:00
|
|
|
if(options.TryGetValue("compress", out tmpValue))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!bool.TryParse(tmpValue, out _compress))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-03-10 00:04:19 +00:00
|
|
|
ErrorMessage = "Invalid value for compress option";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-07-20 21:11:32 +01:00
|
|
|
_compress = true;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sectorsPerBlock = 4096;
|
|
|
|
|
dictionary = 1 << 25;
|
|
|
|
|
maxDdtSize = 256;
|
2020-08-28 02:06:20 +01:00
|
|
|
doMd5 = true;
|
|
|
|
|
doSha1 = true;
|
|
|
|
|
doSha256 = true;
|
2018-07-23 23:25:43 +01:00
|
|
|
doSpamsum = false;
|
2020-07-20 21:11:32 +01:00
|
|
|
_deduplicate = true;
|
|
|
|
|
_compress = true;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This really, cannot happen
|
|
|
|
|
if(!SupportedMediaTypes.Contains(mediaType))
|
|
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
ErrorMessage = $"Unsupported media format {mediaType}";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate shift
|
2020-07-20 21:11:32 +01:00
|
|
|
_shift = 0;
|
2018-07-23 23:25:43 +01:00
|
|
|
uint oldSectorsPerBlock = sectorsPerBlock;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
while(sectorsPerBlock > 1)
|
|
|
|
|
{
|
|
|
|
|
sectorsPerBlock >>= 1;
|
2020-07-20 21:11:32 +01:00
|
|
|
_shift++;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Got a shift of {0} for {1} sectors per block", _shift,
|
2020-02-29 18:03:35 +00:00
|
|
|
oldSectorsPerBlock);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageInfo = new ImageInfo
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
MediaType = mediaType,
|
|
|
|
|
SectorSize = sectorSize,
|
|
|
|
|
Sectors = sectors,
|
2018-07-23 23:25:43 +01:00
|
|
|
XmlMediaType = GetXmlMediaType(mediaType)
|
|
|
|
|
};
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
try
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
|
2020-01-12 01:02:06 +00:00
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
catch(IOException e)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = $"Could not create new image file, exception {e.Message}";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if appending to an existing image
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_imageStream.Length > Marshal.SizeOf<AaruHeader>())
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<AaruHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_header = Marshal.ByteArrayToStructureLittleEndian<AaruHeader>(_structureBytes);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_header.identifier != DIC_MAGIC &&
|
|
|
|
|
_header.identifier != AARU_MAGIC)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-02-27 14:01:13 +00:00
|
|
|
ErrorMessage = "Cannot append to a non Aaru Format image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_header.imageMajorVersion > AARUFMT_VERSION)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
ErrorMessage = $"Cannot append to an unknown image version {_header.imageMajorVersion}";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_header.mediaType != mediaType)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage =
|
2020-07-20 21:11:32 +01:00
|
|
|
$"Cannot write a media with type {mediaType} to an image with type {_header.mediaType}";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_header = new AaruHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = AARU_MAGIC,
|
|
|
|
|
mediaType = mediaType,
|
|
|
|
|
creationTime = DateTime.UtcNow.ToFileTimeUtc()
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(new byte[Marshal.SizeOf<AaruHeader>()], 0, Marshal.SizeOf<AaruHeader>());
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_header.application = "Aaru";
|
|
|
|
|
_header.imageMajorVersion = AARUFMT_VERSION_V1;
|
|
|
|
|
_header.imageMinorVersion = 0;
|
|
|
|
|
_header.applicationMajorVersion = (byte)typeof(AaruFormat).Assembly.GetName().Version.Major;
|
|
|
|
|
_header.applicationMinorVersion = (byte)typeof(AaruFormat).Assembly.GetName().Version.Minor;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-11-05 01:21:04 +00:00
|
|
|
// Initialize tables
|
|
|
|
|
_index = new List<IndexEntry>();
|
|
|
|
|
_mediaTags = new Dictionary<MediaTagType, byte[]>();
|
|
|
|
|
_checksumProvider = SHA256.Create();
|
|
|
|
|
_deduplicationTable = new Dictionary<string, ulong>();
|
|
|
|
|
_trackIsrcs = new Dictionary<byte, string>();
|
|
|
|
|
_trackFlags = new Dictionary<byte, byte>();
|
|
|
|
|
_imageInfo.ReadableSectorTags = new List<SectorTagType>();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
// If there exists an index, we are appending, so read index
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_header.indexOffset > 0)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-11-05 01:21:04 +00:00
|
|
|
List<CompactDiscIndexEntry> compactDiscIndexes = null;
|
|
|
|
|
|
|
|
|
|
// Initialize caches
|
|
|
|
|
_blockCache = new Dictionary<ulong, byte[]>();
|
|
|
|
|
_blockHeaderCache = new Dictionary<ulong, BlockHeader>();
|
|
|
|
|
_currentCacheSize = 0;
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Can't calculate checksum of an appended image
|
2020-07-20 21:11:32 +01:00
|
|
|
_md5Provider = null;
|
|
|
|
|
_sha1Provider = null;
|
|
|
|
|
_sha256Provider = null;
|
|
|
|
|
_spamsumProvider = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Position = (long)_header.indexOffset;
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<IndexHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
IndexHeader idxHeader = Marshal.SpanToStructureLittleEndian<IndexHeader>(_structureBytes);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(idxHeader.identifier != BlockType.Index)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Index not found in existing image, cannot continue";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries",
|
2020-07-20 21:11:32 +01:00
|
|
|
_header.indexOffset, idxHeader.entries);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
for(ushort i = 0; i < idxHeader.entries; i++)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<IndexEntry>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
IndexEntry entry = Marshal.SpanToStructureLittleEndian<IndexEntry>(_structureBytes);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Block type {0} with data type {1} is indexed to be at {2}",
|
|
|
|
|
entry.blockType, entry.dataType, entry.offset);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(entry);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Invalidate previous checksum block
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.ChecksumBlock && t.dataType == DataType.NoData);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
bool foundUserDataDdt = false;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
foreach(IndexEntry entry in _index)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Position = (long)entry.offset;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
switch(entry.blockType)
|
|
|
|
|
{
|
|
|
|
|
case BlockType.DataBlock:
|
2020-11-06 02:27:06 +00:00
|
|
|
// NOP block, skip
|
|
|
|
|
if(entry.dataType == DataType.NoData)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
_imageStream.Position = (long)entry.offset;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
BlockHeader blockHeader = Marshal.SpanToStructureLittleEndian<BlockHeader>(_structureBytes);
|
|
|
|
|
_imageInfo.ImageSize += blockHeader.cmpLength;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-11-06 02:27:06 +00:00
|
|
|
// Unused, skip
|
|
|
|
|
if(entry.dataType == DataType.UserData)
|
|
|
|
|
break;
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
if(blockHeader.identifier != entry.blockType)
|
|
|
|
|
{
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Incorrect identifier for data block at position {0}",
|
|
|
|
|
entry.offset);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(blockHeader.type != entry.dataType)
|
|
|
|
|
{
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Expected block with data type {0} at position {1} but found data type {2}",
|
|
|
|
|
entry.dataType, entry.offset, blockHeader.type);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] data;
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Found data block type {0} at position {1}", entry.dataType,
|
|
|
|
|
entry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-11-06 02:27:06 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
// Decompress media tag
|
|
|
|
|
if(blockHeader.compression == CompressionType.Lzma ||
|
|
|
|
|
blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-11-06 02:27:06 +00:00
|
|
|
if(blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform &&
|
|
|
|
|
entry.dataType != DataType.CdSectorSubchannel)
|
|
|
|
|
{
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
|
|
|
|
"Invalid compression type {0} for block with data type {1}, continuing...",
|
|
|
|
|
blockHeader.compression, entry.dataType);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DateTime startDecompress = DateTime.Now;
|
|
|
|
|
byte[] compressedTag = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH];
|
|
|
|
|
byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH);
|
|
|
|
|
_imageStream.Read(compressedTag, 0, compressedTag.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
var compressedTagMs = new MemoryStream(compressedTag);
|
|
|
|
|
var lzmaBlock = new LzmaStream(lzmaProperties, compressedTagMs);
|
2018-07-23 23:25:43 +01:00
|
|
|
data = new byte[blockHeader.length];
|
|
|
|
|
lzmaBlock.Read(data, 0, (int)blockHeader.length);
|
|
|
|
|
lzmaBlock.Close();
|
|
|
|
|
compressedTagMs.Close();
|
2020-11-06 02:27:06 +00:00
|
|
|
|
|
|
|
|
if(blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform)
|
|
|
|
|
data = ClauniaSubchannelUntransform(data);
|
|
|
|
|
|
|
|
|
|
DateTime endDecompress = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to decompress block",
|
|
|
|
|
(endDecompress - startDecompress).TotalSeconds);
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else if(blockHeader.compression == CompressionType.None)
|
|
|
|
|
{
|
|
|
|
|
data = new byte[blockHeader.length];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(data, 0, (int)blockHeader.length);
|
2020-11-06 02:27:06 +00:00
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Found unknown compression type {0}, continuing...",
|
|
|
|
|
(ushort)blockHeader.compression);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-06 02:27:06 +00:00
|
|
|
// Check CRC, if not correct, skip it
|
2018-07-23 23:25:43 +01:00
|
|
|
Crc64Context.Data(data, out byte[] blockCrc);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
if(BitConverter.ToUInt64(blockCrc, 0) != blockHeader.crc64)
|
|
|
|
|
{
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...",
|
|
|
|
|
BitConverter.ToUInt64(blockCrc, 0), blockHeader.crc64);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-06 02:27:06 +00:00
|
|
|
// Check if it's not a media tag, but a sector tag, and fill the appropriate table then
|
2018-07-23 23:25:43 +01:00
|
|
|
switch(entry.dataType)
|
|
|
|
|
{
|
|
|
|
|
case DataType.CdSectorPrefix:
|
2020-11-06 02:27:06 +00:00
|
|
|
case DataType.CdSectorPrefixCorrected:
|
|
|
|
|
if(entry.dataType == DataType.CdSectorPrefixCorrected)
|
|
|
|
|
{
|
|
|
|
|
_sectorPrefixMs = new NonClosableStream();
|
|
|
|
|
_sectorPrefixMs.Write(data, 0, data.Length);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
_sectorPrefix = data;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-11-06 02:27:06 +00:00
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-11-06 02:27:06 +00:00
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader);
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
break;
|
2020-11-06 02:27:06 +00:00
|
|
|
case DataType.CdSectorSuffix:
|
2018-07-31 23:27:08 +01:00
|
|
|
case DataType.CdSectorSuffixCorrected:
|
2020-11-06 02:27:06 +00:00
|
|
|
if(entry.dataType == DataType.CdSectorSuffixCorrected)
|
|
|
|
|
{
|
|
|
|
|
_sectorSuffixMs = new NonClosableStream();
|
|
|
|
|
_sectorSuffixMs.Write(data, 0, data.Length);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
_sectorSuffix = data;
|
|
|
|
|
|
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader);
|
|
|
|
|
|
|
|
|
|
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));
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
break;
|
2018-07-23 23:25:43 +01:00
|
|
|
case DataType.CdSectorSubchannel:
|
2020-11-06 02:27:06 +00:00
|
|
|
_sectorSubchannel = data;
|
|
|
|
|
|
|
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel);
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
break;
|
2018-07-23 23:25:43 +01:00
|
|
|
case DataType.AppleProfileTag:
|
|
|
|
|
case DataType.AppleSonyTag:
|
|
|
|
|
case DataType.PriamDataTowerTag:
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSubchannel = data;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-11-06 02:27:06 +00:00
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag);
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case DataType.CompactDiscMode2Subheader:
|
|
|
|
|
_mode2Subheaders = data;
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
break;
|
2021-01-16 11:09:42 +01:00
|
|
|
case DataType.DvdSectorCpiMai:
|
2021-01-13 22:22:34 +01:00
|
|
|
_sectorCpiMai = data;
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-15 11:43:05 +01:00
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdCmi))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.DvdCmi);
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-15 11:43:05 +01:00
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdTitleKey))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.DvdTitleKey);
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
break;
|
2021-01-16 11:09:42 +01:00
|
|
|
case DataType.DvdSectorTitleKeyDecrypted:
|
2021-01-13 22:22:34 +01:00
|
|
|
_sectorDecryptedTitleKey = data;
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-15 11:43:05 +01:00
|
|
|
if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdTitleKeyDecrypted))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.DvdTitleKeyDecrypted);
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
break;
|
2020-11-06 02:27:06 +00:00
|
|
|
default:
|
|
|
|
|
MediaTagType mediaTagType = GetMediaTagTypeForDataType(blockHeader.type);
|
|
|
|
|
|
|
|
|
|
if(_mediaTags.ContainsKey(mediaTagType))
|
|
|
|
|
{
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
|
|
|
|
"Media tag type {0} duplicated, removing previous entry...",
|
|
|
|
|
mediaTagType);
|
|
|
|
|
|
|
|
|
|
_mediaTags.Remove(mediaTagType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_mediaTags.Add(mediaTagType, data);
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case BlockType.DeDuplicationTable:
|
|
|
|
|
// Only user data deduplication tables are used right now
|
2018-07-31 23:27:08 +01:00
|
|
|
if(entry.dataType == DataType.UserData)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DdtHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-03-01 07:35:22 +00:00
|
|
|
DdtHeader ddtHeader =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.ByteArrayToStructureLittleEndian<DdtHeader>(_structureBytes);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(ddtHeader.identifier != BlockType.DeDuplicationTable)
|
|
|
|
|
break;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(ddtHeader.entries != _imageInfo.Sectors &&
|
2020-01-12 01:02:06 +00:00
|
|
|
!IsTape)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage =
|
2020-07-20 21:11:32 +01:00
|
|
|
$"Trying to write a media with {_imageInfo.Sectors} sectors to an image with {ddtHeader.entries} sectors, not continuing...";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_shift = ddtHeader.shift;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
switch(ddtHeader.compression)
|
|
|
|
|
{
|
|
|
|
|
case CompressionType.Lzma:
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Decompressing DDT...");
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
DateTime ddtStart = DateTime.UtcNow;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH];
|
|
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH);
|
|
|
|
|
_imageStream.Read(compressedDdt, 0, compressedDdt.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
var compressedDdtMs = new MemoryStream(compressedDdt);
|
|
|
|
|
var lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs);
|
|
|
|
|
byte[] decompressedDdt = new byte[ddtHeader.length];
|
2018-07-31 23:27:08 +01:00
|
|
|
lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length);
|
|
|
|
|
lzmaDdt.Close();
|
|
|
|
|
compressedDdtMs.Close();
|
2020-07-20 21:11:32 +01:00
|
|
|
_userDataDdt = MemoryMarshal.Cast<byte, ulong>(decompressedDdt).ToArray();
|
2018-07-31 23:27:08 +01:00
|
|
|
DateTime ddtEnd = DateTime.UtcNow;
|
2020-07-20 21:11:32 +01:00
|
|
|
_inMemoryDdt = true;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Took {0} seconds to decompress DDT",
|
|
|
|
|
(ddtEnd - ddtStart).TotalSeconds);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
break;
|
|
|
|
|
case CompressionType.None:
|
2020-07-20 21:11:32 +01:00
|
|
|
_inMemoryDdt = false;
|
|
|
|
|
_outMemoryDdtPosition = (long)entry.offset;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}");
|
|
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
if(IsTape)
|
2019-05-01 23:26:49 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_tapeDdt = new Dictionary<ulong, ulong>();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
for(long i = 0; i < _userDataDdt.LongLength; i++)
|
|
|
|
|
_tapeDdt.Add((ulong)i, _userDataDdt[i]);
|
2019-05-01 23:26:49 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_userDataDdt = null;
|
2019-05-01 23:26:49 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
foundUserDataDdt = true;
|
|
|
|
|
}
|
|
|
|
|
else if(entry.dataType == DataType.CdSectorPrefixCorrected ||
|
|
|
|
|
entry.dataType == DataType.CdSectorSuffixCorrected)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DdtHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-03-01 07:35:22 +00:00
|
|
|
DdtHeader ddtHeader =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.ByteArrayToStructureLittleEndian<DdtHeader>(_structureBytes);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(ddtHeader.identifier != BlockType.DeDuplicationTable)
|
|
|
|
|
break;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(ddtHeader.entries != _imageInfo.Sectors)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage =
|
2020-07-20 21:11:32 +01:00
|
|
|
$"Trying to write a media with {_imageInfo.Sectors} sectors to an image with {ddtHeader.entries} sectors, not continuing...";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] decompressedDdt = new byte[ddtHeader.length];
|
|
|
|
|
|
|
|
|
|
switch(ddtHeader.compression)
|
|
|
|
|
{
|
|
|
|
|
case CompressionType.Lzma:
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Decompressing DDT...");
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
DateTime ddtStart = DateTime.UtcNow;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH];
|
|
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH);
|
|
|
|
|
_imageStream.Read(compressedDdt, 0, compressedDdt.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
var compressedDdtMs = new MemoryStream(compressedDdt);
|
|
|
|
|
var lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs);
|
2018-07-31 23:27:08 +01:00
|
|
|
lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length);
|
|
|
|
|
lzmaDdt.Close();
|
|
|
|
|
compressedDdtMs.Close();
|
|
|
|
|
DateTime ddtEnd = DateTime.UtcNow;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Took {0} seconds to decompress DDT",
|
|
|
|
|
(ddtEnd - ddtStart).TotalSeconds);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
break;
|
|
|
|
|
case CompressionType.None:
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(decompressedDdt, 0, decompressedDdt.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new
|
|
|
|
|
ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}");
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-15 22:07:10 +00:00
|
|
|
uint[] cdDdt = MemoryMarshal.Cast<byte, uint>(decompressedDdt).ToArray();
|
2018-07-31 23:27:08 +01:00
|
|
|
|
|
|
|
|
switch(entry.dataType)
|
|
|
|
|
{
|
|
|
|
|
case DataType.CdSectorPrefixCorrected:
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixDdt = cdDdt;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
break;
|
|
|
|
|
case DataType.CdSectorSuffixCorrected:
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt = cdDdt;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-11-05 01:21:04 +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.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_geometryBlock = Marshal.SpanToStructureLittleEndian<GeometryBlock>(_structureBytes);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
_imageInfo.Cylinders = _geometryBlock.cylinders;
|
|
|
|
|
_imageInfo.Heads = _geometryBlock.heads;
|
|
|
|
|
_imageInfo.SectorsPerTrack = _geometryBlock.sectorsPerTrack;
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Metadata block
|
|
|
|
|
case BlockType.MetadataBlock:
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<MetadataBlock>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
|
|
|
|
|
MetadataBlock metadataBlock =
|
|
|
|
|
Marshal.SpanToStructureLittleEndian<MetadataBlock>(_structureBytes);
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.identifier != entry.blockType)
|
|
|
|
|
{
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
|
|
|
|
"Incorrect identifier for data block at position {0}",
|
|
|
|
|
entry.offset);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found metadata block at position {0}",
|
|
|
|
|
entry.offset);
|
|
|
|
|
|
|
|
|
|
byte[] metadata = new byte[metadataBlock.blockSize];
|
|
|
|
|
_imageStream.Position = (long)entry.offset;
|
|
|
|
|
_imageStream.Read(metadata, 0, metadata.Length);
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.mediaSequence > 0 &&
|
|
|
|
|
metadataBlock.lastMediaSequence > 0)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.MediaSequence = metadataBlock.mediaSequence;
|
|
|
|
|
_imageInfo.LastMediaSequence = metadataBlock.lastMediaSequence;
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media sequence as {0} of {1}",
|
|
|
|
|
_imageInfo.MediaSequence, _imageInfo.LastMediaSequence);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.creatorLength > 0 &&
|
|
|
|
|
metadataBlock.creatorLength + metadataBlock.creatorOffset <= metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.Creator =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.creatorOffset,
|
|
|
|
|
(int)(metadataBlock.creatorLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting creator: {0}",
|
|
|
|
|
_imageInfo.Creator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.commentsOffset > 0 &&
|
|
|
|
|
metadataBlock.commentsLength + metadataBlock.commentsOffset <= metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.Comments =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.commentsOffset,
|
|
|
|
|
(int)(metadataBlock.commentsLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting comments: {0}",
|
|
|
|
|
_imageInfo.Comments);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.mediaTitleOffset > 0 &&
|
|
|
|
|
metadataBlock.mediaTitleLength + metadataBlock.mediaTitleOffset <= metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.MediaTitle =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaTitleOffset,
|
|
|
|
|
(int)(metadataBlock.mediaTitleLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media title: {0}",
|
|
|
|
|
_imageInfo.MediaTitle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.mediaManufacturerOffset > 0 &&
|
|
|
|
|
metadataBlock.mediaManufacturerLength + metadataBlock.mediaManufacturerOffset <=
|
|
|
|
|
metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.MediaManufacturer =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaManufacturerOffset,
|
|
|
|
|
(int)(metadataBlock.mediaManufacturerLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media manufacturer: {0}",
|
|
|
|
|
_imageInfo.MediaManufacturer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.mediaModelOffset > 0 &&
|
|
|
|
|
metadataBlock.mediaModelLength + metadataBlock.mediaModelOffset <= metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.MediaModel =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaModelOffset,
|
|
|
|
|
(int)(metadataBlock.mediaModelLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media model: {0}",
|
|
|
|
|
_imageInfo.MediaModel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.mediaSerialNumberOffset > 0 &&
|
|
|
|
|
metadataBlock.mediaSerialNumberLength + metadataBlock.mediaSerialNumberOffset <=
|
|
|
|
|
metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.MediaSerialNumber =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaSerialNumberOffset,
|
|
|
|
|
(int)(metadataBlock.mediaSerialNumberLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media serial number: {0}",
|
|
|
|
|
_imageInfo.MediaSerialNumber);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.mediaBarcodeOffset > 0 &&
|
|
|
|
|
metadataBlock.mediaBarcodeLength + metadataBlock.mediaBarcodeOffset <= metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.MediaBarcode =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaBarcodeOffset,
|
|
|
|
|
(int)(metadataBlock.mediaBarcodeLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media barcode: {0}",
|
|
|
|
|
_imageInfo.MediaBarcode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.mediaPartNumberOffset > 0 &&
|
|
|
|
|
metadataBlock.mediaPartNumberLength + metadataBlock.mediaPartNumberOffset <=
|
|
|
|
|
metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.MediaPartNumber =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaPartNumberOffset,
|
|
|
|
|
(int)(metadataBlock.mediaPartNumberLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media part number: {0}",
|
|
|
|
|
_imageInfo.MediaPartNumber);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.driveManufacturerOffset > 0 &&
|
|
|
|
|
metadataBlock.driveManufacturerLength + metadataBlock.driveManufacturerOffset <=
|
|
|
|
|
metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.DriveManufacturer =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveManufacturerOffset,
|
|
|
|
|
(int)(metadataBlock.driveManufacturerLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive manufacturer: {0}",
|
|
|
|
|
_imageInfo.DriveManufacturer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.driveModelOffset > 0 &&
|
|
|
|
|
metadataBlock.driveModelLength + metadataBlock.driveModelOffset <= metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.DriveModel =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveModelOffset,
|
|
|
|
|
(int)(metadataBlock.driveModelLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive model: {0}",
|
|
|
|
|
_imageInfo.DriveModel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.driveSerialNumberOffset > 0 &&
|
|
|
|
|
metadataBlock.driveSerialNumberLength + metadataBlock.driveSerialNumberOffset <=
|
|
|
|
|
metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.DriveSerialNumber =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveSerialNumberOffset,
|
|
|
|
|
(int)(metadataBlock.driveSerialNumberLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive serial number: {0}",
|
|
|
|
|
_imageInfo.DriveSerialNumber);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(metadataBlock.driveFirmwareRevisionOffset > 0 &&
|
|
|
|
|
metadataBlock.driveFirmwareRevisionLength + metadataBlock.driveFirmwareRevisionOffset <=
|
|
|
|
|
metadata.Length)
|
|
|
|
|
{
|
|
|
|
|
_imageInfo.DriveFirmwareRevision =
|
|
|
|
|
Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveFirmwareRevisionOffset,
|
|
|
|
|
(int)(metadataBlock.driveFirmwareRevisionLength - 2));
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive firmware revision: {0}",
|
|
|
|
|
_imageInfo.DriveFirmwareRevision);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// Optical disc tracks block
|
|
|
|
|
case BlockType.TracksBlock:
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TracksHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
|
|
|
|
|
TracksHeader tracksHeader =
|
|
|
|
|
Marshal.SpanToStructureLittleEndian<TracksHeader>(_structureBytes);
|
|
|
|
|
|
|
|
|
|
if(tracksHeader.identifier != BlockType.TracksBlock)
|
|
|
|
|
{
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
|
|
|
|
"Incorrect identifier for tracks block at position {0}",
|
|
|
|
|
entry.offset);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TrackEntry>() * tracksHeader.entries];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
Crc64Context.Data(_structureBytes, out byte[] trksCrc);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_imageStream.Position -= _structureBytes.Length;
|
|
|
|
|
|
|
|
|
|
Tracks = new List<Track>();
|
|
|
|
|
_trackFlags = new Dictionary<byte, byte>();
|
|
|
|
|
_trackIsrcs = new Dictionary<byte, string>();
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found {0} tracks at position {0}",
|
|
|
|
|
tracksHeader.entries, entry.offset);
|
|
|
|
|
|
|
|
|
|
for(ushort i = 0; i < tracksHeader.entries; i++)
|
|
|
|
|
{
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TrackEntry>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
|
|
|
|
|
TrackEntry trackEntry =
|
|
|
|
|
Marshal.ByteArrayToStructureLittleEndian<TrackEntry>(_structureBytes);
|
|
|
|
|
|
|
|
|
|
Tracks.Add(new Track
|
|
|
|
|
{
|
|
|
|
|
TrackSequence = trackEntry.sequence,
|
|
|
|
|
TrackType = trackEntry.type,
|
|
|
|
|
TrackStartSector = (ulong)trackEntry.start,
|
|
|
|
|
TrackEndSector = (ulong)trackEntry.end,
|
|
|
|
|
TrackPregap = (ulong)trackEntry.pregap,
|
|
|
|
|
TrackSession = trackEntry.session,
|
|
|
|
|
TrackFileType = "BINARY"
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(trackEntry.type == TrackType.Data)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
_trackFlags.Add(trackEntry.sequence, trackEntry.flags);
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrEmpty(trackEntry.isrc))
|
|
|
|
|
_trackIsrcs.Add(trackEntry.sequence, trackEntry.isrc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(_trackFlags.Count > 0 &&
|
|
|
|
|
!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackFlags))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackFlags);
|
|
|
|
|
|
|
|
|
|
if(_trackIsrcs.Count > 0 &&
|
|
|
|
|
!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackIsrc))
|
|
|
|
|
_imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackIsrc);
|
|
|
|
|
|
|
|
|
|
_imageInfo.HasPartitions = true;
|
|
|
|
|
_imageInfo.HasSessions = true;
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// CICM XML metadata block
|
|
|
|
|
case BlockType.CicmBlock:
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<CicmMetadataBlock>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-03-01 07:35:22 +00:00
|
|
|
CicmMetadataBlock cicmBlock =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.SpanToStructureLittleEndian<CicmMetadataBlock>(_structureBytes);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(cicmBlock.identifier != BlockType.CicmBlock)
|
|
|
|
|
break;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Found CICM XML metadata block at position {0}", entry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
byte[] cicmBytes = new byte[cicmBlock.length];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(cicmBytes, 0, cicmBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
var cicmMs = new MemoryStream(cicmBytes);
|
|
|
|
|
var cicmXs = new XmlSerializer(typeof(CICMMetadataType));
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
try
|
|
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
var sr = new StreamReader(cicmMs);
|
2018-07-23 23:25:43 +01:00
|
|
|
CicmMetadata = (CICMMetadataType)cicmXs.Deserialize(sr);
|
|
|
|
|
sr.Close();
|
|
|
|
|
}
|
|
|
|
|
catch(XmlException ex)
|
|
|
|
|
{
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Exception {0} processing CICM XML metadata block",
|
|
|
|
|
ex.Message);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
CicmMetadata = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Dump hardware block
|
|
|
|
|
case BlockType.DumpHardwareBlock:
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DumpHardwareHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-03-01 07:35:22 +00:00
|
|
|
DumpHardwareHeader dumpBlock =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.SpanToStructureLittleEndian<DumpHardwareHeader>(_structureBytes);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(dumpBlock.identifier != BlockType.DumpHardwareBlock)
|
|
|
|
|
break;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Found dump hardware block at position {0}", entry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[dumpBlock.length];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
Crc64Context.Data(_structureBytes, out byte[] dumpCrc);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
if(BitConverter.ToUInt64(dumpCrc, 0) != dumpBlock.crc64)
|
|
|
|
|
{
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...",
|
|
|
|
|
BitConverter.ToUInt64(dumpCrc, 0), dumpBlock.crc64);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Position -= _structureBytes.Length;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
DumpHardware = new List<DumpHardwareType>();
|
|
|
|
|
|
|
|
|
|
for(ushort i = 0; i < dumpBlock.entries; i++)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DumpHardwareEntry>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-03-01 07:35:22 +00:00
|
|
|
DumpHardwareEntry dumpEntry =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.SpanToStructureLittleEndian<DumpHardwareEntry>(_structureBytes);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var dump = new DumpHardwareType
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
Software = new SoftwareType(),
|
|
|
|
|
Extents = new ExtentType[dumpEntry.extents]
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] tmp;
|
|
|
|
|
|
|
|
|
|
if(dumpEntry.manufacturerLength > 0)
|
|
|
|
|
{
|
|
|
|
|
tmp = new byte[dumpEntry.manufacturerLength - 1];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
|
|
|
|
_imageStream.Position += 1;
|
|
|
|
|
dump.Manufacturer = Encoding.UTF8.GetString(tmp);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpEntry.modelLength > 0)
|
|
|
|
|
{
|
|
|
|
|
tmp = new byte[dumpEntry.modelLength - 1];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
|
|
|
|
_imageStream.Position += 1;
|
|
|
|
|
dump.Model = Encoding.UTF8.GetString(tmp);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpEntry.revisionLength > 0)
|
|
|
|
|
{
|
|
|
|
|
tmp = new byte[dumpEntry.revisionLength - 1];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
|
|
|
|
_imageStream.Position += 1;
|
|
|
|
|
dump.Revision = Encoding.UTF8.GetString(tmp);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpEntry.firmwareLength > 0)
|
|
|
|
|
{
|
|
|
|
|
tmp = new byte[dumpEntry.firmwareLength - 1];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
|
|
|
|
_imageStream.Position += 1;
|
|
|
|
|
dump.Firmware = Encoding.UTF8.GetString(tmp);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpEntry.serialLength > 0)
|
|
|
|
|
{
|
|
|
|
|
tmp = new byte[dumpEntry.serialLength - 1];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
|
|
|
|
_imageStream.Position += 1;
|
|
|
|
|
dump.Serial = Encoding.UTF8.GetString(tmp);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpEntry.softwareNameLength > 0)
|
|
|
|
|
{
|
|
|
|
|
tmp = new byte[dumpEntry.softwareNameLength - 1];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
|
|
|
|
_imageStream.Position += 1;
|
|
|
|
|
dump.Software.Name = Encoding.UTF8.GetString(tmp);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpEntry.softwareVersionLength > 0)
|
|
|
|
|
{
|
|
|
|
|
tmp = new byte[dumpEntry.softwareVersionLength - 1];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
|
|
|
|
_imageStream.Position += 1;
|
2018-07-23 23:25:43 +01:00
|
|
|
dump.Software.Version = Encoding.UTF8.GetString(tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpEntry.softwareOperatingSystemLength > 0)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmp = new byte[dumpEntry.softwareOperatingSystemLength - 1];
|
|
|
|
|
_imageStream.Position += 1;
|
|
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
dump.Software.OperatingSystem = Encoding.UTF8.GetString(tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp = new byte[16];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
for(uint j = 0; j < dumpEntry.extents; j++)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tmp, 0, tmp.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
dump.Extents[j] = new ExtentType
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
Start = BitConverter.ToUInt64(tmp, 0),
|
|
|
|
|
End = BitConverter.ToUInt64(tmp, 8)
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dump.Extents = dump.Extents.OrderBy(t => t.Start).ToArray();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(dump.Extents.Length > 0)
|
|
|
|
|
DumpHardware.Add(dump);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(DumpHardware.Count == 0)
|
|
|
|
|
DumpHardware = null;
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
// Tape partition block
|
|
|
|
|
case BlockType.TapePartitionBlock:
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TapePartitionHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
TapePartitionHeader partitionHeader =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.SpanToStructureLittleEndian<TapePartitionHeader>(_structureBytes);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(partitionHeader.identifier != BlockType.TapePartitionBlock)
|
|
|
|
|
break;
|
2019-05-02 00:25:24 +01:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Found tape partition block at position {0}", entry.offset);
|
2019-05-02 00:25:24 +01:00
|
|
|
|
|
|
|
|
byte[] tapePartitionBytes = new byte[partitionHeader.length];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tapePartitionBytes, 0, tapePartitionBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
Span<TapePartitionEntry> tapePartitions =
|
|
|
|
|
MemoryMarshal.Cast<byte, TapePartitionEntry>(tapePartitionBytes);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
TapePartitions = new List<TapePartition>();
|
|
|
|
|
|
|
|
|
|
foreach(TapePartitionEntry tapePartition in tapePartitions)
|
|
|
|
|
TapePartitions.Add(new TapePartition
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
FirstBlock = tapePartition.FirstBlock,
|
|
|
|
|
LastBlock = tapePartition.LastBlock,
|
2019-05-02 00:25:24 +01:00
|
|
|
Number = tapePartition.Number
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
IsTape = true;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
// Tape file block
|
|
|
|
|
case BlockType.TapeFileBlock:
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TapeFileHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
TapeFileHeader fileHeader =
|
2020-07-20 21:11:32 +01:00
|
|
|
Marshal.SpanToStructureLittleEndian<TapeFileHeader>(_structureBytes);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(fileHeader.identifier != BlockType.TapeFileBlock)
|
|
|
|
|
break;
|
2019-05-02 00:25:24 +01:00
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Found tape file block at position {0}",
|
|
|
|
|
entry.offset);
|
2019-05-02 00:25:24 +01:00
|
|
|
|
|
|
|
|
byte[] tapeFileBytes = new byte[fileHeader.length];
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Read(tapeFileBytes, 0, tapeFileBytes.Length);
|
2019-05-02 00:25:24 +01:00
|
|
|
Span<TapeFileEntry> tapeFiles = MemoryMarshal.Cast<byte, TapeFileEntry>(tapeFileBytes);
|
|
|
|
|
Files = new List<TapeFile>();
|
|
|
|
|
|
|
|
|
|
foreach(TapeFileEntry file in tapeFiles)
|
|
|
|
|
Files.Add(new TapeFile
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
FirstBlock = file.FirstBlock,
|
|
|
|
|
LastBlock = file.LastBlock,
|
|
|
|
|
Partition = file.Partition,
|
|
|
|
|
File = file.File
|
2019-05-02 00:25:24 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
IsTape = true;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
break;
|
2020-11-05 01:21:04 +00:00
|
|
|
|
|
|
|
|
// Optical disc tracks block
|
|
|
|
|
case BlockType.CompactDiscIndexesBlock:
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<CompactDiscIndexesHeader>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
|
|
|
|
|
CompactDiscIndexesHeader indexesHeader =
|
|
|
|
|
Marshal.SpanToStructureLittleEndian<CompactDiscIndexesHeader>(_structureBytes);
|
|
|
|
|
|
|
|
|
|
if(indexesHeader.identifier != BlockType.CompactDiscIndexesBlock)
|
|
|
|
|
{
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
|
|
|
|
"Incorrect identifier for compact disc indexes block at position {0}",
|
|
|
|
|
entry.offset);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<CompactDiscIndexEntry>() * indexesHeader.entries];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
Crc64Context.Data(_structureBytes, out byte[] idsxCrc);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_imageStream.Position -= _structureBytes.Length;
|
|
|
|
|
|
|
|
|
|
compactDiscIndexes = new List<CompactDiscIndexEntry>();
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
|
|
|
|
"Found {0} compact disc indexes at position {0}",
|
|
|
|
|
indexesHeader.entries, entry.offset);
|
|
|
|
|
|
|
|
|
|
for(ushort i = 0; i < indexesHeader.entries; i++)
|
|
|
|
|
{
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<CompactDiscIndexEntry>()];
|
|
|
|
|
_imageStream.Read(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
|
|
|
|
|
compactDiscIndexes.Add(Marshal.
|
|
|
|
|
ByteArrayToStructureLittleEndian<CompactDiscIndexEntry
|
|
|
|
|
>(_structureBytes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
break;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!foundUserDataDdt)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Could not find user data deduplication table.";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sectorSuffixMs == null ||
|
|
|
|
|
_sectorSuffixDdt == null ||
|
|
|
|
|
_sectorPrefixMs == null ||
|
|
|
|
|
_sectorPrefixDdt == null)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixMs = null;
|
|
|
|
|
_sectorSuffixDdt = null;
|
|
|
|
|
_sectorPrefixMs = null;
|
|
|
|
|
_sectorPrefixDdt = null;
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
2020-11-05 01:21:04 +00:00
|
|
|
|
|
|
|
|
if(!_inMemoryDdt)
|
|
|
|
|
_ddtEntryCache = new Dictionary<ulong, ulong>();
|
|
|
|
|
|
|
|
|
|
// Initialize tracks, sessions and partitions
|
|
|
|
|
if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc)
|
|
|
|
|
{
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
if(Tracks == null ||
|
|
|
|
|
Tracks.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
Tracks = new List<Track>
|
|
|
|
|
{
|
|
|
|
|
new Track
|
|
|
|
|
{
|
|
|
|
|
TrackBytesPerSector = (int)_imageInfo.SectorSize,
|
|
|
|
|
TrackEndSector = _imageInfo.Sectors - 1,
|
|
|
|
|
TrackFileType = "BINARY",
|
|
|
|
|
TrackRawBytesPerSector = (int)_imageInfo.SectorSize,
|
|
|
|
|
TrackSession = 1,
|
|
|
|
|
TrackSequence = 1,
|
|
|
|
|
TrackType = TrackType.Data
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_trackFlags = new Dictionary<byte, byte>
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
1, (byte)CdFlags.DataTrack
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_trackIsrcs = new Dictionary<byte, string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
Sessions = new List<Session>();
|
|
|
|
|
|
|
|
|
|
for(int i = 1; i <= Tracks.Max(t => t.TrackSession); i++)
|
|
|
|
|
Sessions.Add(new Session
|
|
|
|
|
{
|
|
|
|
|
SessionSequence = (ushort)i,
|
|
|
|
|
StartTrack = Tracks.Where(t => t.TrackSession == i).Min(t => t.TrackSequence),
|
|
|
|
|
EndTrack = Tracks.Where(t => t.TrackSession == i).Max(t => t.TrackSequence),
|
|
|
|
|
StartSector = Tracks.Where(t => t.TrackSession == i).Min(t => t.TrackStartSector),
|
|
|
|
|
EndSector = Tracks.Where(t => t.TrackSession == i).Max(t => t.TrackEndSector)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
foreach(Track track in Tracks.OrderBy(t => t.TrackStartSector))
|
|
|
|
|
{
|
|
|
|
|
if(track.TrackSequence == 1)
|
|
|
|
|
{
|
|
|
|
|
track.TrackPregap = 150;
|
|
|
|
|
track.Indexes[0] = -150;
|
|
|
|
|
track.Indexes[1] = (int)track.TrackStartSector;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(track.TrackPregap > 0)
|
|
|
|
|
{
|
|
|
|
|
track.Indexes[0] = (int)track.TrackStartSector;
|
|
|
|
|
track.Indexes[1] = (int)(track.TrackStartSector + track.TrackPregap);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
track.Indexes[1] = (int)track.TrackStartSector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ulong currentTrackOffset = 0;
|
|
|
|
|
Partitions = new List<Partition>();
|
|
|
|
|
|
|
|
|
|
foreach(Track track in Tracks.OrderBy(t => t.TrackStartSector))
|
|
|
|
|
{
|
|
|
|
|
Partitions.Add(new Partition
|
|
|
|
|
{
|
|
|
|
|
Sequence = track.TrackSequence,
|
|
|
|
|
Type = track.TrackType.ToString(),
|
|
|
|
|
Name = $"Track {track.TrackSequence}",
|
|
|
|
|
Offset = currentTrackOffset,
|
|
|
|
|
Start = (ulong)track.Indexes[1],
|
|
|
|
|
Size = ((track.TrackEndSector - (ulong)track.Indexes[1]) + 1) *
|
|
|
|
|
(ulong)track.TrackBytesPerSector,
|
|
|
|
|
Length = (track.TrackEndSector - (ulong)track.Indexes[1]) + 1,
|
|
|
|
|
Scheme = "Optical disc track"
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
currentTrackOffset += ((track.TrackEndSector - track.TrackStartSector) + 1) *
|
|
|
|
|
(ulong)track.TrackBytesPerSector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
Track[] tracks = Tracks.ToArray();
|
|
|
|
|
|
|
|
|
|
foreach(Track trk in tracks)
|
|
|
|
|
{
|
|
|
|
|
byte[] sector = ReadSector(trk.TrackStartSector);
|
|
|
|
|
trk.TrackBytesPerSector = sector.Length;
|
|
|
|
|
|
|
|
|
|
trk.TrackRawBytesPerSector =
|
|
|
|
|
(_sectorPrefix != null && _sectorSuffix != null) ||
|
|
|
|
|
(_sectorPrefixDdt != null && _sectorSuffixDdt != null) ? 2352 : sector.Length;
|
|
|
|
|
|
|
|
|
|
if(_sectorSubchannel == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
trk.TrackSubchannelFile = trk.TrackFile;
|
|
|
|
|
trk.TrackSubchannelFilter = trk.TrackFilter;
|
|
|
|
|
trk.TrackSubchannelType = TrackSubchannelType.Raw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
Tracks = tracks.ToList();
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes",
|
|
|
|
|
GC.GetTotalMemory(false));
|
|
|
|
|
|
|
|
|
|
if(compactDiscIndexes != null)
|
|
|
|
|
{
|
|
|
|
|
foreach(CompactDiscIndexEntry compactDiscIndex in compactDiscIndexes.OrderBy(i => i.Track).
|
|
|
|
|
ThenBy(i => i.Index))
|
|
|
|
|
{
|
|
|
|
|
Track track = Tracks.FirstOrDefault(t => t.TrackSequence == compactDiscIndex.Track);
|
|
|
|
|
|
|
|
|
|
if(track is null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
track.Indexes[compactDiscIndex.Index] = compactDiscIndex.Lba;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Tracks = null;
|
|
|
|
|
Sessions = null;
|
|
|
|
|
Partitions = null;
|
|
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Creating new
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Checking that DDT is smaller than requested size
|
2020-07-20 21:11:32 +01:00
|
|
|
_inMemoryDdt = sectors <= (maxDdtSize * 1024 * 1024) / sizeof(ulong);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
// If in memory, easy
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_inMemoryDdt)
|
2019-05-01 22:51:30 +01:00
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
if(IsTape)
|
2020-07-20 21:11:32 +01:00
|
|
|
_tapeDdt = new Dictionary<ulong, ulong>();
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-07-20 21:11:32 +01:00
|
|
|
_userDataDdt = new ulong[sectors];
|
2019-05-01 22:51:30 +01:00
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// If not, create the block, add to index, and enlarge the file to allow the DDT to exist on-disk
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_outMemoryDdtPosition = _imageStream.Position;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(new IndexEntry
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DeDuplicationTable,
|
|
|
|
|
dataType = DataType.UserData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_outMemoryDdtPosition
|
2018-07-23 23:25:43 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// CRC64 will be calculated later
|
2020-01-12 01:02:06 +00:00
|
|
|
var ddtHeader = new DdtHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DeDuplicationTable,
|
|
|
|
|
type = DataType.UserData,
|
|
|
|
|
compression = CompressionType.None,
|
2020-07-20 21:11:32 +01:00
|
|
|
shift = _shift,
|
2020-07-20 04:34:16 +01:00
|
|
|
entries = sectors,
|
|
|
|
|
cmpLength = sectors * sizeof(ulong),
|
|
|
|
|
length = sectors * sizeof(ulong)
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DdtHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref ddtHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_structureBytes = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Position += (long)(sectors * sizeof(ulong)) - 1;
|
|
|
|
|
_imageStream.WriteByte(0);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(doMd5)
|
2020-07-20 21:11:32 +01:00
|
|
|
_md5Provider = new Md5Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(doSha1)
|
2020-07-20 21:11:32 +01:00
|
|
|
_sha1Provider = new Sha1Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(doSha256)
|
2020-07-20 21:11:32 +01:00
|
|
|
_sha256Provider = new Sha256Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(doSpamsum)
|
2020-07-20 21:11:32 +01:00
|
|
|
_spamsumProvider = new SpamSumContext();
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "In memory DDT?: {0}", _inMemoryDdt);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Seek(0, SeekOrigin.End);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
// Initialize compressors properties (all maxed)
|
2020-07-20 21:11:32 +01:00
|
|
|
_lzmaEncoderProperties = new LzmaEncoderProperties(true, (int)dictionary, 273);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_flakeWriterSettings = new EncoderSettings
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
PCM = AudioPCMConfig.RedBook,
|
|
|
|
|
DoMD5 = false,
|
2020-07-20 21:11:32 +01:00
|
|
|
BlockSize = (1 << _shift) * SAMPLES_PER_SECTOR,
|
2020-07-20 04:34:16 +01:00
|
|
|
MinFixedOrder = 0,
|
|
|
|
|
MaxFixedOrder = 4,
|
|
|
|
|
MinLPCOrder = 1,
|
|
|
|
|
MaxLPCOrder = 32,
|
|
|
|
|
MaxPartitionOrder = 8,
|
|
|
|
|
StereoMethod = StereoMethod.Evaluate,
|
|
|
|
|
PredictionType = PredictionType.Search,
|
|
|
|
|
WindowMethod = WindowMethod.EvaluateN,
|
|
|
|
|
EstimationDepth = 5,
|
|
|
|
|
MinPrecisionSearch = 1,
|
|
|
|
|
MaxPrecisionSearch = 1,
|
|
|
|
|
TukeyParts = 0,
|
|
|
|
|
TukeyOverlap = 1.0,
|
|
|
|
|
TukeyP = 1.0,
|
|
|
|
|
AllowNonSubset = true
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Check if FLAKE's block size is bigger than what we want
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_flakeWriterSettings.BlockSize > MAX_FLAKE_BLOCK)
|
|
|
|
|
_flakeWriterSettings.BlockSize = MAX_FLAKE_BLOCK;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_flakeWriterSettings.BlockSize < MIN_FLAKE_BLOCK)
|
|
|
|
|
_flakeWriterSettings.BlockSize = MIN_FLAKE_BLOCK;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-04-22 17:44:05 +01:00
|
|
|
AudioEncoder.Vendor = "Aaru";
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
IsWriting = true;
|
|
|
|
|
ErrorMessage = null;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteMediaTag(byte[] data, MediaTagType tag)
|
|
|
|
|
{
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write on a non-writable image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_mediaTags.ContainsKey(tag))
|
|
|
|
|
_mediaTags.Remove(tag);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_mediaTags.Add(tag, data);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteSector(byte[] data, ulong sectorAddress)
|
|
|
|
|
{
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write on a non-writable image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(sectorAddress >= Info.Sectors &&
|
|
|
|
|
!IsTape)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write past image size";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if((_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc || !_writingLong) &&
|
|
|
|
|
!_rewinded)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if(sectorAddress <= _lastWrittenBlock && _alreadyWrittenZero)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_rewinded = true;
|
|
|
|
|
_md5Provider = null;
|
|
|
|
|
_sha1Provider = null;
|
|
|
|
|
_sha256Provider = null;
|
|
|
|
|
_spamsumProvider = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_md5Provider?.Update(data);
|
|
|
|
|
_sha1Provider?.Update(data);
|
|
|
|
|
_sha256Provider?.Update(data);
|
|
|
|
|
_spamsumProvider?.Update(data);
|
|
|
|
|
_lastWrittenBlock = sectorAddress;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(sectorAddress == 0)
|
2020-07-20 21:11:32 +01:00
|
|
|
_alreadyWrittenZero = true;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
byte[] hash = null;
|
2020-07-20 21:11:32 +01:00
|
|
|
_writtenSectors++;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
// Compute hash only if asked to deduplicate, or the sector is empty (those will always be deduplicated)
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_deduplicate || ArrayHelpers.ArrayIsNullOrEmpty(data))
|
|
|
|
|
hash = _checksumProvider.ComputeHash(data);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
string hashString = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(hash != null)
|
|
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
var hashSb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
foreach(byte h in hash)
|
|
|
|
|
hashSb.Append(h.ToString("x2"));
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
hashString = hashSb.ToString();
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_deduplicationTable.TryGetValue(hashString, out ulong pointer))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
SetDdtEntry(sectorAddress, pointer);
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var trk = new Track();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
// If optical disc check track
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
trk = Tracks.FirstOrDefault(t => sectorAddress >= t.TrackStartSector &&
|
|
|
|
|
sectorAddress <= t.TrackEndSector);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
if(trk is null)
|
|
|
|
|
trk.TrackType = TrackType.Data; // TODO: Check intersession data type
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(trk.TrackSequence == 0 &&
|
|
|
|
|
trk.TrackStartSector == 0 &&
|
|
|
|
|
trk.TrackEndSector == 0)
|
2020-06-16 22:29:56 +01:00
|
|
|
trk.TrackType = TrackType.Data; // TODO: Check intersession data type
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close current block first
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_blockStream != null &&
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// When sector siz changes
|
2020-07-20 21:11:32 +01:00
|
|
|
(_currentBlockHeader.sectorSize != data.Length ||
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// When block if filled
|
2020-07-20 21:11:32 +01:00
|
|
|
_currentBlockOffset == 1 << _shift ||
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// When we change to/from CompactDisc audio
|
2020-07-20 21:11:32 +01:00
|
|
|
(_currentBlockHeader.compression == CompressionType.Flac && trk.TrackType != TrackType.Audio)))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_currentBlockHeader.length = _currentBlockOffset * _currentBlockHeader.sectorSize;
|
|
|
|
|
_currentBlockHeader.crc64 = BitConverter.ToUInt64(_crc64.Final(), 0);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc64Context = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
byte[] lzmaProperties = new byte[0];
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
switch(_currentBlockHeader.compression)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
case CompressionType.Flac:
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
long remaining = (_currentBlockOffset * SAMPLES_PER_SECTOR) % _flakeWriter.Settings.BlockSize;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
// Fill FLAC block
|
|
|
|
|
if(remaining != 0)
|
|
|
|
|
{
|
|
|
|
|
var audioBuffer =
|
|
|
|
|
new AudioBuffer(AudioPCMConfig.RedBook, new byte[remaining * 4], (int)remaining);
|
|
|
|
|
|
|
|
|
|
_flakeWriter.Write(audioBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_flakeWriter.Close();
|
|
|
|
|
|
|
|
|
|
break;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
2020-07-22 13:20:25 +01:00
|
|
|
case CompressionType.Lzma:
|
|
|
|
|
{
|
|
|
|
|
lzmaProperties = _lzmaBlockStream.Properties;
|
|
|
|
|
_lzmaBlockStream.Close();
|
|
|
|
|
_lzmaBlockStream = null;
|
|
|
|
|
cmpCrc64Context.Update(lzmaProperties);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
if(_blockStream.Length > _decompressedStream.Length)
|
|
|
|
|
_currentBlockHeader.compression = CompressionType.None;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.None)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = _decompressedStream;
|
|
|
|
|
_currentBlockHeader.cmpCrc64 = _currentBlockHeader.crc64;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc64Context.Update(_blockStream.ToArray());
|
|
|
|
|
_currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_currentBlockHeader.cmpLength = (uint)_blockStream.Length;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.Lzma)
|
|
|
|
|
_currentBlockHeader.cmpLength += LZMA_PROPERTIES_LENGTH;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(new IndexEntry
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.UserData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
});
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref _currentBlockHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_structureBytes = null;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.Lzma)
|
|
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.Close();
|
|
|
|
|
_blockStream = null;
|
2018-12-01 19:38:43 +00:00
|
|
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, false);
|
2020-07-20 21:11:32 +01:00
|
|
|
_currentBlockOffset = 0;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No block set
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_blockStream == null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_currentBlockHeader = new BlockHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.UserData,
|
2020-07-20 21:11:32 +01:00
|
|
|
compression = _compress ? CompressionType.Lzma : CompressionType.None,
|
2020-07-20 04:34:16 +01:00
|
|
|
sectorSize = (uint)data.Length
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc &&
|
|
|
|
|
trk.TrackType == TrackType.Audio &&
|
|
|
|
|
_compress)
|
|
|
|
|
_currentBlockHeader.compression = CompressionType.Flac;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
// JaguarCD stores data in audio tracks. FLAC is too inefficient, use LZMA there.
|
2019-02-11 20:05:00 +00:00
|
|
|
// VideoNow stores video in audio tracks, and LZMA works better too.
|
2020-07-20 21:11:32 +01:00
|
|
|
if(((_imageInfo.MediaType == MediaType.JaguarCD && trk.TrackSession > 1) ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.VideoNow || _imageInfo.MediaType == MediaType.VideoNowColor ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.VideoNowXp) &&
|
|
|
|
|
trk.TrackType == TrackType.Audio &&
|
|
|
|
|
_compress &&
|
|
|
|
|
_currentBlockHeader.compression == CompressionType.Flac)
|
|
|
|
|
_currentBlockHeader.compression = CompressionType.Lzma;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
|
|
|
|
_decompressedStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_currentBlockHeader.compression)
|
2019-05-02 00:32:19 +01:00
|
|
|
{
|
|
|
|
|
case CompressionType.Flac:
|
2020-07-20 21:11:32 +01:00
|
|
|
_flakeWriter = new AudioEncoder(_flakeWriterSettings, "", _blockStream)
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
DoSeekTable = false
|
|
|
|
|
};
|
|
|
|
|
|
2019-05-02 00:32:19 +01:00
|
|
|
break;
|
|
|
|
|
case CompressionType.Lzma:
|
2020-07-20 21:11:32 +01:00
|
|
|
_lzmaBlockStream = new LzmaStream(_lzmaEncoderProperties, false, _blockStream);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:32:19 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
2020-07-20 21:11:32 +01:00
|
|
|
_lzmaBlockStream = null;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-02 00:32:19 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_crc64 = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
ulong ddtEntry = (ulong)((_imageStream.Position << _shift) + _currentBlockOffset);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(hash != null)
|
2020-07-20 21:11:32 +01:00
|
|
|
_deduplicationTable.Add(hashString, ddtEntry);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.Flac)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
var audioBuffer = new AudioBuffer(AudioPCMConfig.RedBook, data, SAMPLES_PER_SECTOR);
|
2020-07-20 21:11:32 +01:00
|
|
|
_flakeWriter.Write(audioBuffer);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_decompressedStream.Write(data, 0, data.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.Lzma)
|
|
|
|
|
_lzmaBlockStream.Write(data, 0, data.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetDdtEntry(sectorAddress, ddtEntry);
|
2020-07-20 21:11:32 +01:00
|
|
|
_crc64.Update(data);
|
|
|
|
|
_currentBlockOffset++;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteSectors(byte[] data, ulong sectorAddress, uint length)
|
|
|
|
|
{
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write on a non-writable image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(sectorAddress + length > Info.Sectors)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write past image size";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint sectorSize = (uint)(data.Length / length);
|
|
|
|
|
|
|
|
|
|
for(uint i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
byte[] tmp = new byte[sectorSize];
|
|
|
|
|
Array.Copy(data, i * sectorSize, tmp, 0, sectorSize);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(!WriteSector(tmp, sectorAddress + i))
|
|
|
|
|
return false;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteSectorLong(byte[] data, ulong sectorAddress)
|
|
|
|
|
{
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write on a non-writable image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] sector;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_imageInfo.XmlMediaType)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
case XmlMediaType.OpticalDisc:
|
|
|
|
|
Track track =
|
|
|
|
|
Tracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
|
|
|
|
|
sectorAddress <= trk.TrackEndSector);
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
if(track is null)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Track not found";
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(track.TrackSequence == 0 &&
|
|
|
|
|
track.TrackStartSector == 0 &&
|
|
|
|
|
track.TrackEndSector == 0)
|
2020-06-16 22:29:56 +01:00
|
|
|
track.TrackType = TrackType.Data;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(data.Length != 2352)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_writingLong = true;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_rewinded)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if(sectorAddress <= _lastWrittenBlock && _alreadyWrittenZero)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_rewinded = true;
|
|
|
|
|
_md5Provider = null;
|
|
|
|
|
_sha1Provider = null;
|
|
|
|
|
_sha256Provider = null;
|
|
|
|
|
_spamsumProvider = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_md5Provider?.Update(data);
|
|
|
|
|
_sha1Provider?.Update(data);
|
|
|
|
|
_sha256Provider?.Update(data);
|
|
|
|
|
_spamsumProvider?.Update(data);
|
|
|
|
|
_lastWrittenBlock = sectorAddress;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
bool prefixCorrect;
|
|
|
|
|
int minute;
|
|
|
|
|
int second;
|
|
|
|
|
int frame;
|
|
|
|
|
int storedLba;
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Split raw cd sector data in prefix (sync, header), user data and suffix (edc, ecc p, ecc q)
|
|
|
|
|
switch(track.TrackType)
|
|
|
|
|
{
|
|
|
|
|
case TrackType.Audio:
|
|
|
|
|
case TrackType.Data: return WriteSector(data, sectorAddress);
|
|
|
|
|
case TrackType.CdMode1:
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sectorPrefix != null &&
|
|
|
|
|
_sectorSuffix != null)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
|
|
|
|
sector = new byte[2048];
|
2020-07-20 21:11:32 +01:00
|
|
|
Array.Copy(data, 0, _sectorPrefix, (int)sectorAddress * 16, 16);
|
2020-01-12 01:02:06 +00:00
|
|
|
Array.Copy(data, 16, sector, 0, 2048);
|
2020-07-20 21:11:32 +01:00
|
|
|
Array.Copy(data, 2064, _sectorSuffix, (int)sectorAddress * 288, 288);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
return WriteSector(sector, sectorAddress);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorSuffixMs ??= new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorPrefixMs ??= new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sectorSuffixDdt == null)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt = new uint[_imageInfo.Sectors];
|
2018-07-31 23:27:08 +01:00
|
|
|
EccInit();
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorPrefixDdt ??= new uint[_imageInfo.Sectors];
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
sector = new byte[2048];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
if(ArrayHelpers.ArrayIsNullOrEmpty(data))
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixDdt[sectorAddress] = (uint)CdFixFlags.NotDumped;
|
|
|
|
|
_sectorSuffixDdt[sectorAddress] = (uint)CdFixFlags.NotDumped;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
return WriteSector(sector, sectorAddress);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prefixCorrect = true;
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(data[0x00] != 0x00 ||
|
|
|
|
|
data[0x01] != 0xFF ||
|
|
|
|
|
data[0x02] != 0xFF ||
|
|
|
|
|
data[0x03] != 0xFF ||
|
|
|
|
|
data[0x04] != 0xFF ||
|
|
|
|
|
data[0x05] != 0xFF ||
|
|
|
|
|
data[0x06] != 0xFF ||
|
|
|
|
|
data[0x07] != 0xFF ||
|
|
|
|
|
data[0x08] != 0xFF ||
|
|
|
|
|
data[0x09] != 0xFF ||
|
|
|
|
|
data[0x0A] != 0xFF ||
|
|
|
|
|
data[0x0B] != 0x00 ||
|
|
|
|
|
data[0x0F] != 0x01)
|
|
|
|
|
prefixCorrect = false;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
|
|
|
|
if(prefixCorrect)
|
|
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
minute = ((data[0x0C] >> 4) * 10) + (data[0x0C] & 0x0F);
|
|
|
|
|
second = ((data[0x0D] >> 4) * 10) + (data[0x0D] & 0x0F);
|
|
|
|
|
frame = ((data[0x0E] >> 4) * 10) + (data[0x0E] & 0x0F);
|
|
|
|
|
storedLba = ((minute * 60 * 75) + (second * 75) + frame) - 150;
|
2018-07-31 23:27:08 +01:00
|
|
|
prefixCorrect = storedLba == (int)sectorAddress;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(prefixCorrect)
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixDdt[sectorAddress] = (uint)CdFixFlags.Correct;
|
2018-07-31 23:27:08 +01:00
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) > 0)
|
|
|
|
|
_sectorPrefixMs.Position =
|
|
|
|
|
((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16;
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixMs.Seek(0, SeekOrigin.End);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixDdt[sectorAddress] = (uint)((_sectorPrefixMs.Position / 16) + 1);
|
|
|
|
|
_sectorPrefixMs.Write(data, 0, 16);
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool correct = SuffixIsCorrect(data);
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(correct)
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt[sectorAddress] = (uint)CdFixFlags.Correct;
|
2018-07-31 23:27:08 +01:00
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) > 0)
|
|
|
|
|
_sectorSuffixMs.Position =
|
|
|
|
|
((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288;
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixMs.Seek(0, SeekOrigin.End);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt[sectorAddress] = (uint)((_sectorSuffixMs.Position / 288) + 1);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixMs.Write(data, 2064, 288);
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Array.Copy(data, 16, sector, 0, 2048);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return WriteSector(sector, sectorAddress);
|
|
|
|
|
case TrackType.CdMode2Formless:
|
|
|
|
|
case TrackType.CdMode2Form1:
|
|
|
|
|
case TrackType.CdMode2Form2:
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sectorPrefix != null &&
|
|
|
|
|
_sectorSuffix != null)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
|
|
|
|
sector = new byte[2336];
|
2020-07-20 21:11:32 +01:00
|
|
|
Array.Copy(data, 0, _sectorPrefix, (int)sectorAddress * 16, 16);
|
2020-01-12 01:02:06 +00:00
|
|
|
Array.Copy(data, 16, sector, 0, 2336);
|
|
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
return WriteSector(sector, sectorAddress);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorSuffixMs ??= new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorPrefixMs ??= new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sectorSuffixDdt == null)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt = new uint[_imageInfo.Sectors];
|
2018-07-31 23:27:08 +01:00
|
|
|
EccInit();
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorPrefixDdt ??= new uint[_imageInfo.Sectors];
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2018-08-02 22:31:02 +01:00
|
|
|
sector = new byte[2328];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-08-02 20:38:58 +01:00
|
|
|
if(ArrayHelpers.ArrayIsNullOrEmpty(data))
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixDdt[sectorAddress] = (uint)CdFixFlags.NotDumped;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
return WriteSector(sector, sectorAddress);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prefixCorrect = true;
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(data[0x00] != 0x00 ||
|
|
|
|
|
data[0x01] != 0xFF ||
|
|
|
|
|
data[0x02] != 0xFF ||
|
|
|
|
|
data[0x03] != 0xFF ||
|
|
|
|
|
data[0x04] != 0xFF ||
|
|
|
|
|
data[0x05] != 0xFF ||
|
|
|
|
|
data[0x06] != 0xFF ||
|
|
|
|
|
data[0x07] != 0xFF ||
|
|
|
|
|
data[0x08] != 0xFF ||
|
|
|
|
|
data[0x09] != 0xFF ||
|
|
|
|
|
data[0x0A] != 0xFF ||
|
|
|
|
|
data[0x0B] != 0x00 ||
|
|
|
|
|
data[0x0F] != 0x02)
|
|
|
|
|
prefixCorrect = false;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
|
|
|
|
if(prefixCorrect)
|
|
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
minute = ((data[0x0C] >> 4) * 10) + (data[0x0C] & 0x0F);
|
|
|
|
|
second = ((data[0x0D] >> 4) * 10) + (data[0x0D] & 0x0F);
|
|
|
|
|
frame = ((data[0x0E] >> 4) * 10) + (data[0x0E] & 0x0F);
|
|
|
|
|
storedLba = ((minute * 60 * 75) + (second * 75) + frame) - 150;
|
2018-07-31 23:27:08 +01:00
|
|
|
prefixCorrect = storedLba == (int)sectorAddress;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(prefixCorrect)
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixDdt[sectorAddress] = (uint)CdFixFlags.Correct;
|
2018-07-31 23:27:08 +01:00
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) > 0)
|
|
|
|
|
_sectorPrefixMs.Position =
|
|
|
|
|
((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16;
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixMs.Seek(0, SeekOrigin.End);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixDdt[sectorAddress] = (uint)((_sectorPrefixMs.Position / 16) + 1);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorPrefixMs.Write(data, 0, 16);
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_mode2Subheaders ??= new byte[_imageInfo.Sectors * 8];
|
2018-08-02 22:31:02 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
bool form2 = (data[18] & 0x20) == 0x20 || (data[22] & 0x20) == 0x20;
|
2018-08-02 23:25:20 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(form2)
|
2018-08-02 23:25:20 +01:00
|
|
|
{
|
|
|
|
|
uint computedEdc = ComputeEdc(0, data, 0x91C, 0x10);
|
|
|
|
|
uint edc = BitConverter.ToUInt32(data, 0x92C);
|
2020-07-22 13:20:25 +01:00
|
|
|
bool correctEdc = computedEdc == edc;
|
2018-08-02 23:25:20 +01:00
|
|
|
|
|
|
|
|
sector = new byte[2324];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorSuffixDdt ??= new uint[_imageInfo.Sectors];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-08-02 23:25:20 +01:00
|
|
|
Array.Copy(data, 24, sector, 0, 2324);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(correctEdc)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt[sectorAddress] = (uint)CdFixFlags.Mode2Form2Ok;
|
2020-01-12 01:02:06 +00:00
|
|
|
}
|
|
|
|
|
else if(BitConverter.ToUInt32(data, 0x92C) == 0)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt[sectorAddress] = (uint)CdFixFlags.Mode2Form2NoCrc;
|
2020-01-12 01:02:06 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) > 0)
|
|
|
|
|
_sectorSuffixMs.Position =
|
|
|
|
|
((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288;
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixMs.Seek(0, SeekOrigin.End);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt[sectorAddress] = (uint)((_sectorSuffixMs.Position / 288) + 1);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixMs.Write(data, 2348, 4);
|
2020-01-12 01:02:06 +00:00
|
|
|
}
|
2018-08-02 23:25:20 +01:00
|
|
|
}
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2018-08-02 23:25:20 +01:00
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
bool correctEcc = SuffixIsCorrectMode2(data);
|
|
|
|
|
|
|
|
|
|
uint computedEdc = ComputeEdc(0, data, 0x808, 0x10);
|
|
|
|
|
uint edc = BitConverter.ToUInt32(data, 0x818);
|
2020-07-22 13:20:25 +01:00
|
|
|
bool correctEdc = computedEdc == edc;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
sector = new byte[2048];
|
|
|
|
|
Array.Copy(data, 24, sector, 0, 2048);
|
|
|
|
|
|
|
|
|
|
if(correctEcc && correctEdc)
|
|
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorSuffixDdt ??= new uint[_imageInfo.Sectors];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt[sectorAddress] = (uint)CdFixFlags.Mode2Form1Ok;
|
2020-01-12 01:02:06 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) > 0)
|
|
|
|
|
_sectorSuffixMs.Position =
|
|
|
|
|
((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288;
|
2020-01-12 01:02:06 +00:00
|
|
|
else
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixMs.Seek(0, SeekOrigin.End);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixDdt[sectorAddress] = (uint)((_sectorSuffixMs.Position / 288) + 1);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_sectorSuffixMs.Write(data, 2072, 280);
|
2020-01-12 01:02:06 +00:00
|
|
|
}
|
2018-08-02 23:25:20 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Array.Copy(data, 16, _mode2Subheaders, (int)sectorAddress * 8, 8);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return WriteSector(sector, sectorAddress);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case XmlMediaType.BlockMedia:
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_imageInfo.MediaType)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
// Split user data from Apple tags
|
|
|
|
|
case MediaType.AppleFileWare:
|
|
|
|
|
case MediaType.AppleProfile:
|
|
|
|
|
case MediaType.AppleSonyDS:
|
|
|
|
|
case MediaType.AppleSonySS:
|
|
|
|
|
case MediaType.AppleWidget:
|
|
|
|
|
case MediaType.PriamDataTower:
|
|
|
|
|
byte[] oldTag;
|
|
|
|
|
byte[] newTag;
|
|
|
|
|
|
|
|
|
|
switch(data.Length - 512)
|
|
|
|
|
{
|
|
|
|
|
// Sony tag, convert to Profile
|
2020-07-20 21:11:32 +01:00
|
|
|
case 12 when _imageInfo.MediaType == MediaType.AppleProfile ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleFileWare:
|
2018-07-23 23:25:43 +01:00
|
|
|
oldTag = new byte[12];
|
|
|
|
|
Array.Copy(data, 512, oldTag, 0, 12);
|
|
|
|
|
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToProfile().GetBytes();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Sony tag, convert to Priam
|
2020-07-20 21:11:32 +01:00
|
|
|
case 12 when _imageInfo.MediaType == MediaType.PriamDataTower:
|
2018-07-23 23:25:43 +01:00
|
|
|
oldTag = new byte[12];
|
|
|
|
|
Array.Copy(data, 512, oldTag, 0, 12);
|
|
|
|
|
newTag = LisaTag.DecodeSonyTag(oldTag)?.ToPriam().GetBytes();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Sony tag, copy to Sony
|
2020-07-20 21:11:32 +01:00
|
|
|
case 12 when _imageInfo.MediaType == MediaType.AppleSonySS ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleSonySS:
|
2018-07-23 23:25:43 +01:00
|
|
|
newTag = new byte[12];
|
|
|
|
|
Array.Copy(data, 512, newTag, 0, 12);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Profile tag, copy to Profile
|
2020-07-20 21:11:32 +01:00
|
|
|
case 20 when _imageInfo.MediaType == MediaType.AppleProfile ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleFileWare:
|
2018-07-23 23:25:43 +01:00
|
|
|
newTag = new byte[20];
|
|
|
|
|
Array.Copy(data, 512, newTag, 0, 20);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Profile tag, convert to Priam
|
2020-07-20 21:11:32 +01:00
|
|
|
case 20 when _imageInfo.MediaType == MediaType.PriamDataTower:
|
2018-07-23 23:25:43 +01:00
|
|
|
oldTag = new byte[20];
|
|
|
|
|
Array.Copy(data, 512, oldTag, 0, 20);
|
|
|
|
|
newTag = LisaTag.DecodeProfileTag(oldTag)?.ToPriam().GetBytes();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Profile tag, convert to Sony
|
2020-07-20 21:11:32 +01:00
|
|
|
case 20 when _imageInfo.MediaType == MediaType.AppleSonySS ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleSonySS:
|
2018-07-23 23:25:43 +01:00
|
|
|
oldTag = new byte[20];
|
|
|
|
|
Array.Copy(data, 512, oldTag, 0, 20);
|
|
|
|
|
newTag = LisaTag.DecodeProfileTag(oldTag)?.ToSony().GetBytes();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Priam tag, convert to Profile
|
2020-07-20 21:11:32 +01:00
|
|
|
case 24 when _imageInfo.MediaType == MediaType.AppleProfile ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleFileWare:
|
2018-07-23 23:25:43 +01:00
|
|
|
oldTag = new byte[24];
|
|
|
|
|
Array.Copy(data, 512, oldTag, 0, 24);
|
|
|
|
|
newTag = LisaTag.DecodePriamTag(oldTag)?.ToProfile().GetBytes();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Priam tag, copy to Priam
|
2020-07-20 21:11:32 +01:00
|
|
|
case 12 when _imageInfo.MediaType == MediaType.PriamDataTower:
|
2018-07-23 23:25:43 +01:00
|
|
|
newTag = new byte[24];
|
|
|
|
|
Array.Copy(data, 512, newTag, 0, 24);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Priam tag, convert to Sony
|
2020-07-20 21:11:32 +01:00
|
|
|
case 24 when _imageInfo.MediaType == MediaType.AppleSonySS ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleSonySS:
|
2018-07-23 23:25:43 +01:00
|
|
|
oldTag = new byte[24];
|
|
|
|
|
Array.Copy(data, 512, oldTag, 0, 24);
|
|
|
|
|
newTag = LisaTag.DecodePriamTag(oldTag)?.ToSony().GetBytes();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
case 0:
|
|
|
|
|
newTag = null;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ErrorMessage = "Incorrect data size";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sector = new byte[512];
|
|
|
|
|
Array.Copy(data, 0, sector, 0, 512);
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if(newTag == null)
|
|
|
|
|
return WriteSector(sector, sectorAddress);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorSubchannel ??= new byte[newTag.Length * (int)_imageInfo.Sectors];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Array.Copy(newTag, 0, _sectorSubchannel, newTag.Length * (int)sectorAddress, newTag.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
return WriteSector(sector, sectorAddress);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErrorMessage = "Unknown long sector type, cannot write.";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length)
|
|
|
|
|
{
|
|
|
|
|
byte[] sector;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_imageInfo.XmlMediaType)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
case XmlMediaType.OpticalDisc:
|
|
|
|
|
if(data.Length % 2352 != 0)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sector = new byte[2352];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
for(uint i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
Array.Copy(data, 2352 * i, sector, 0, 2352);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(!WriteSectorLong(sector, sectorAddress + i))
|
|
|
|
|
return false;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
case XmlMediaType.BlockMedia:
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_imageInfo.MediaType)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
case MediaType.AppleFileWare:
|
|
|
|
|
case MediaType.AppleProfile:
|
|
|
|
|
case MediaType.AppleSonyDS:
|
|
|
|
|
case MediaType.AppleSonySS:
|
|
|
|
|
case MediaType.AppleWidget:
|
|
|
|
|
case MediaType.PriamDataTower:
|
|
|
|
|
int sectorSize = 0;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(data.Length % 524 == 0)
|
|
|
|
|
sectorSize = 524;
|
|
|
|
|
else if(data.Length % 532 == 0)
|
|
|
|
|
sectorSize = 532;
|
|
|
|
|
else if(data.Length % 536 == 0)
|
|
|
|
|
sectorSize = 536;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(sectorSize == 0)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sector = new byte[sectorSize];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
for(uint i = 0; i < length; i++)
|
|
|
|
|
{
|
|
|
|
|
Array.Copy(data, sectorSize * i, sector, 0, sectorSize);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(!WriteSectorLong(sector, sectorAddress + i))
|
|
|
|
|
return false;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErrorMessage = "Unknown long sector type, cannot write.";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SetTracks(List<Track> tracks)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = "Unsupported feature";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write on a non-writable image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tracks = tracks;
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Close()
|
|
|
|
|
{
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Image is not opened for writing";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close current block first
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_blockStream != null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_currentBlockHeader.length = _currentBlockOffset * _currentBlockHeader.sectorSize;
|
|
|
|
|
_currentBlockHeader.crc64 = BitConverter.ToUInt64(_crc64.Final(), 0);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc64Context = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
byte[] lzmaProperties = new byte[0];
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.Flac)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
long remaining = (_currentBlockOffset * SAMPLES_PER_SECTOR) % _flakeWriter.Settings.BlockSize;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// Fill FLAC block
|
|
|
|
|
if(remaining != 0)
|
|
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
var audioBuffer =
|
2018-07-23 23:25:43 +01:00
|
|
|
new AudioBuffer(AudioPCMConfig.RedBook, new byte[remaining * 4], (int)remaining);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_flakeWriter.Write(audioBuffer);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_flakeWriter.Close();
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
2020-07-20 21:11:32 +01:00
|
|
|
else if(_currentBlockHeader.compression == CompressionType.Lzma)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
lzmaProperties = _lzmaBlockStream.Properties;
|
|
|
|
|
_lzmaBlockStream.Close();
|
|
|
|
|
_lzmaBlockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
cmpCrc64Context.Update(lzmaProperties);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_blockStream.Length > _decompressedStream.Length)
|
|
|
|
|
_currentBlockHeader.compression = CompressionType.None;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.None)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = _decompressedStream;
|
|
|
|
|
_currentBlockHeader.cmpCrc64 = _currentBlockHeader.crc64;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc64Context.Update(_blockStream.ToArray());
|
|
|
|
|
_currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_currentBlockHeader.cmpLength = (uint)_blockStream.Length;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.Lzma)
|
|
|
|
|
_currentBlockHeader.cmpLength += LZMA_PROPERTIES_LENGTH;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(new IndexEntry
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.UserData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
});
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref _currentBlockHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_structureBytes = null;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_currentBlockHeader.compression == CompressionType.Lzma)
|
|
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_deduplicate)
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Of {0} sectors written, {1} are unique ({2:P})",
|
2020-07-20 21:11:32 +01:00
|
|
|
_writtenSectors, _deduplicationTable.Count,
|
|
|
|
|
(double)_deduplicationTable.Count / _writtenSectors);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
IndexEntry idxEntry;
|
|
|
|
|
|
|
|
|
|
// Write media tag blocks
|
2020-07-20 21:11:32 +01:00
|
|
|
foreach(KeyValuePair<MediaTagType, byte[]> mediaTag in _mediaTags)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
DataType dataType = GetDataTypeForMediaTag(mediaTag.Key);
|
2019-08-10 14:35:14 +01:00
|
|
|
|
|
|
|
|
if(mediaTag.Value is null)
|
|
|
|
|
{
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.ErrorWriteLine("Tag type {0} is null, skipping...", dataType);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-08-10 14:35:14 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = dataType,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing tag type {0} to position {1}", mediaTag.Key,
|
|
|
|
|
idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
Crc64Context.Data(mediaTag.Value, out byte[] tagCrc);
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var tagBlock = new BlockHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = dataType,
|
|
|
|
|
length = (uint)mediaTag.Value.Length,
|
2018-07-23 23:25:43 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(tagCrc, 0)
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
byte[] lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(mediaTag.Value, _lzmaEncoderProperties, _blockStream);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
byte[] tagData;
|
|
|
|
|
|
|
|
|
|
// Not compressible
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_blockStream.Length + LZMA_PROPERTIES_LENGTH >= mediaTag.Value.Length)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
tagBlock.cmpLength = tagBlock.length;
|
|
|
|
|
tagBlock.cmpCrc64 = tagBlock.crc64;
|
|
|
|
|
tagData = mediaTag.Value;
|
|
|
|
|
tagBlock.compression = CompressionType.None;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tagData = _blockStream.ToArray();
|
2020-01-12 01:02:06 +00:00
|
|
|
var crc64Ctx = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
crc64Ctx.Update(lzmaProperties);
|
|
|
|
|
crc64Ctx.Update(tagData);
|
|
|
|
|
tagCrc = crc64Ctx.Final();
|
|
|
|
|
tagBlock.cmpLength = (uint)tagData.Length + LZMA_PROPERTIES_LENGTH;
|
|
|
|
|
tagBlock.cmpCrc64 = BitConverter.ToUInt64(tagCrc, 0);
|
|
|
|
|
tagBlock.compression = CompressionType.Lzma;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref tagBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
if(tagBlock.compression == CompressionType.Lzma)
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(tagData, 0, tagData.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock && t.dataType == dataType);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have set the geometry block, write it
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_geometryBlock.identifier == BlockType.GeometryBlock)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.GeometryBlock,
|
|
|
|
|
dataType = DataType.NoData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing geometry block to position {0}",
|
2020-02-29 18:03:35 +00:00
|
|
|
idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<GeometryBlock>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref _geometryBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.GeometryBlock && t.dataType == DataType.NoData);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have dump hardware, write it
|
|
|
|
|
if(DumpHardware != null)
|
|
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
var dumpMs = new MemoryStream();
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
foreach(DumpHardwareType dump in DumpHardware)
|
|
|
|
|
{
|
|
|
|
|
byte[] dumpManufacturer = null;
|
|
|
|
|
byte[] dumpModel = null;
|
|
|
|
|
byte[] dumpRevision = null;
|
|
|
|
|
byte[] dumpFirmware = null;
|
|
|
|
|
byte[] dumpSerial = null;
|
|
|
|
|
byte[] dumpSoftwareName = null;
|
|
|
|
|
byte[] dumpSoftwareVersion = null;
|
|
|
|
|
byte[] dumpSoftwareOperatingSystem = null;
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(dump.Manufacturer))
|
|
|
|
|
dumpManufacturer = Encoding.UTF8.GetBytes(dump.Manufacturer);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(dump.Model))
|
|
|
|
|
dumpModel = Encoding.UTF8.GetBytes(dump.Model);
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(dump.Revision))
|
|
|
|
|
dumpRevision = Encoding.UTF8.GetBytes(dump.Revision);
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(dump.Firmware))
|
|
|
|
|
dumpFirmware = Encoding.UTF8.GetBytes(dump.Firmware);
|
|
|
|
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(dump.Serial))
|
|
|
|
|
dumpSerial = Encoding.UTF8.GetBytes(dump.Serial);
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(dump.Software?.Name))
|
|
|
|
|
dumpSoftwareName = Encoding.UTF8.GetBytes(dump.Software.Name);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(dump.Software?.Version))
|
|
|
|
|
dumpSoftwareVersion = Encoding.UTF8.GetBytes(dump.Software.Version);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(dump.Software?.OperatingSystem))
|
|
|
|
|
dumpSoftwareOperatingSystem = Encoding.UTF8.GetBytes(dump.Software.OperatingSystem);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var dumpEntry = new DumpHardwareEntry
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
manufacturerLength = (uint)(dumpManufacturer?.Length + 1 ?? 0),
|
|
|
|
|
modelLength = (uint)(dumpModel?.Length + 1 ?? 0),
|
|
|
|
|
revisionLength = (uint)(dumpRevision?.Length + 1 ?? 0),
|
|
|
|
|
firmwareLength = (uint)(dumpFirmware?.Length + 1 ?? 0),
|
|
|
|
|
serialLength = (uint)(dumpSerial?.Length + 1 ?? 0),
|
|
|
|
|
softwareNameLength = (uint)(dumpSoftwareName?.Length + 1 ?? 0),
|
|
|
|
|
softwareVersionLength = (uint)(dumpSoftwareVersion?.Length + 1 ?? 0),
|
|
|
|
|
softwareOperatingSystemLength = (uint)(dumpSoftwareOperatingSystem?.Length + 1 ?? 0),
|
|
|
|
|
extents = (uint)dump.Extents.Length
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DumpHardwareEntry>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref dumpEntry);
|
|
|
|
|
dumpMs.Write(_structureBytes, 0, _structureBytes.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
if(dumpManufacturer != null)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(dumpManufacturer, 0, dumpManufacturer.Length);
|
|
|
|
|
dumpMs.WriteByte(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpModel != null)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(dumpModel, 0, dumpModel.Length);
|
|
|
|
|
dumpMs.WriteByte(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpRevision != null)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(dumpRevision, 0, dumpRevision.Length);
|
|
|
|
|
dumpMs.WriteByte(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpFirmware != null)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(dumpFirmware, 0, dumpFirmware.Length);
|
|
|
|
|
dumpMs.WriteByte(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpSerial != null)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(dumpSerial, 0, dumpSerial.Length);
|
|
|
|
|
dumpMs.WriteByte(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpSoftwareName != null)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(dumpSoftwareName, 0, dumpSoftwareName.Length);
|
|
|
|
|
dumpMs.WriteByte(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpSoftwareVersion != null)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(dumpSoftwareVersion, 0, dumpSoftwareVersion.Length);
|
|
|
|
|
dumpMs.WriteByte(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dumpSoftwareOperatingSystem != null)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(dumpSoftwareOperatingSystem, 0, dumpSoftwareOperatingSystem.Length);
|
|
|
|
|
dumpMs.WriteByte(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach(ExtentType extent in dump.Extents)
|
|
|
|
|
{
|
|
|
|
|
dumpMs.Write(BitConverter.GetBytes(extent.Start), 0, sizeof(ulong));
|
2020-01-12 01:02:06 +00:00
|
|
|
dumpMs.Write(BitConverter.GetBytes(extent.End), 0, sizeof(ulong));
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DumpHardwareBlock,
|
|
|
|
|
dataType = DataType.NoData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing dump hardware block to position {0}",
|
2020-02-29 18:03:35 +00:00
|
|
|
idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
Crc64Context.Data(dumpMs.ToArray(), out byte[] dumpCrc);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
var dumpBlock = new DumpHardwareHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DumpHardwareBlock,
|
|
|
|
|
entries = (ushort)DumpHardware.Count,
|
|
|
|
|
crc64 = BitConverter.ToUInt64(dumpCrc, 0),
|
|
|
|
|
length = (uint)dumpMs.Length
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DumpHardwareHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref dumpBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_imageStream.Write(dumpMs.ToArray(), 0, (int)dumpMs.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DumpHardwareBlock && t.dataType == DataType.NoData);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have CICM XML metadata, write it
|
|
|
|
|
if(CicmMetadata != null)
|
|
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
var cicmMs = new MemoryStream();
|
|
|
|
|
var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
|
2018-07-23 23:25:43 +01:00
|
|
|
xmlSer.Serialize(cicmMs, CicmMetadata);
|
|
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.CicmBlock,
|
|
|
|
|
dataType = DataType.NoData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing CICM XML block to position {0}",
|
2020-02-29 18:03:35 +00:00
|
|
|
idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cicmBlock = new CicmMetadataBlock
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.CicmBlock,
|
|
|
|
|
length = (uint)cicmMs.Length
|
2020-01-12 01:02:06 +00:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<CicmMetadataBlock>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref cicmBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_imageStream.Write(cicmMs.ToArray(), 0, (int)cicmMs.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.CicmBlock && t.dataType == DataType.NoData);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have checksums, write it to disk
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_md5Provider != null ||
|
|
|
|
|
_sha1Provider != null ||
|
|
|
|
|
_sha256Provider != null ||
|
|
|
|
|
_spamsumProvider != null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
var chkMs = new MemoryStream();
|
|
|
|
|
|
|
|
|
|
var chkHeader = new ChecksumHeader
|
|
|
|
|
{
|
|
|
|
|
identifier = BlockType.ChecksumBlock
|
|
|
|
|
};
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_md5Provider != null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
byte[] md5 = _md5Provider.Final();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
var md5Entry = new ChecksumEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
type = ChecksumAlgorithm.Md5,
|
|
|
|
|
length = (uint)md5.Length
|
2020-01-12 01:02:06 +00:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<ChecksumEntry>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref md5Entry);
|
|
|
|
|
chkMs.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
chkMs.Write(md5, 0, md5.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
chkHeader.entries++;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sha1Provider != null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
byte[] sha1 = _sha1Provider.Final();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
var sha1Entry = new ChecksumEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
type = ChecksumAlgorithm.Sha1,
|
|
|
|
|
length = (uint)sha1.Length
|
2020-01-12 01:02:06 +00:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<ChecksumEntry>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref sha1Entry);
|
|
|
|
|
chkMs.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
chkMs.Write(sha1, 0, sha1.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
chkHeader.entries++;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sha256Provider != null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
byte[] sha256 = _sha256Provider.Final();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
var sha256Entry = new ChecksumEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
type = ChecksumAlgorithm.Sha256,
|
|
|
|
|
length = (uint)sha256.Length
|
2020-01-12 01:02:06 +00:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<ChecksumEntry>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref sha256Entry);
|
|
|
|
|
chkMs.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
chkMs.Write(sha256, 0, sha256.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
chkHeader.entries++;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_spamsumProvider != null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
byte[] spamsum = Encoding.ASCII.GetBytes(_spamsumProvider.End());
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
var spamsumEntry = new ChecksumEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
type = ChecksumAlgorithm.SpamSum,
|
|
|
|
|
length = (uint)spamsum.Length
|
2020-01-12 01:02:06 +00:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<ChecksumEntry>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref spamsumEntry);
|
|
|
|
|
chkMs.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
chkMs.Write(spamsum, 0, spamsum.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
chkHeader.entries++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(chkHeader.entries > 0)
|
|
|
|
|
{
|
|
|
|
|
chkHeader.length = (uint)chkMs.Length;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.ChecksumBlock,
|
|
|
|
|
dataType = DataType.NoData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing checksum block to position {0}",
|
2020-02-29 18:03:35 +00:00
|
|
|
idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<ChecksumHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref chkHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_imageStream.Write(chkMs.ToArray(), 0, (int)chkMs.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.ChecksumBlock && t.dataType == DataType.NoData);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 00:25:24 +01:00
|
|
|
if(IsTape)
|
2019-05-01 22:51:30 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
ulong latestBlock = _tapeDdt.Max(b => b.Key);
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_userDataDdt = new ulong[latestBlock + 1];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
foreach(KeyValuePair<ulong, ulong> block in _tapeDdt)
|
|
|
|
|
_userDataDdt[block.Key] = block.Value;
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_inMemoryDdt = true;
|
|
|
|
|
_tapeDdt.Clear();
|
2019-05-01 22:51:30 +01:00
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.TapePartitionBlock,
|
|
|
|
|
dataType = DataType.UserData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2019-05-01 22:51:30 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing tape partitions to position {0}",
|
2020-02-29 18:03:35 +00:00
|
|
|
idxEntry.offset);
|
2019-05-01 22:51:30 +01:00
|
|
|
|
|
|
|
|
TapePartitionEntry[] tapePartitionEntries = new TapePartitionEntry[TapePartitions.Count];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-01 22:51:30 +01:00
|
|
|
for(int t = 0; t < TapePartitions.Count; t++)
|
|
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
tapePartitionEntries[t] = new TapePartitionEntry
|
|
|
|
|
{
|
|
|
|
|
Number = TapePartitions[t].Number,
|
|
|
|
|
FirstBlock = TapePartitions[t].FirstBlock,
|
|
|
|
|
LastBlock = TapePartitions[t].LastBlock
|
|
|
|
|
};
|
2019-05-01 22:51:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] tapePartitionEntriesData =
|
|
|
|
|
MemoryMarshal.Cast<TapePartitionEntry, byte>(tapePartitionEntries).ToArray();
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
var tapePartitionHeader = new TapePartitionHeader
|
|
|
|
|
{
|
|
|
|
|
identifier = BlockType.TapePartitionBlock,
|
|
|
|
|
entries = (byte)tapePartitionEntries.Length,
|
|
|
|
|
length = (ulong)tapePartitionEntriesData.Length
|
|
|
|
|
};
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_crc64 = new Crc64Context();
|
|
|
|
|
_crc64.Update(tapePartitionEntriesData);
|
|
|
|
|
tapePartitionHeader.crc64 = BitConverter.ToUInt64(_crc64.Final(), 0);
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TapePartitionHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref tapePartitionHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_structureBytes = null;
|
|
|
|
|
_imageStream.Write(tapePartitionEntriesData, 0, tapePartitionEntriesData.Length);
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.TapePartitionBlock && t.dataType == DataType.UserData);
|
|
|
|
|
_index.Add(idxEntry);
|
2019-05-01 22:51:30 +01:00
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.TapeFileBlock,
|
|
|
|
|
dataType = DataType.UserData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2019-05-01 22:51:30 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing tape files to position {0}", idxEntry.offset);
|
2019-05-01 22:51:30 +01:00
|
|
|
|
|
|
|
|
TapeFileEntry[] tapeFileEntries = new TapeFileEntry[Files.Count];
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2019-05-01 22:51:30 +01:00
|
|
|
for(int t = 0; t < Files.Count; t++)
|
|
|
|
|
tapeFileEntries[t] = new TapeFileEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
File = Files[t].File,
|
|
|
|
|
FirstBlock = Files[t].FirstBlock,
|
|
|
|
|
LastBlock = Files[t].LastBlock
|
2019-05-01 22:51:30 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] tapeFileEntriesData = MemoryMarshal.Cast<TapeFileEntry, byte>(tapeFileEntries).ToArray();
|
|
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var tapeFileHeader = new TapeFileHeader
|
2019-05-01 22:51:30 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.TapeFileBlock,
|
|
|
|
|
entries = (uint)tapeFileEntries.Length,
|
2019-05-01 22:51:30 +01:00
|
|
|
length = (ulong)tapeFileEntriesData.Length
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_crc64 = new Crc64Context();
|
|
|
|
|
_crc64.Update(tapeFileEntriesData);
|
|
|
|
|
tapeFileHeader.crc64 = BitConverter.ToUInt64(_crc64.Final(), 0);
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TapeFileHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref tapeFileHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_structureBytes = null;
|
|
|
|
|
_imageStream.Write(tapeFileEntriesData, 0, tapeFileEntriesData.Length);
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.TapeFileBlock && t.dataType == DataType.UserData);
|
|
|
|
|
_index.Add(idxEntry);
|
2019-05-01 22:51:30 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
// If the DDT is in-memory, write it to disk
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_inMemoryDdt)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DeDuplicationTable,
|
|
|
|
|
dataType = DataType.UserData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing user data DDT to position {0}",
|
2020-02-29 18:03:35 +00:00
|
|
|
idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var ddtHeader = new DdtHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DeDuplicationTable,
|
|
|
|
|
type = DataType.UserData,
|
|
|
|
|
compression = CompressionType.Lzma,
|
2020-07-20 21:11:32 +01:00
|
|
|
shift = _shift,
|
|
|
|
|
entries = (ulong)_userDataDdt.LongLength,
|
|
|
|
|
length = (ulong)(_userDataDdt.LongLength * sizeof(ulong))
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
var userDataDdtStream = new MemoryStream();
|
2020-07-20 21:11:32 +01:00
|
|
|
_crc64 = new Crc64Context();
|
|
|
|
|
byte[] ddtEntries = MemoryMarshal.Cast<ulong, byte>(_userDataDdt).ToArray();
|
|
|
|
|
_crc64.Update(ddtEntries);
|
2019-03-15 22:07:10 +00:00
|
|
|
userDataDdtStream.Write(ddtEntries, 0, ddtEntries.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
byte[] lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(userDataDdtStream.ToArray(), _lzmaEncoderProperties, _blockStream);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
userDataDdtStream.Close();
|
2020-07-20 21:11:32 +01:00
|
|
|
ddtHeader.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc64Context = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
cmpCrc64Context.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc64Context.Update(_blockStream.ToArray());
|
2018-07-23 23:25:43 +01:00
|
|
|
ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0);
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DdtHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref ddtHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_structureBytes = null;
|
|
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
|
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable && t.dataType == DataType.UserData);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write the sector prefix, suffix and subchannels if present
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_imageInfo.XmlMediaType)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
case XmlMediaType.OpticalDisc when Tracks != null && Tracks.Count > 0:
|
|
|
|
|
DateTime startCompress;
|
|
|
|
|
DateTime endCompress;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
// Old format
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sectorPrefix != null &&
|
|
|
|
|
_sectorSuffix != null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.CdSectorPrefix,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Writing CD sector prefix block to position {0}", idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_sectorPrefix, out byte[] blockCrc);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var prefixBlock = new BlockHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.CdSectorPrefix,
|
2020-07-20 21:11:32 +01:00
|
|
|
length = (uint)_sectorPrefix.Length,
|
2020-07-20 04:34:16 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0),
|
2018-07-23 23:25:43 +01:00
|
|
|
sectorSize = 16
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] lzmaProperties = null;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_compress)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
prefixBlock.compression = CompressionType.None;
|
|
|
|
|
prefixBlock.cmpCrc64 = prefixBlock.crc64;
|
|
|
|
|
prefixBlock.cmpLength = prefixBlock.length;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream(_sectorPrefix);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-12-01 19:38:43 +00:00
|
|
|
startCompress = DateTime.Now;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(_sectorPrefix, _lzmaEncoderProperties, _blockStream);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
cmpCrc.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
2018-07-23 23:25:43 +01:00
|
|
|
blockCrc = cmpCrc.Final();
|
2020-07-20 21:11:32 +01:00
|
|
|
prefixBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2018-07-23 23:25:43 +01:00
|
|
|
prefixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
prefixBlock.compression = CompressionType.Lzma;
|
|
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
endCompress = DateTime.Now;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to compress prefix",
|
|
|
|
|
(endCompress - startCompress).TotalSeconds);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref prefixBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
if(prefixBlock.compression == CompressionType.Lzma)
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock &&
|
|
|
|
|
t.dataType == DataType.CdSectorPrefix);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.CdSectorSuffix,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Writing CD sector suffix block to position {0}", idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_sectorSuffix, out blockCrc);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
prefixBlock = new BlockHeader
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.CdSectorSuffix,
|
2020-07-20 21:11:32 +01:00
|
|
|
length = (uint)_sectorSuffix.Length,
|
2020-07-20 04:34:16 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0),
|
2018-07-23 23:25:43 +01:00
|
|
|
sectorSize = 288
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_compress)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
prefixBlock.compression = CompressionType.None;
|
|
|
|
|
prefixBlock.cmpCrc64 = prefixBlock.crc64;
|
|
|
|
|
prefixBlock.cmpLength = prefixBlock.length;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream(_sectorSuffix);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-12-01 19:38:43 +00:00
|
|
|
startCompress = DateTime.Now;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(_sectorSuffix, _lzmaEncoderProperties, _blockStream);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
cmpCrc.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
2018-07-23 23:25:43 +01:00
|
|
|
blockCrc = cmpCrc.Final();
|
2020-07-20 21:11:32 +01:00
|
|
|
prefixBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2018-07-23 23:25:43 +01:00
|
|
|
prefixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
prefixBlock.compression = CompressionType.Lzma;
|
|
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
endCompress = DateTime.Now;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to compress suffix",
|
|
|
|
|
(endCompress - startCompress).TotalSeconds);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref prefixBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
if(prefixBlock.compression == CompressionType.Lzma)
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock &&
|
|
|
|
|
t.dataType == DataType.CdSectorSuffix);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
2020-07-20 21:11:32 +01:00
|
|
|
else if(_sectorSuffixMs != null &&
|
|
|
|
|
_sectorSuffixDdt != null &&
|
|
|
|
|
_sectorPrefixMs != null &&
|
|
|
|
|
_sectorPrefixDdt != null)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
#if DEBUG
|
2018-07-31 23:27:08 +01:00
|
|
|
uint notDumpedPrefixes = 0;
|
|
|
|
|
uint correctPrefixes = 0;
|
|
|
|
|
uint writtenPrefixes = 0;
|
|
|
|
|
uint notDumpedSuffixes = 0;
|
|
|
|
|
uint correctSuffixes = 0;
|
|
|
|
|
uint writtenSuffixes = 0;
|
2018-08-02 23:30:52 +01:00
|
|
|
uint correctMode2Form1 = 0;
|
|
|
|
|
uint correctMode2Form2 = 0;
|
|
|
|
|
uint emptyMode2Form1 = 0;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
for(long i = 0; i < _sectorPrefixDdt.LongLength; i++)
|
|
|
|
|
if((_sectorPrefixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.NotDumped)
|
2018-07-31 23:27:08 +01:00
|
|
|
notDumpedPrefixes++;
|
2020-07-20 21:11:32 +01:00
|
|
|
else if((_sectorPrefixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct)
|
2020-01-12 01:02:06 +00:00
|
|
|
correctPrefixes++;
|
2020-07-20 21:11:32 +01:00
|
|
|
else if((_sectorPrefixDdt[i] & CD_DFIX_MASK) > 0)
|
2020-01-12 01:02:06 +00:00
|
|
|
writtenPrefixes++;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
for(long i = 0; i < _sectorPrefixDdt.LongLength; i++)
|
|
|
|
|
if((_sectorSuffixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.NotDumped)
|
2018-07-31 23:27:08 +01:00
|
|
|
notDumpedSuffixes++;
|
2020-07-20 21:11:32 +01:00
|
|
|
else if((_sectorSuffixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct)
|
2020-01-12 01:02:06 +00:00
|
|
|
correctSuffixes++;
|
2020-07-20 21:11:32 +01:00
|
|
|
else if((_sectorSuffixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.Mode2Form1Ok)
|
2018-08-02 23:30:52 +01:00
|
|
|
correctMode2Form1++;
|
2020-07-20 21:11:32 +01:00
|
|
|
else if((_sectorSuffixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.Mode2Form2Ok)
|
2018-08-02 23:30:52 +01:00
|
|
|
correctMode2Form2++;
|
2020-07-20 21:11:32 +01:00
|
|
|
else if((_sectorSuffixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.Mode2Form2NoCrc)
|
2018-08-02 23:30:52 +01:00
|
|
|
emptyMode2Form1++;
|
2020-07-20 21:11:32 +01:00
|
|
|
else if((_sectorSuffixDdt[i] & CD_DFIX_MASK) > 0)
|
2020-01-12 01:02:06 +00:00
|
|
|
writtenSuffixes++;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"{0} ({1:P}% prefixes are correct, {2} ({3:P}%) prefixes have not been dumped, {4} ({5:P}%) prefixes have been written to image",
|
2020-07-20 21:11:32 +01:00
|
|
|
correctPrefixes, correctPrefixes / _imageInfo.Sectors,
|
|
|
|
|
notDumpedPrefixes, notDumpedPrefixes / _imageInfo.Sectors,
|
|
|
|
|
writtenPrefixes, writtenPrefixes / _imageInfo.Sectors);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"{0} ({1:P}% suffixes are correct, {2} ({3:P}%) suffixes have not been dumped, {4} ({5:P}%) suffixes have been written to image",
|
2020-07-20 21:11:32 +01:00
|
|
|
correctSuffixes, correctSuffixes / _imageInfo.Sectors,
|
|
|
|
|
notDumpedSuffixes, notDumpedSuffixes / _imageInfo.Sectors,
|
|
|
|
|
writtenSuffixes, writtenSuffixes / _imageInfo.Sectors);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"{0} ({1:P}% MODE 2 Form 1 are correct, {2} ({3:P}%) MODE 2 Form 2 are correct, {4} ({5:P}%) MODE 2 Form 2 have empty CRC",
|
2020-07-20 21:11:32 +01:00
|
|
|
correctMode2Form1, correctMode2Form1 / _imageInfo.Sectors,
|
|
|
|
|
correctMode2Form2, correctMode2Form2 / _imageInfo.Sectors,
|
|
|
|
|
emptyMode2Form1, emptyMode2Form1 / _imageInfo.Sectors);
|
2020-01-12 01:02:06 +00:00
|
|
|
#endif
|
2018-08-02 23:30:52 +01:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DeDuplicationTable,
|
|
|
|
|
dataType = DataType.CdSectorPrefixCorrected,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-31 23:27:08 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Writing CompactDisc sector prefix DDT to position {0}",
|
|
|
|
|
idxEntry.offset);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var ddtHeader = new DdtHeader
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DeDuplicationTable,
|
|
|
|
|
type = DataType.CdSectorPrefixCorrected,
|
|
|
|
|
compression = CompressionType.Lzma,
|
2020-07-20 21:11:32 +01:00
|
|
|
entries = (ulong)_sectorPrefixDdt.LongLength,
|
|
|
|
|
length = (ulong)(_sectorPrefixDdt.LongLength * sizeof(uint))
|
2018-07-31 23:27:08 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
var sectorPrefixDdtStream = new MemoryStream();
|
2020-07-20 21:11:32 +01:00
|
|
|
_crc64 = new Crc64Context();
|
|
|
|
|
byte[] ddtEntries = MemoryMarshal.Cast<uint, byte>(_sectorPrefixDdt).ToArray();
|
|
|
|
|
_crc64.Update(ddtEntries);
|
2019-03-15 22:07:10 +00:00
|
|
|
sectorPrefixDdtStream.Write(ddtEntries, 0, ddtEntries.Length);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
byte[] lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(sectorPrefixDdtStream.ToArray(), _lzmaEncoderProperties,
|
|
|
|
|
_blockStream);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
sectorPrefixDdtStream.Close();
|
2020-07-20 21:11:32 +01:00
|
|
|
ddtHeader.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc64Context = new Crc64Context();
|
2018-07-31 23:27:08 +01:00
|
|
|
cmpCrc64Context.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc64Context.Update(_blockStream.ToArray());
|
2018-07-31 23:27:08 +01:00
|
|
|
ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0);
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DdtHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref ddtHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_structureBytes = null;
|
|
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
|
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable &&
|
|
|
|
|
t.dataType == DataType.CdSectorPrefixCorrected);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DeDuplicationTable,
|
|
|
|
|
dataType = DataType.CdSectorSuffixCorrected,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-31 23:27:08 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Writing CompactDisc sector suffix DDT to position {0}",
|
|
|
|
|
idxEntry.offset);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
|
|
|
|
ddtHeader = new DdtHeader
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DeDuplicationTable,
|
|
|
|
|
type = DataType.CdSectorSuffixCorrected,
|
|
|
|
|
compression = CompressionType.Lzma,
|
2020-07-20 21:11:32 +01:00
|
|
|
entries = (ulong)_sectorSuffixDdt.LongLength,
|
|
|
|
|
length = (ulong)(_sectorSuffixDdt.LongLength * sizeof(uint))
|
2018-07-31 23:27:08 +01:00
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
var sectorSuffixDdtStream = new MemoryStream();
|
2020-07-20 21:11:32 +01:00
|
|
|
_crc64 = new Crc64Context();
|
|
|
|
|
ddtEntries = MemoryMarshal.Cast<uint, byte>(_sectorSuffixDdt).ToArray();
|
|
|
|
|
_crc64.Update(ddtEntries);
|
2019-03-15 22:07:10 +00:00
|
|
|
sectorSuffixDdtStream.Write(ddtEntries, 0, ddtEntries.Length);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(sectorSuffixDdtStream.ToArray(), _lzmaEncoderProperties,
|
|
|
|
|
_blockStream);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
ddtHeader.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2018-07-31 23:27:08 +01:00
|
|
|
cmpCrc64Context = new Crc64Context();
|
|
|
|
|
cmpCrc64Context.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc64Context.Update(_blockStream.ToArray());
|
2018-07-31 23:27:08 +01:00
|
|
|
ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0);
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<DdtHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref ddtHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_structureBytes = null;
|
|
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
|
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable &&
|
|
|
|
|
t.dataType == DataType.CdSectorSuffixCorrected);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.CdSectorPrefixCorrected,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-31 23:27:08 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Writing CD sector corrected prefix block to position {0}",
|
|
|
|
|
idxEntry.offset);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_sectorPrefixMs.GetBuffer(), (uint)_sectorPrefixMs.Length,
|
|
|
|
|
out byte[] blockCrc);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var prefixBlock = new BlockHeader
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.CdSectorPrefixCorrected,
|
2020-07-20 21:11:32 +01:00
|
|
|
length = (uint)_sectorPrefixMs.Length,
|
2020-07-20 04:34:16 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0),
|
2018-07-31 23:27:08 +01:00
|
|
|
sectorSize = 16
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
lzmaProperties = null;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_compress)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
|
|
|
|
prefixBlock.compression = CompressionType.None;
|
|
|
|
|
prefixBlock.cmpCrc64 = prefixBlock.crc64;
|
|
|
|
|
prefixBlock.cmpLength = prefixBlock.length;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = _sectorPrefixMs;
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-12-01 19:38:43 +00:00
|
|
|
startCompress = DateTime.Now;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(_sectorPrefixMs.ToArray(), _lzmaEncoderProperties,
|
|
|
|
|
_blockStream);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc = new Crc64Context();
|
2018-07-31 23:27:08 +01:00
|
|
|
cmpCrc.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
2018-07-31 23:27:08 +01:00
|
|
|
blockCrc = cmpCrc.Final();
|
2020-07-20 21:11:32 +01:00
|
|
|
prefixBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2018-07-31 23:27:08 +01:00
|
|
|
prefixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
prefixBlock.compression = CompressionType.Lzma;
|
|
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
endCompress = DateTime.Now;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to compress prefix",
|
|
|
|
|
(endCompress - startCompress).TotalSeconds);
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref prefixBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
if(prefixBlock.compression == CompressionType.Lzma)
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock &&
|
|
|
|
|
t.dataType == DataType.CdSectorPrefixCorrected);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.CdSectorSuffixCorrected,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-31 23:27:08 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Writing CD sector corrected suffix block to position {0}",
|
|
|
|
|
idxEntry.offset);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_sectorSuffixMs.GetBuffer(), (uint)_sectorSuffixMs.Length, out blockCrc);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var suffixBlock = new BlockHeader
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.CdSectorSuffixCorrected,
|
2020-07-20 21:11:32 +01:00
|
|
|
length = (uint)_sectorSuffixMs.Length,
|
2020-07-20 04:34:16 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0),
|
2018-07-31 23:27:08 +01:00
|
|
|
sectorSize = 288
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
lzmaProperties = null;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_compress)
|
2018-07-31 23:27:08 +01:00
|
|
|
{
|
|
|
|
|
suffixBlock.compression = CompressionType.None;
|
|
|
|
|
suffixBlock.cmpCrc64 = suffixBlock.crc64;
|
|
|
|
|
suffixBlock.cmpLength = suffixBlock.length;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = _sectorSuffixMs;
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-12-01 19:38:43 +00:00
|
|
|
startCompress = DateTime.Now;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(_sectorSuffixMs.ToArray(), _lzmaEncoderProperties,
|
|
|
|
|
_blockStream);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc = new Crc64Context();
|
2018-07-31 23:27:08 +01:00
|
|
|
cmpCrc.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
2018-07-31 23:27:08 +01:00
|
|
|
blockCrc = cmpCrc.Final();
|
2020-07-20 21:11:32 +01:00
|
|
|
suffixBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2018-07-31 23:27:08 +01:00
|
|
|
suffixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
suffixBlock.compression = CompressionType.Lzma;
|
|
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
endCompress = DateTime.Now;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to compress suffix",
|
|
|
|
|
(endCompress - startCompress).TotalSeconds);
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref suffixBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-31 23:27:08 +01:00
|
|
|
if(suffixBlock.compression == CompressionType.Lzma)
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock &&
|
|
|
|
|
t.dataType == DataType.CdSectorSuffixCorrected);
|
2018-07-31 23:27:08 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
2018-07-31 23:27:08 +01:00
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_mode2Subheaders != null)
|
2018-08-02 22:31:02 +01:00
|
|
|
{
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.CompactDiscMode2Subheader,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-08-02 22:31:02 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Writing CD MODE2 subheaders block to position {0}",
|
|
|
|
|
idxEntry.offset);
|
2018-08-02 22:31:02 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_mode2Subheaders, out byte[] blockCrc);
|
2018-08-02 22:31:02 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var subheaderBlock = new BlockHeader
|
2018-08-02 22:31:02 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.CompactDiscMode2Subheader,
|
2020-07-20 21:11:32 +01:00
|
|
|
length = (uint)_mode2Subheaders.Length,
|
2020-07-20 04:34:16 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0),
|
2018-08-02 22:31:02 +01:00
|
|
|
sectorSize = 8
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] lzmaProperties = null;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_compress)
|
2018-08-02 22:31:02 +01:00
|
|
|
{
|
|
|
|
|
subheaderBlock.compression = CompressionType.None;
|
|
|
|
|
subheaderBlock.cmpCrc64 = subheaderBlock.crc64;
|
|
|
|
|
subheaderBlock.cmpLength = subheaderBlock.length;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream(_mode2Subheaders);
|
2018-08-02 22:31:02 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-12-01 19:38:43 +00:00
|
|
|
startCompress = DateTime.Now;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(_mode2Subheaders, _lzmaEncoderProperties, _blockStream);
|
2018-08-02 22:31:02 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc = new Crc64Context();
|
2018-08-02 22:31:02 +01:00
|
|
|
cmpCrc.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
2018-08-02 23:25:20 +01:00
|
|
|
blockCrc = cmpCrc.Final();
|
2020-07-20 21:11:32 +01:00
|
|
|
subheaderBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2018-08-02 22:31:02 +01:00
|
|
|
subheaderBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
subheaderBlock.compression = CompressionType.Lzma;
|
|
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
endCompress = DateTime.Now;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Took {0} seconds to compress MODE2 subheaders",
|
|
|
|
|
(endCompress - startCompress).TotalSeconds);
|
2018-08-02 22:31:02 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref subheaderBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-08-02 22:31:02 +01:00
|
|
|
if(subheaderBlock.compression == CompressionType.Lzma)
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
2018-08-02 22:31:02 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock &&
|
|
|
|
|
t.dataType == DataType.CompactDiscMode2Subheader);
|
2018-08-02 22:31:02 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-08-02 22:31:02 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sectorSubchannel != null)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.CdSectorSubchannel,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing CD subchannel block to position {0}",
|
|
|
|
|
idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_sectorSubchannel, out byte[] blockCrc);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var subchannelBlock = new BlockHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.CdSectorSubchannel,
|
2020-07-20 21:11:32 +01:00
|
|
|
length = (uint)_sectorSubchannel.Length,
|
2020-07-20 04:34:16 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0),
|
2018-07-23 23:25:43 +01:00
|
|
|
sectorSize = 96
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] lzmaProperties = null;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_compress)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
subchannelBlock.compression = CompressionType.None;
|
|
|
|
|
subchannelBlock.cmpCrc64 = subchannelBlock.crc64;
|
|
|
|
|
subchannelBlock.cmpLength = subchannelBlock.length;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream(_sectorSubchannel);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
startCompress = DateTime.Now;
|
2020-07-20 21:11:32 +01:00
|
|
|
byte[] transformedSubchannel = ClauniaSubchannelTransform(_sectorSubchannel);
|
|
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(transformedSubchannel, _lzmaEncoderProperties,
|
|
|
|
|
_blockStream);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
cmpCrc.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
2018-07-23 23:25:43 +01:00
|
|
|
blockCrc = cmpCrc.Final();
|
2020-07-20 21:11:32 +01:00
|
|
|
subchannelBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2018-07-23 23:25:43 +01:00
|
|
|
subchannelBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
subchannelBlock.compression = CompressionType.LzmaClauniaSubchannelTransform;
|
|
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
endCompress = DateTime.Now;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-02-29 18:03:35 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to compress subchannel",
|
|
|
|
|
(endCompress - startCompress).TotalSeconds);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref subchannelBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
if(subchannelBlock.compression == CompressionType.Lzma ||
|
|
|
|
|
subchannelBlock.compression == CompressionType.LzmaClauniaSubchannelTransform)
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock &&
|
|
|
|
|
t.dataType == DataType.CdSectorSubchannel);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2021-01-16 11:09:42 +01:00
|
|
|
if(_sectorCpiMai != null)
|
|
|
|
|
{
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
|
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.DvdSectorCpiMai,
|
|
|
|
|
offset = (ulong)_imageStream.Position
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing DVD CPI_MAI block to position {0}",
|
|
|
|
|
idxEntry.offset);
|
|
|
|
|
|
|
|
|
|
Crc64Context.Data(_sectorCpiMai, out byte[] blockCrc);
|
|
|
|
|
|
|
|
|
|
var cpiMaiBlock = new BlockHeader
|
|
|
|
|
{
|
|
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.DvdSectorCpiMai,
|
|
|
|
|
length = (uint)_sectorCpiMai.Length,
|
|
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0),
|
|
|
|
|
sectorSize = 6
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] lzmaProperties = null;
|
|
|
|
|
|
|
|
|
|
if(!_compress)
|
|
|
|
|
{
|
|
|
|
|
cpiMaiBlock.compression = CompressionType.None;
|
|
|
|
|
cpiMaiBlock.cmpCrc64 = cpiMaiBlock.crc64;
|
|
|
|
|
cpiMaiBlock.cmpLength = cpiMaiBlock.length;
|
|
|
|
|
_blockStream = new NonClosableStream(_sectorCpiMai);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
startCompress = DateTime.Now;
|
|
|
|
|
_blockStream = new NonClosableStream();
|
|
|
|
|
|
|
|
|
|
lzmaProperties =
|
|
|
|
|
CompressDataToStreamWithLZMA(_sectorCpiMai, _lzmaEncoderProperties, _blockStream);
|
|
|
|
|
|
|
|
|
|
var cmpCrc = new Crc64Context();
|
|
|
|
|
cmpCrc.Update(lzmaProperties);
|
|
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
|
|
|
|
blockCrc = cmpCrc.Final();
|
|
|
|
|
cpiMaiBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
|
|
|
|
cpiMaiBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
cpiMaiBlock.compression = CompressionType.Lzma;
|
|
|
|
|
|
|
|
|
|
endCompress = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to compress CPI_MAI",
|
|
|
|
|
(endCompress - startCompress).TotalSeconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref cpiMaiBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
|
|
|
|
|
if(cpiMaiBlock.compression == CompressionType.Lzma ||
|
|
|
|
|
cpiMaiBlock.compression == CompressionType.LzmaClauniaSubchannelTransform)
|
|
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
|
|
|
|
|
|
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
|
|
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock &&
|
|
|
|
|
t.dataType == DataType.DvdSectorCpiMai);
|
|
|
|
|
|
|
|
|
|
_index.Add(idxEntry);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(_sectorDecryptedTitleKey != null)
|
|
|
|
|
{
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
|
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = DataType.DvdSectorTitleKeyDecrypted,
|
|
|
|
|
offset = (ulong)_imageStream.Position
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
|
|
|
|
"Writing decrypted DVD title key block to position {0}",
|
|
|
|
|
idxEntry.offset);
|
|
|
|
|
|
|
|
|
|
Crc64Context.Data(_sectorDecryptedTitleKey, out byte[] blockCrc);
|
|
|
|
|
|
|
|
|
|
var titleKeyBlock = new BlockHeader
|
|
|
|
|
{
|
|
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = DataType.DvdSectorTitleKeyDecrypted,
|
|
|
|
|
length = (uint)_sectorDecryptedTitleKey.Length,
|
|
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0),
|
|
|
|
|
sectorSize = 5
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
byte[] lzmaProperties = null;
|
|
|
|
|
|
|
|
|
|
if(!_compress)
|
|
|
|
|
{
|
|
|
|
|
titleKeyBlock.compression = CompressionType.None;
|
|
|
|
|
titleKeyBlock.cmpCrc64 = titleKeyBlock.crc64;
|
|
|
|
|
titleKeyBlock.cmpLength = titleKeyBlock.length;
|
|
|
|
|
_blockStream = new NonClosableStream(_sectorDecryptedTitleKey);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
startCompress = DateTime.Now;
|
|
|
|
|
_blockStream = new NonClosableStream();
|
|
|
|
|
|
|
|
|
|
lzmaProperties =
|
|
|
|
|
CompressDataToStreamWithLZMA(_sectorDecryptedTitleKey, _lzmaEncoderProperties,
|
|
|
|
|
_blockStream);
|
|
|
|
|
|
|
|
|
|
var cmpCrc = new Crc64Context();
|
|
|
|
|
cmpCrc.Update(lzmaProperties);
|
|
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
|
|
|
|
blockCrc = cmpCrc.Final();
|
|
|
|
|
titleKeyBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
|
|
|
|
titleKeyBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
titleKeyBlock.compression = CompressionType.Lzma;
|
|
|
|
|
|
|
|
|
|
endCompress = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
|
|
|
|
"Took {0} seconds to compress decrypted DVD title keys",
|
|
|
|
|
(endCompress - startCompress).TotalSeconds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref titleKeyBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
|
|
|
|
|
if(titleKeyBlock.compression == CompressionType.Lzma ||
|
|
|
|
|
titleKeyBlock.compression == CompressionType.LzmaClauniaSubchannelTransform)
|
|
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
|
|
|
|
|
|
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
|
|
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock &&
|
|
|
|
|
t.dataType == DataType.DvdSectorTitleKeyDecrypted);
|
|
|
|
|
|
|
|
|
|
_index.Add(idxEntry);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-21 02:28:18 +01:00
|
|
|
List<TrackEntry> trackEntries = new List<TrackEntry>();
|
|
|
|
|
List<CompactDiscIndexEntry> compactDiscIndexEntries = new List<CompactDiscIndexEntry>();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
foreach(Track track in Tracks)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_trackFlags.TryGetValue((byte)track.TrackSequence, out byte flags);
|
|
|
|
|
_trackIsrcs.TryGetValue((byte)track.TrackSequence, out string isrc);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
if((flags & (int)CdFlags.DataTrack) == 0 &&
|
|
|
|
|
track.TrackType != TrackType.Audio)
|
2018-07-23 23:25:43 +01:00
|
|
|
flags += (byte)CdFlags.DataTrack;
|
|
|
|
|
|
|
|
|
|
trackEntries.Add(new TrackEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
sequence = (byte)track.TrackSequence,
|
|
|
|
|
type = track.TrackType,
|
|
|
|
|
start = (long)track.TrackStartSector,
|
|
|
|
|
end = (long)track.TrackEndSector,
|
|
|
|
|
pregap = (long)track.TrackPregap,
|
|
|
|
|
session = (byte)track.TrackSession,
|
|
|
|
|
isrc = isrc,
|
2018-07-23 23:25:43 +01:00
|
|
|
flags = flags
|
|
|
|
|
});
|
2020-06-21 02:28:18 +01:00
|
|
|
|
|
|
|
|
if(!track.Indexes.ContainsKey(0) &&
|
|
|
|
|
track.TrackPregap > 0)
|
|
|
|
|
{
|
|
|
|
|
track.Indexes[0] = (int)track.TrackStartSector;
|
|
|
|
|
track.Indexes[1] = (int)(track.TrackStartSector + track.TrackPregap);
|
|
|
|
|
}
|
|
|
|
|
else if(!track.Indexes.ContainsKey(0) &&
|
|
|
|
|
!track.Indexes.ContainsKey(1))
|
|
|
|
|
track.Indexes[0] = (int)track.TrackStartSector;
|
|
|
|
|
|
|
|
|
|
compactDiscIndexEntries.AddRange(track.Indexes.Select(trackIndex => new CompactDiscIndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
Index = trackIndex.Key,
|
|
|
|
|
Lba = trackIndex.Value,
|
|
|
|
|
Track = (ushort)track.TrackSequence
|
2020-06-21 02:28:18 +01:00
|
|
|
}));
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there are tracks build the tracks block
|
|
|
|
|
if(trackEntries.Count > 0)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
foreach(TrackEntry entry in trackEntries)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_structurePointer =
|
2019-03-01 07:35:22 +00:00
|
|
|
System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf<TrackEntry>());
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TrackEntry>()];
|
|
|
|
|
System.Runtime.InteropServices.Marshal.StructureToPtr(entry, _structurePointer, true);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
System.Runtime.InteropServices.Marshal.Copy(_structurePointer, _structureBytes, 0,
|
|
|
|
|
_structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
System.Runtime.InteropServices.Marshal.FreeHGlobal(_structurePointer);
|
|
|
|
|
_blockStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_blockStream.ToArray(), out byte[] trksCrc);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
|
|
|
|
var trkHeader = new TracksHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.TracksBlock,
|
|
|
|
|
entries = (ushort)trackEntries.Count,
|
2018-07-23 23:25:43 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(trksCrc, 0)
|
|
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing tracks to position {0}",
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Position);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.TracksBlock && t.dataType == DataType.NoData);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(new IndexEntry
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.TracksBlock,
|
|
|
|
|
dataType = DataType.NoData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
});
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<TracksHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref trkHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-06-21 02:28:18 +01:00
|
|
|
// If there are track indexes bigger than 1
|
|
|
|
|
if(compactDiscIndexEntries.Any(i => i.Index > 1))
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-06-21 02:28:18 +01:00
|
|
|
|
|
|
|
|
foreach(CompactDiscIndexEntry entry in compactDiscIndexEntries)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_structurePointer =
|
2020-06-21 02:28:18 +01:00
|
|
|
System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.
|
2020-11-05 01:21:04 +00:00
|
|
|
SizeOf<CompactDiscIndexEntry>());
|
2020-06-21 02:28:18 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<CompactDiscIndexEntry>()];
|
|
|
|
|
System.Runtime.InteropServices.Marshal.StructureToPtr(entry, _structurePointer, true);
|
2020-06-21 02:28:18 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
System.Runtime.InteropServices.Marshal.Copy(_structurePointer, _structureBytes, 0,
|
|
|
|
|
_structureBytes.Length);
|
2020-06-21 02:28:18 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
System.Runtime.InteropServices.Marshal.FreeHGlobal(_structurePointer);
|
|
|
|
|
_blockStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-06-21 02:28:18 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_blockStream.ToArray(), out byte[] cdixCrc);
|
2020-06-21 02:28:18 +01:00
|
|
|
|
|
|
|
|
var cdixHeader = new CompactDiscIndexesHeader
|
|
|
|
|
{
|
|
|
|
|
identifier = BlockType.CompactDiscIndexesBlock,
|
2020-07-20 04:34:16 +01:00
|
|
|
entries = (ushort)compactDiscIndexEntries.Count,
|
|
|
|
|
crc64 = BitConverter.ToUInt64(cdixCrc, 0)
|
2020-06-21 02:28:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing compact disc indexes to position {0}",
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Position);
|
2020-06-21 02:28:18 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.CompactDiscIndexesBlock &&
|
|
|
|
|
t.dataType == DataType.NoData);
|
2020-06-21 02:28:18 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(new IndexEntry
|
2020-06-21 02:28:18 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.CompactDiscIndexesBlock,
|
|
|
|
|
dataType = DataType.NoData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2020-06-21 02:28:18 +01:00
|
|
|
});
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<CompactDiscIndexesHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref cdixHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2020-06-21 02:28:18 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
case XmlMediaType.BlockMedia:
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_sectorSubchannel != null &&
|
|
|
|
|
(_imageInfo.MediaType == MediaType.AppleFileWare ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleSonySS ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleSonyDS ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleProfile ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.AppleWidget ||
|
|
|
|
|
_imageInfo.MediaType == MediaType.PriamDataTower))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
DataType tagType = DataType.NoData;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_imageInfo.MediaType)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
case MediaType.AppleSonySS:
|
|
|
|
|
case MediaType.AppleSonyDS:
|
|
|
|
|
tagType = DataType.AppleSonyTag;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
case MediaType.AppleFileWare:
|
|
|
|
|
case MediaType.AppleProfile:
|
|
|
|
|
case MediaType.AppleWidget:
|
|
|
|
|
tagType = DataType.AppleProfileTag;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
case MediaType.PriamDataTower:
|
|
|
|
|
tagType = DataType.PriamDataTowerTag;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idxEntry = new IndexEntry
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.DataBlock,
|
|
|
|
|
dataType = tagType,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin",
|
2020-02-29 18:03:35 +00:00
|
|
|
"Writing apple sector tag block to position {0}", idxEntry.offset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_sectorSubchannel, out byte[] blockCrc);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var subchannelBlock = new BlockHeader
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.DataBlock,
|
|
|
|
|
type = tagType,
|
2020-07-20 21:11:32 +01:00
|
|
|
length = (uint)_sectorSubchannel.Length,
|
2018-07-23 23:25:43 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(blockCrc, 0)
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
switch(_imageInfo.MediaType)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
case MediaType.AppleSonySS:
|
|
|
|
|
case MediaType.AppleSonyDS:
|
|
|
|
|
subchannelBlock.sectorSize = 12;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
case MediaType.AppleFileWare:
|
|
|
|
|
case MediaType.AppleProfile:
|
|
|
|
|
case MediaType.AppleWidget:
|
|
|
|
|
subchannelBlock.sectorSize = 20;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
case MediaType.PriamDataTower:
|
|
|
|
|
subchannelBlock.sectorSize = 24;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] lzmaProperties = null;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!_compress)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
subchannelBlock.compression = CompressionType.None;
|
|
|
|
|
subchannelBlock.cmpCrc64 = subchannelBlock.crc64;
|
|
|
|
|
subchannelBlock.cmpLength = subchannelBlock.length;
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream(_sectorSubchannel);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-12-01 19:38:43 +00:00
|
|
|
lzmaProperties =
|
2020-07-20 21:11:32 +01:00
|
|
|
CompressDataToStreamWithLZMA(_sectorSubchannel, _lzmaEncoderProperties, _blockStream);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
var cmpCrc = new Crc64Context();
|
2018-07-23 23:25:43 +01:00
|
|
|
cmpCrc.Update(lzmaProperties);
|
2020-07-20 21:11:32 +01:00
|
|
|
cmpCrc.Update(_blockStream.ToArray());
|
2018-07-23 23:25:43 +01:00
|
|
|
blockCrc = cmpCrc.Final();
|
2020-07-20 21:11:32 +01:00
|
|
|
subchannelBlock.cmpLength = (uint)_blockStream.Length + LZMA_PROPERTIES_LENGTH;
|
2018-07-23 23:25:43 +01:00
|
|
|
subchannelBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0);
|
|
|
|
|
subchannelBlock.compression = CompressionType.Lzma;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<BlockHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref subchannelBlock);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
if(subchannelBlock.compression == CompressionType.Lzma)
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(lzmaProperties, 0, lzmaProperties.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.DataBlock && t.dataType == tagType);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(idxEntry);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write metadata if present
|
|
|
|
|
SetMetadataFromTags();
|
2020-01-12 01:02:06 +00:00
|
|
|
var metadataBlock = new MetadataBlock();
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
|
|
|
|
_blockStream.Write(new byte[Marshal.SizeOf<MetadataBlock>()], 0, Marshal.SizeOf<MetadataBlock>());
|
2018-07-23 23:25:43 +01:00
|
|
|
byte[] tmpUtf16Le;
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_imageInfo.MediaSequence > 0 &&
|
|
|
|
|
_imageInfo.LastMediaSequence > 0)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.mediaSequence = _imageInfo.MediaSequence;
|
|
|
|
|
metadataBlock.lastMediaSequence = _imageInfo.LastMediaSequence;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.Creator))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.Creator);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.creatorOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.creatorLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.Comments))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.Comments);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.commentsOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.commentsLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.MediaTitle))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.MediaTitle);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.mediaTitleOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.mediaTitleLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.MediaManufacturer))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.MediaManufacturer);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.mediaManufacturerOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.mediaManufacturerLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.MediaModel))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.MediaModel);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.mediaModelOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.mediaModelLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.MediaSerialNumber))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.MediaSerialNumber);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.mediaSerialNumberOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.mediaSerialNumberLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.MediaBarcode))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.MediaBarcode);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.mediaBarcodeOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.mediaBarcodeLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.MediaPartNumber))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.MediaPartNumber);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.mediaPartNumberOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.mediaPartNumberLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.DriveManufacturer);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.driveManufacturerOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.driveManufacturerLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.DriveModel))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.DriveModel);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.driveModelOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.driveModelLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.DriveSerialNumber))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.DriveSerialNumber);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.driveSerialNumberOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.driveSerialNumberLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(!string.IsNullOrWhiteSpace(_imageInfo.DriveFirmwareRevision))
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
tmpUtf16Le = Encoding.Unicode.GetBytes(_imageInfo.DriveFirmwareRevision);
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.identifier = BlockType.MetadataBlock;
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.driveFirmwareRevisionOffset = (uint)_blockStream.Position;
|
2018-07-23 23:25:43 +01:00
|
|
|
metadataBlock.driveFirmwareRevisionLength = (uint)(tmpUtf16Le.Length + 2);
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream.Write(new byte[]
|
2020-01-12 01:02:06 +00:00
|
|
|
{
|
|
|
|
|
0, 0
|
|
|
|
|
}, 0, 2);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if we set up any metadata earlier, then write its block
|
|
|
|
|
if(metadataBlock.identifier == BlockType.MetadataBlock)
|
|
|
|
|
{
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing metadata to position {0}",
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Position);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
metadataBlock.blockSize = (uint)_blockStream.Length;
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<MetadataBlock>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref metadataBlock);
|
|
|
|
|
_blockStream.Position = 0;
|
|
|
|
|
_blockStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
_index.RemoveAll(t => t.blockType == BlockType.MetadataBlock && t.dataType == DataType.NoData);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_index.Add(new IndexEntry
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
blockType = BlockType.MetadataBlock,
|
|
|
|
|
dataType = DataType.NoData,
|
2020-07-20 21:11:32 +01:00
|
|
|
offset = (ulong)_imageStream.Position
|
2018-07-23 23:25:43 +01:00
|
|
|
});
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_header.indexOffset = (ulong)_imageStream.Position;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing index to position {0}", _header.indexOffset);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_blockStream = new NonClosableStream();
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
// Write index to memory
|
2020-07-20 21:11:32 +01:00
|
|
|
foreach(IndexEntry entry in _index)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<IndexEntry>()];
|
2019-03-15 22:07:10 +00:00
|
|
|
IndexEntry indexEntry = entry;
|
2020-07-20 21:11:32 +01:00
|
|
|
MemoryMarshal.Write(_structureBytes, ref indexEntry);
|
|
|
|
|
_blockStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Crc64Context.Data(_blockStream.ToArray(), out byte[] idxCrc);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_index.Count > ushort.MaxValue)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_header.imageMajorVersion = AARUFMT_VERSION;
|
2020-04-03 06:49:23 +01:00
|
|
|
|
|
|
|
|
var idxHeader = new IndexHeader2
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.Index2,
|
2020-07-20 21:11:32 +01:00
|
|
|
entries = (ulong)_index.Count,
|
2020-04-03 06:49:23 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(idxCrc, 0)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Write index header to disk
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<IndexHeader2>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref idxHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-04-03 06:49:23 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var idxHeader = new IndexHeader
|
|
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.Index,
|
2020-07-20 21:11:32 +01:00
|
|
|
entries = (ushort)_index.Count,
|
2020-04-03 06:49:23 +01:00
|
|
|
crc64 = BitConverter.ToUInt64(idxCrc, 0)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Write index header to disk
|
2020-07-20 21:11:32 +01:00
|
|
|
_structureBytes = new byte[Marshal.SizeOf<IndexHeader>()];
|
|
|
|
|
MemoryMarshal.Write(_structureBytes, ref idxHeader);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
2020-04-03 06:49:23 +01:00
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
// Write index to disk
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageStream.Write(_blockStream.ToArray(), 0, (int)_blockStream.Length);
|
|
|
|
|
_blockStream.ReallyClose();
|
|
|
|
|
_blockStream = null;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-02-27 23:48:41 +00:00
|
|
|
AaruConsole.DebugWriteLine("Aaru Format plugin", "Writing header");
|
2020-07-20 21:11:32 +01:00
|
|
|
_header.lastWrittenTime = DateTime.UtcNow.ToFileTimeUtc();
|
|
|
|
|
_imageStream.Position = 0;
|
|
|
|
|
_structurePointer = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf<AaruHeader>());
|
|
|
|
|
_structureBytes = new byte[Marshal.SizeOf<AaruHeader>()];
|
|
|
|
|
System.Runtime.InteropServices.Marshal.StructureToPtr(_header, _structurePointer, true);
|
|
|
|
|
System.Runtime.InteropServices.Marshal.Copy(_structurePointer, _structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
System.Runtime.InteropServices.Marshal.FreeHGlobal(_structurePointer);
|
|
|
|
|
_imageStream.Write(_structureBytes, 0, _structureBytes.Length);
|
|
|
|
|
|
|
|
|
|
_imageStream.Flush();
|
|
|
|
|
_imageStream.Close();
|
2018-12-01 19:38:43 +00:00
|
|
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
IsWriting = false;
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SetMetadata(ImageInfo metadata)
|
|
|
|
|
{
|
2020-07-20 21:11:32 +01:00
|
|
|
_imageInfo.Creator = metadata.Creator;
|
|
|
|
|
_imageInfo.Comments = metadata.Comments;
|
|
|
|
|
_imageInfo.MediaManufacturer = metadata.MediaManufacturer;
|
|
|
|
|
_imageInfo.MediaModel = metadata.MediaModel;
|
|
|
|
|
_imageInfo.MediaSerialNumber = metadata.MediaSerialNumber;
|
|
|
|
|
_imageInfo.MediaBarcode = metadata.MediaBarcode;
|
|
|
|
|
_imageInfo.MediaPartNumber = metadata.MediaPartNumber;
|
|
|
|
|
_imageInfo.MediaSequence = metadata.MediaSequence;
|
|
|
|
|
_imageInfo.LastMediaSequence = metadata.LastMediaSequence;
|
|
|
|
|
_imageInfo.DriveManufacturer = metadata.DriveManufacturer;
|
|
|
|
|
_imageInfo.DriveModel = metadata.DriveModel;
|
|
|
|
|
_imageInfo.DriveSerialNumber = metadata.DriveSerialNumber;
|
|
|
|
|
_imageInfo.DriveFirmwareRevision = metadata.DriveFirmwareRevision;
|
|
|
|
|
_imageInfo.MediaTitle = metadata.MediaTitle;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack)
|
|
|
|
|
{
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write on a non-writable image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_imageInfo.XmlMediaType != XmlMediaType.BlockMedia)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
ErrorMessage = "Tried to set geometry on a media that doesn't support it";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_geometryBlock = new GeometryBlock
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
2020-07-20 04:34:16 +01:00
|
|
|
identifier = BlockType.GeometryBlock,
|
|
|
|
|
cylinders = cylinders,
|
|
|
|
|
heads = heads,
|
2018-07-23 23:25:43 +01:00
|
|
|
sectorsPerTrack = sectorsPerTrack
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ErrorMessage = "";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag)
|
|
|
|
|
{
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write on a non-writable image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(sectorAddress >= _imageInfo.Sectors)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write past image size";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 07:47:12 +01:00
|
|
|
Track track;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
switch(tag)
|
|
|
|
|
{
|
|
|
|
|
case SectorTagType.CdTrackFlags:
|
|
|
|
|
case SectorTagType.CdTrackIsrc:
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
|
2020-06-14 23:45:26 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect tag for disk type";
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
track = Tracks.FirstOrDefault(trk => sectorAddress == trk.TrackSequence);
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
if(track is null ||
|
|
|
|
|
(track.TrackSequence == 0 && track.TrackStartSector == 0 && track.TrackEndSector == 0))
|
2020-06-14 23:45:26 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = $"Can't find track {sectorAddress}";
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
2018-07-23 23:25:43 +01:00
|
|
|
case SectorTagType.CdSectorSubchannel:
|
2020-07-20 21:11:32 +01:00
|
|
|
if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect tag for disk type";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
track = Tracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector &&
|
|
|
|
|
sectorAddress <= trk.TrackEndSector);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2020-11-06 21:47:35 +00:00
|
|
|
if(track != null &&
|
|
|
|
|
track.TrackSequence == 0 &&
|
|
|
|
|
track.TrackStartSector == 0 &&
|
2020-01-12 01:02:06 +00:00
|
|
|
track.TrackEndSector == 0)
|
2020-06-16 22:29:56 +01:00
|
|
|
track.TrackType = TrackType.Data;
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(tag)
|
|
|
|
|
{
|
|
|
|
|
case SectorTagType.CdTrackFlags:
|
|
|
|
|
{
|
|
|
|
|
if(data.Length != 1)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size for track flags";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
_trackFlags[(byte)sectorAddress] = data[0];
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
case SectorTagType.CdTrackIsrc:
|
|
|
|
|
{
|
2020-01-12 01:02:06 +00:00
|
|
|
if(data != null)
|
2020-07-20 21:11:32 +01:00
|
|
|
_trackIsrcs[(byte)sectorAddress] = Encoding.UTF8.GetString(data);
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
case SectorTagType.CdSectorSubchannel:
|
|
|
|
|
{
|
|
|
|
|
if(data.Length != 96)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size for subchannel";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorSubchannel ??= new byte[_imageInfo.Sectors * 96];
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Array.Copy(data, 0, _sectorSubchannel, (int)(96 * sectorAddress), 96);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
case SectorTagType.DvdCmi:
|
|
|
|
|
{
|
|
|
|
|
if(data.Length != 1)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size for CMI";
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
_sectorCpiMai ??= new byte[_imageInfo.Sectors * 6];
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
Array.Copy(data, 0, _sectorCpiMai, (int)(6 * sectorAddress), 1);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
case SectorTagType.DvdTitleKey:
|
|
|
|
|
{
|
|
|
|
|
if(data.Length != 5)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size for title key";
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
_sectorCpiMai ??= new byte[_imageInfo.Sectors * 6];
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
Array.Copy(data, 0, _sectorCpiMai, (int)(1 + (6 * sectorAddress)), 5);
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
case SectorTagType.DvdTitleKeyDecrypted:
|
|
|
|
|
{
|
|
|
|
|
if(data.Length != 5)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size for decrypted title key";
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
_sectorDecryptedTitleKey ??= new byte[_imageInfo.Sectors * 5];
|
2021-01-16 11:09:42 +01:00
|
|
|
|
2021-01-13 22:22:34 +01:00
|
|
|
Array.Copy(data, 0, _sectorDecryptedTitleKey, (int)(5 * sectorAddress), 5);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-05-01 22:51:30 +01:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
default:
|
|
|
|
|
ErrorMessage = $"Don't know how to write sector tag type {tag}";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag)
|
|
|
|
|
{
|
|
|
|
|
if(!IsWriting)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write on a non-writable image";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if(sectorAddress + length > _imageInfo.Sectors)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write past image size";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(tag)
|
|
|
|
|
{
|
|
|
|
|
case SectorTagType.CdTrackFlags:
|
|
|
|
|
case SectorTagType.CdTrackIsrc: return WriteSectorTag(data, sectorAddress, tag);
|
|
|
|
|
case SectorTagType.CdSectorSubchannel:
|
|
|
|
|
{
|
|
|
|
|
if(data.Length % 96 != 0)
|
|
|
|
|
{
|
|
|
|
|
ErrorMessage = "Incorrect data size for subchannel";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
_sectorSubchannel ??= new byte[_imageInfo.Sectors * 96];
|
2018-07-23 23:25:43 +01:00
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
if((sectorAddress * 96) + (length * 96) > (ulong)_sectorSubchannel.LongLength)
|
2018-07-23 23:25:43 +01:00
|
|
|
{
|
|
|
|
|
ErrorMessage = "Tried to write more data than possible";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-20 21:11:32 +01:00
|
|
|
Array.Copy(data, 0, _sectorSubchannel, (int)(96 * sectorAddress), 96 * length);
|
2018-07-23 23:25:43 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ErrorMessage = $"Don't know how to write sector tag type {tag}";
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SetDumpHardware(List<DumpHardwareType> dumpHardware)
|
|
|
|
|
{
|
|
|
|
|
DumpHardware = dumpHardware;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SetCicmMetadata(CICMMetadataType metadata)
|
|
|
|
|
{
|
|
|
|
|
CicmMetadata = metadata;
|
2020-01-12 01:02:06 +00:00
|
|
|
|
2018-07-23 23:25:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2018-12-01 19:38:43 +00:00
|
|
|
|
2020-01-12 01:02:06 +00:00
|
|
|
/// <summary>This method exists to ensure the .NET memory allocator frees the LZ tree on each call</summary>
|
2018-12-01 19:38:43 +00:00
|
|
|
/// <param name="data">Data to compress</param>
|
|
|
|
|
/// <param name="properties">LZMA properties</param>
|
|
|
|
|
/// <param name="stream">Stream where to write the compressed data to</param>
|
|
|
|
|
/// <returns>The properties as a byte array</returns>
|
2020-07-20 07:47:12 +01:00
|
|
|
static byte[] CompressDataToStreamWithLZMA(byte[] data, LzmaEncoderProperties properties, Stream stream)
|
2018-12-01 19:38:43 +00:00
|
|
|
{
|
2020-07-22 13:20:25 +01:00
|
|
|
using var lzmaStream = new LzmaStream(properties, false, stream);
|
2018-12-01 19:38:43 +00:00
|
|
|
|
2020-07-22 13:20:25 +01:00
|
|
|
lzmaStream.Write(data, 0, data.Length);
|
|
|
|
|
byte[] propertiesArray = new byte[lzmaStream.Properties.Length];
|
|
|
|
|
lzmaStream.Properties.CopyTo(propertiesArray, 0);
|
2018-12-01 19:38:43 +00:00
|
|
|
|
|
|
|
|
return propertiesArray;
|
|
|
|
|
}
|
2018-07-23 23:25:43 +01:00
|
|
|
}
|
|
|
|
|
}
|