diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index b8f015afe..3a33c3c96 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -684,6 +684,7 @@ + diff --git a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj index 938775e92..7b7301a73 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj +++ b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj @@ -198,6 +198,7 @@ + diff --git a/DiscImageChef.DiscImages/DiscImageChef/CdEcc.cs b/DiscImageChef.DiscImages/DiscImageChef/CdEcc.cs new file mode 100644 index 000000000..6523da92f --- /dev/null +++ b/DiscImageChef.DiscImages/DiscImageChef/CdEcc.cs @@ -0,0 +1,123 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : ClauniaSubchannelTransform.cs +// Author(s) : Natalia Portillo +// +// Component : Disk image plugins. +// +// --[ Description ] ---------------------------------------------------------- +// +// Contains the CD ECC algorithm. +// +// --[ 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 . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2018 Natalia Portillo +// ECC algorithm from ECM(c) 2002-2011 Neill Corlett +// ****************************************************************************/ + +using System; + +namespace DiscImageChef.DiscImages +{ + public partial class DiscImageChef + { + byte[] eccBTable; + byte[] eccFTable; + uint[] edcTable; + + void EccInit() + { + eccFTable = new byte[256]; + eccBTable = new byte[256]; + edcTable = new uint[256]; + + for(uint i = 0; i < 256; i++) + { + uint edc = i; + uint j = (uint)((i << 1) ^ ((i & 0x80) == 0x80 ? 0x11D : 0)); + eccFTable[i] = (byte)j; + eccBTable[i ^ j] = (byte)i; + for(j = 0; j < 8; j++) edc = (edc >> 1) ^ ((edc & 1) > 0 ? 0xD8018001 : 0); + edcTable[i] = edc; + } + } + + bool SuffixIsCorrect(byte[] channel) + { + if(channel[0x814] != 0x00 || // reserved (8 bytes) + channel[0x815] != 0x00 || channel[0x816] != 0x00 || channel[0x817] != 0x00 || channel[0x818] != 0x00 || + channel[0x819] != 0x00 || channel[0x81A] != 0x00 || channel[0x81B] != 0x00) return false; + + byte[] address = new byte[4]; + byte[] data = new byte[2060]; + byte[] data2 = new byte[2232]; + byte[] eccP = new byte[172]; + byte[] eccQ = new byte[104]; + + Array.Copy(channel, 0x0C, address, 0, 4); + Array.Copy(channel, 0x10, data, 0, 2060); + Array.Copy(channel, 0x10, data2, 0, 2232); + Array.Copy(channel, 0x81C, eccP, 0, 172); + Array.Copy(channel, 0x8C8, eccQ, 0, 104); + + bool correctEccP = CheckEcc(ref address, ref data, 86, 24, 2, 86, ref eccP); + if(!correctEccP) return false; + + bool correctEccQ = CheckEcc(ref address, ref data2, 52, 43, 86, 88, ref eccQ); + if(!correctEccQ) return false; + + uint storedEdc = BitConverter.ToUInt32(channel, 0x810); + uint edc = 0; + int size = 0x810; + int pos = 0; + for(; size > 0; size--) edc = (edc >> 8) ^ edcTable[(edc ^ channel[pos++]) & 0xFF]; + uint calculatedEdc = edc; + + return calculatedEdc == storedEdc; + } + + bool CheckEcc(ref byte[] address, ref byte[] data, uint majorCount, uint minorCount, uint majorMult, + uint minorInc, ref byte[] ecc) + { + uint size = majorCount * minorCount; + uint major; + for(major = 0; major < majorCount; major++) + { + uint idx = (major >> 1) * majorMult + (major & 1); + byte eccA = 0; + byte eccB = 0; + uint minor; + for(minor = 0; minor < minorCount; minor++) + { + byte temp = idx < 4 ? address[idx] : data[idx - 4]; + idx += minorInc; + if(idx >= size) idx -= size; + eccA ^= temp; + eccB ^= temp; + eccA = eccFTable[eccA]; + } + + eccA = eccBTable[eccFTable[eccA] ^ eccB]; + if(ecc[major] != eccA || ecc[major + majorCount] != (eccA ^ eccB)) return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/DiscImageChef.DiscImages/DiscImageChef/Constants.cs b/DiscImageChef.DiscImages/DiscImageChef/Constants.cs index 3dbd102fc..7a7c90d2f 100644 --- a/DiscImageChef.DiscImages/DiscImageChef/Constants.cs +++ b/DiscImageChef.DiscImages/DiscImageChef/Constants.cs @@ -56,5 +56,9 @@ namespace DiscImageChef.DiscImages /// smaller than 256. /// const int MIN_FLAKE_BLOCK = 256; + /// This mask is to check for flags in CompactDisc suffix/prefix DDT + const uint CD_XFIX_MASK = 0xFF000000; + /// This mask is to check for position in CompactDisc suffix/prefix deduplicated block + const uint CD_DFIX_MASK = 0x00FFFFFF; } } \ No newline at end of file diff --git a/DiscImageChef.DiscImages/DiscImageChef/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef/DiscImageChef.cs index 02166d338..eeeb92eaf 100644 --- a/DiscImageChef.DiscImages/DiscImageChef/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef/DiscImageChef.cs @@ -136,10 +136,14 @@ namespace DiscImageChef.DiscImages bool rewinded; /// Cache for data that prefixes the user data on a sector (e.g. sync). byte[] sectorPrefix; + uint[] sectorPrefixDdt; + MemoryStream sectorPrefixMs; /// Cache for data that goes side by side with user data (e.g. CompactDisc subchannel). byte[] sectorSubchannel; /// Cache for data that suffixes the user data on a sector (e.g. edc, ecc). byte[] sectorSuffix; + uint[] sectorSuffixDdt; + MemoryStream sectorSuffixMs; Sha1Context sha1Provider; Sha256Context sha256Provider; /// Shift for calculating number of sectors in a block. diff --git a/DiscImageChef.DiscImages/DiscImageChef/Enums.cs b/DiscImageChef.DiscImages/DiscImageChef/Enums.cs index 5df7ff313..5608dcc46 100644 --- a/DiscImageChef.DiscImages/DiscImageChef/Enums.cs +++ b/DiscImageChef.DiscImages/DiscImageChef/Enums.cs @@ -201,7 +201,11 @@ namespace DiscImageChef.DiscImages /// Priam Data Tower (24 byte) tag PriamDataTowerTag = 74, /// CompactDisc Media Catalogue Number (as in Lead-in), 13 bytes, ASCII - CompactDiscMediaCatalogueNumber = 75 + CompactDiscMediaCatalogueNumber = 75, + /// CompactDisc sector prefix (sync, header), only incorrect stored + CdSectorPrefixCorrected = 76, + /// CompactDisc sector suffix (edc, ecc p, ecc q), only incorrect stored + CdSectorSuffixCorrected = 77 } /// List of known blocks types @@ -243,5 +247,11 @@ namespace DiscImageChef.DiscImages Sha256 = 3, SpamSum = 4 } + + enum CdFixFlags : uint + { + NotDumped = 0x10000000, + Correct = 0x20000000 + } } } \ No newline at end of file diff --git a/DiscImageChef.DiscImages/DiscImageChef/Read.cs b/DiscImageChef.DiscImages/DiscImageChef/Read.cs index c91acb8c1..9982200a2 100644 --- a/DiscImageChef.DiscImages/DiscImageChef/Read.cs +++ b/DiscImageChef.DiscImages/DiscImageChef/Read.cs @@ -224,14 +224,28 @@ namespace DiscImageChef.DiscImages switch(entry.dataType) { case DataType.CdSectorPrefix: - sectorPrefix = data; + case DataType.CdSectorPrefixCorrected: + if(entry.dataType == DataType.CdSectorPrefixCorrected) + { + sectorPrefixMs = new MemoryStream(); + sectorPrefixMs.Write(data, 0, data.Length); + } + else sectorPrefix = data; + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync)) imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync); if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader)) imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader); break; case DataType.CdSectorSuffix: - sectorSuffix = data; + case DataType.CdSectorSuffixCorrected: + if(entry.dataType == DataType.CdSectorSuffixCorrected) + { + sectorSuffixMs = new MemoryStream(); + 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)) @@ -274,57 +288,108 @@ namespace DiscImageChef.DiscImages break; case BlockType.DeDuplicationTable: // Only user data deduplication tables are used right now - if(entry.dataType != DataType.UserData) break; - - DdtHeader ddtHeader = new DdtHeader(); - structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; - imageStream.Read(structureBytes, 0, structureBytes.Length); - structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); - Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); - ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); - Marshal.FreeHGlobal(structurePointer); - imageInfo.ImageSize += ddtHeader.cmpLength; - - if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; - - imageInfo.Sectors = ddtHeader.entries; - shift = ddtHeader.shift; - - // Check for DDT compression - switch(ddtHeader.compression) + if(entry.dataType == DataType.UserData) { - case CompressionType.Lzma: - DicConsole.DebugWriteLine("DiscImageChef format plugin", "Decompressing DDT..."); - DateTime ddtStart = DateTime.UtcNow; - byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; - byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; - imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); - imageStream.Read(compressedDdt, 0, compressedDdt.Length); - MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); - LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); - byte[] decompressedDdt = new byte[ddtHeader.length]; - lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); - lzmaDdt.Close(); - compressedDdtMs.Close(); - userDataDdt = new ulong[ddtHeader.entries]; - for(ulong i = 0; i < ddtHeader.entries; i++) - userDataDdt[i] = BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong))); - DateTime ddtEnd = DateTime.UtcNow; - inMemoryDdt = true; - DicConsole.DebugWriteLine("DiscImageChef format plugin", - "Took {0} seconds to decompress DDT", - (ddtEnd - ddtStart).TotalSeconds); - break; - case CompressionType.None: - inMemoryDdt = false; - outMemoryDdtPosition = (long)entry.offset; - break; - default: - throw new - ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + DdtHeader ddtHeader = new DdtHeader(); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); + ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); + Marshal.FreeHGlobal(structurePointer); + imageInfo.ImageSize += ddtHeader.cmpLength; + + if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; + + imageInfo.Sectors = ddtHeader.entries; + shift = ddtHeader.shift; + + // Check for DDT compression + switch(ddtHeader.compression) + { + case CompressionType.Lzma: + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Decompressing DDT..."); + DateTime ddtStart = DateTime.UtcNow; + byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedDdt, 0, compressedDdt.Length); + MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); + LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); + byte[] decompressedDdt = new byte[ddtHeader.length]; + lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); + lzmaDdt.Close(); + compressedDdtMs.Close(); + userDataDdt = new ulong[ddtHeader.entries]; + for(ulong i = 0; i < ddtHeader.entries; i++) + userDataDdt[i] = + BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong))); + DateTime ddtEnd = DateTime.UtcNow; + inMemoryDdt = true; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Took {0} seconds to decompress DDT", + (ddtEnd - ddtStart).TotalSeconds); + break; + case CompressionType.None: + inMemoryDdt = false; + outMemoryDdtPosition = (long)entry.offset; + break; + default: + throw new + ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + } + + foundUserDataDdt = true; + } + else if(entry.dataType == DataType.CdSectorPrefixCorrected || + entry.dataType == DataType.CdSectorSuffixCorrected) + { + DdtHeader ddtHeader = new DdtHeader(); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); + ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); + Marshal.FreeHGlobal(structurePointer); + imageInfo.ImageSize += ddtHeader.cmpLength; + + if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; + + uint[] cdDdt = new uint[ddtHeader.entries]; + byte[] decompressedDdt = new byte[ddtHeader.length]; + + // Check for DDT compression + switch(ddtHeader.compression) + { + case CompressionType.Lzma: + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Decompressing DDT..."); + DateTime ddtStart = DateTime.UtcNow; + byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedDdt, 0, compressedDdt.Length); + MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); + LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); + lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); + lzmaDdt.Close(); + compressedDdtMs.Close(); + DateTime ddtEnd = DateTime.UtcNow; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Took {0} seconds to decompress DDT", + (ddtEnd - ddtStart).TotalSeconds); + break; + case CompressionType.None: + imageStream.Read(decompressedDdt, 0, decompressedDdt.Length); + break; + default: + throw new + ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + } + + for(ulong i = 0; i < ddtHeader.entries; i++) + cdDdt[i] = BitConverter.ToUInt32(decompressedDdt, (int)(i * sizeof(uint))); } - foundUserDataDdt = true; break; // Logical geometry block. It doesn't have a CRC coz, well, it's not so important case BlockType.GeometryBlock: @@ -1234,7 +1299,8 @@ namespace DiscImageChef.DiscImages throw new ArgumentOutOfRangeException(nameof(sectorAddress), "Can't found track containing requested sector"); - if(sectorSuffix == null || sectorPrefix == null) return ReadSector(sectorAddress); + if((sectorSuffix == null || sectorPrefix == null) && + (sectorSuffixMs == null || sectorPrefixMs == null)) return ReadSector(sectorAddress); byte[] sector = new byte[2352]; byte[] data = ReadSector(sectorAddress); @@ -1244,9 +1310,17 @@ namespace DiscImageChef.DiscImages case TrackType.Audio: case TrackType.Data: return data; case TrackType.CdMode1: - Array.Copy(sectorPrefix, (int)sectorAddress * 16, sector, 0, 16); - Array.Copy(data, 0, sector, 16, 2048); - Array.Copy(sectorSuffix, (int)sectorAddress * 288, sector, 2064, 288); + if(sectorPrefix != null) + Array.Copy(sectorPrefix, (int)sectorAddress * 16, sector, 0, 16); + else if(sectorPrefixMs != null) throw new NotImplementedException(); + else throw new InvalidProgramException("Should not have arrived here"); + + if(sectorSuffix != null) + Array.Copy(sectorSuffix, (int)sectorAddress * 288, sector, 2064, 288); + else if(sectorSuffixMs != null) throw new NotImplementedException(); + else throw new InvalidProgramException("Should not have arrived here"); + + Array.Copy(data, 0, sector, 16, 2048); return sector; case TrackType.CdMode2Formless: case TrackType.CdMode2Form1: diff --git a/DiscImageChef.DiscImages/DiscImageChef/Write.cs b/DiscImageChef.DiscImages/DiscImageChef/Write.cs index 0f82b3fc2..62f05bac1 100644 --- a/DiscImageChef.DiscImages/DiscImageChef/Write.cs +++ b/DiscImageChef.DiscImages/DiscImageChef/Write.cs @@ -312,6 +312,8 @@ namespace DiscImageChef.DiscImages { case DataType.CdSectorPrefix: case DataType.CdSectorSuffix: + case DataType.CdSectorPrefixCorrected: + case DataType.CdSectorSuffixCorrected: case DataType.CdSectorSubchannel: case DataType.AppleProfileTag: case DataType.AppleSonyTag: @@ -394,6 +396,14 @@ namespace DiscImageChef.DiscImages case DataType.CdSectorSuffix: sectorSuffix = data; break; + case DataType.CdSectorPrefixCorrected: + sectorPrefixMs = new MemoryStream(); + sectorPrefixMs.Write(data, 0, data.Length); + break; + case DataType.CdSectorSuffixCorrected: + sectorSuffixMs = new MemoryStream(); + sectorSuffixMs.Write(data, 0, data.Length); + break; case DataType.CdSectorSubchannel: case DataType.AppleProfileTag: case DataType.AppleSonyTag: @@ -405,62 +415,131 @@ namespace DiscImageChef.DiscImages break; case BlockType.DeDuplicationTable: // Only user data deduplication tables are used right now - if(entry.dataType != DataType.UserData) break; - - DdtHeader ddtHeader = new DdtHeader(); - structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; - imageStream.Read(structureBytes, 0, structureBytes.Length); - structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); - Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); - ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); - Marshal.FreeHGlobal(structurePointer); - - if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; - - if(ddtHeader.entries != imageInfo.Sectors) + if(entry.dataType == DataType.UserData) { - ErrorMessage = - $"Trying to write a media with {imageInfo.Sectors} sectors to an image with {ddtHeader.entries} sectors, not continuing..."; - return false; + DdtHeader ddtHeader = new DdtHeader(); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); + ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); + Marshal.FreeHGlobal(structurePointer); + + if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; + + if(ddtHeader.entries != imageInfo.Sectors) + { + ErrorMessage = + $"Trying to write a media with {imageInfo.Sectors} sectors to an image with {ddtHeader.entries} sectors, not continuing..."; + return false; + } + + shift = ddtHeader.shift; + + switch(ddtHeader.compression) + { + case CompressionType.Lzma: + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Decompressing DDT..."); + DateTime ddtStart = DateTime.UtcNow; + byte[] compressedDdt = + new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedDdt, 0, compressedDdt.Length); + MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); + LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); + byte[] decompressedDdt = new byte[ddtHeader.length]; + lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); + lzmaDdt.Close(); + compressedDdtMs.Close(); + userDataDdt = new ulong[ddtHeader.entries]; + for(ulong i = 0; i < ddtHeader.entries; i++) + userDataDdt[i] = + BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong))); + DateTime ddtEnd = DateTime.UtcNow; + inMemoryDdt = true; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Took {0} seconds to decompress DDT", + (ddtEnd - ddtStart).TotalSeconds); + break; + case CompressionType.None: + inMemoryDdt = false; + outMemoryDdtPosition = (long)entry.offset; + break; + default: + throw new + ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + } + + foundUserDataDdt = true; + } + else if(entry.dataType == DataType.CdSectorPrefixCorrected || + entry.dataType == DataType.CdSectorSuffixCorrected) + { + DdtHeader ddtHeader = new DdtHeader(); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); + ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); + Marshal.FreeHGlobal(structurePointer); + + if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; + + if(ddtHeader.entries != imageInfo.Sectors) + { + ErrorMessage = + $"Trying to write a media with {imageInfo.Sectors} sectors to an image with {ddtHeader.entries} sectors, not continuing..."; + return false; + } + + byte[] decompressedDdt = new byte[ddtHeader.length]; + uint[] cdDdt = new uint[ddtHeader.entries]; + + switch(ddtHeader.compression) + { + case CompressionType.Lzma: + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Decompressing DDT..."); + DateTime ddtStart = DateTime.UtcNow; + byte[] compressedDdt = + new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedDdt, 0, compressedDdt.Length); + MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); + LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); + lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); + lzmaDdt.Close(); + compressedDdtMs.Close(); + DateTime ddtEnd = DateTime.UtcNow; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Took {0} seconds to decompress DDT", + (ddtEnd - ddtStart).TotalSeconds); + break; + case CompressionType.None: + imageStream.Read(decompressedDdt, 0, decompressedDdt.Length); + break; + default: + throw new + ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + } + + for(ulong i = 0; i < ddtHeader.entries; i++) + cdDdt[i] = BitConverter.ToUInt32(decompressedDdt, (int)(i * sizeof(uint))); + + switch(entry.dataType) + { + case DataType.CdSectorPrefixCorrected: + sectorPrefixDdt = cdDdt; + break; + case DataType.CdSectorSuffixCorrected: + sectorSuffixDdt = cdDdt; + break; + } } - shift = ddtHeader.shift; - - switch(ddtHeader.compression) - { - case CompressionType.Lzma: - DicConsole.DebugWriteLine("DiscImageChef format plugin", "Decompressing DDT..."); - DateTime ddtStart = DateTime.UtcNow; - byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; - byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; - imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); - imageStream.Read(compressedDdt, 0, compressedDdt.Length); - MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); - LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); - byte[] decompressedDdt = new byte[ddtHeader.length]; - lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); - lzmaDdt.Close(); - compressedDdtMs.Close(); - userDataDdt = new ulong[ddtHeader.entries]; - for(ulong i = 0; i < ddtHeader.entries; i++) - userDataDdt[i] = - BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong))); - DateTime ddtEnd = DateTime.UtcNow; - inMemoryDdt = true; - DicConsole.DebugWriteLine("DiscImageChef format plugin", - "Took {0} seconds to decompress DDT", - (ddtEnd - ddtStart).TotalSeconds); - break; - case CompressionType.None: - inMemoryDdt = false; - outMemoryDdtPosition = (long)entry.offset; - break; - default: - throw new - ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); - } - - foundUserDataDdt = true; break; // CICM XML metadata block case BlockType.CicmBlock: @@ -634,6 +713,15 @@ namespace DiscImageChef.DiscImages ErrorMessage = "Could not find user data deduplication table."; return false; } + + if(sectorSuffixMs == null || sectorSuffixDdt == null || sectorPrefixMs == null || + sectorPrefixDdt == null) + { + sectorSuffixMs = null; + sectorSuffixDdt = null; + sectorPrefixMs = null; + sectorPrefixDdt = null; + } } // Creating new else @@ -1018,27 +1106,150 @@ namespace DiscImageChef.DiscImages lastWrittenBlock = sectorAddress; } + bool prefixCorrect; + int minute; + int second; + int frame; + int storedLba; + // 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: - if(sectorPrefix == null) sectorPrefix = new byte[imageInfo.Sectors * 16]; - if(sectorSuffix == null) sectorSuffix = new byte[imageInfo.Sectors * 288]; + if(sectorPrefix != null && sectorSuffix != null) + { + sector = new byte[2048]; + Array.Copy(data, 0, sectorPrefix, (int)sectorAddress * 16, 16); + Array.Copy(data, 16, sector, 0, 2048); + Array.Copy(data, 2064, sectorSuffix, (int)sectorAddress * 288, 288); + return WriteSector(sector, sectorAddress); + } + + if(sectorSuffixMs == null) sectorSuffixMs = new MemoryStream(); + if(sectorPrefixMs == null) sectorPrefixMs = new MemoryStream(); + if(sectorSuffixDdt == null) + { + sectorSuffixDdt = new uint[imageInfo.Sectors]; + EccInit(); + } + + if(sectorPrefixDdt == null) sectorPrefixDdt = new uint[imageInfo.Sectors]; + sector = new byte[2048]; - Array.Copy(data, 0, sectorPrefix, (int)sectorAddress * 16, 16); - Array.Copy(data, 16, sector, 0, 2048); - Array.Copy(data, 2064, sectorSuffix, (int)sectorAddress * 288, 288); + if(ArrayHelpers.ArrayIsNullOrEmpty(data)) + { + sectorPrefixDdt[sectorAddress] = (uint)CdFixFlags.NotDumped; + sectorSuffixDdt[sectorAddress] = (uint)CdFixFlags.NotDumped; + return WriteSector(sector, sectorAddress); + } + + prefixCorrect = true; + + 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; + + if(prefixCorrect) + { + 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; + prefixCorrect = storedLba == (int)sectorAddress; + } + + if(prefixCorrect) sectorPrefixDdt[sectorAddress] = (uint)CdFixFlags.Correct; + else + { + if((sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) > 0) + sectorPrefixMs.Position = + ((sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16; + else sectorPrefixMs.Seek(0, SeekOrigin.End); + + sectorPrefixDdt[sectorAddress] = (uint)(sectorPrefixMs.Position / 16 + 1); + sectorPrefixMs.Write(data, 0, 16); + } + + bool correct = SuffixIsCorrect(data); + + if(correct) sectorSuffixDdt[sectorAddress] = (uint)CdFixFlags.Correct; + else + { + if((sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) > 0) + sectorSuffixMs.Position = + ((sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288; + else sectorSuffixMs.Seek(0, SeekOrigin.End); + + sectorSuffixDdt[sectorAddress] = (uint)(sectorSuffixMs.Position / 288 + 1); + + sectorSuffixMs.Write(data, 2064, 288); + } + + Array.Copy(data, 16, sector, 0, 2048); return WriteSector(sector, sectorAddress); case TrackType.CdMode2Formless: case TrackType.CdMode2Form1: case TrackType.CdMode2Form2: - if(sectorPrefix == null) sectorPrefix = new byte[imageInfo.Sectors * 16]; - if(sectorSuffix == null) sectorSuffix = new byte[imageInfo.Sectors * 288]; + if(sectorPrefix != null && sectorSuffix != null) + { + sector = new byte[2336]; + Array.Copy(data, 0, sectorPrefix, (int)sectorAddress * 16, 16); + Array.Copy(data, 16, sector, 0, 2336); + return WriteSector(sector, sectorAddress); + } + + if(sectorSuffixMs == null) sectorSuffixMs = new MemoryStream(); + if(sectorPrefixMs == null) sectorPrefixMs = new MemoryStream(); + if(sectorSuffixDdt == null) + { + sectorSuffixDdt = new uint[imageInfo.Sectors]; + EccInit(); + } + + if(sectorPrefixDdt == null) sectorPrefixDdt = new uint[imageInfo.Sectors]; + sector = new byte[2336]; - Array.Copy(data, 0, sectorPrefix, (int)sectorAddress * 16, 16); - Array.Copy(data, 16, sector, 0, 2336); + if(ArrayHelpers.ArrayIsNullOrEmpty(sector)) + { + sectorPrefixDdt[sectorAddress] = (uint)CdFixFlags.NotDumped; + return WriteSector(sector, sectorAddress); + } + + prefixCorrect = true; + + 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 || data[0x10] != 0xFF || data[0x11] != 0xFF || data[0x12] != 0x00 || + data[0x13] != 0xFF || data[0x14] != 0xFF || data[0x15] != 0xFF || data[0x16] != 0x00 || + data[0x17] != 0x00) return false; + + if(prefixCorrect) + { + 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; + prefixCorrect = storedLba == (int)sectorAddress; + } + + if(prefixCorrect) sectorPrefixDdt[sectorAddress] = (uint)CdFixFlags.Correct; + else + { + if((sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) > 0) + sectorPrefixMs.Position = + ((sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16; + else sectorPrefixMs.Seek(0, SeekOrigin.End); + + sectorPrefixDdt[sectorAddress] = (uint)(sectorPrefixMs.Position / 16 + 1); + + sectorPrefixMs.Write(data, 0, 16); + } + + Array.Copy(data, 16, sector, 0, 2336); return WriteSector(sector, sectorAddress); } @@ -1744,6 +1955,7 @@ namespace DiscImageChef.DiscImages case XmlMediaType.OpticalDisc when Tracks != null && Tracks.Count > 0: DateTime startCompress; DateTime endCompress; + // Old format if(sectorPrefix != null && sectorSuffix != null) { idxEntry = new IndexEntry @@ -1883,6 +2095,290 @@ namespace DiscImageChef.DiscImages index.Add(idxEntry); blockStream = null; } + else if(sectorSuffixMs != null && sectorSuffixDdt != null && sectorPrefixMs != null && + sectorPrefixDdt != null) + { + uint notDumpedPrefixes = 0; + uint correctPrefixes = 0; + uint writtenPrefixes = 0; + uint notDumpedSuffixes = 0; + uint correctSuffixes = 0; + uint writtenSuffixes = 0; + + for(long i = 0; i < sectorPrefixDdt.LongLength; i++) + if((sectorPrefixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.NotDumped) + notDumpedPrefixes++; + else if((sectorPrefixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct) correctPrefixes++; + else if((sectorPrefixDdt[i] & CD_DFIX_MASK) > 0) writtenPrefixes++; + + for(long i = 0; i < sectorPrefixDdt.LongLength; i++) + if((sectorSuffixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.NotDumped) + notDumpedSuffixes++; + else if((sectorSuffixDdt[i] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct) correctSuffixes++; + else if((sectorSuffixDdt[i] & CD_DFIX_MASK) > 0) writtenSuffixes++; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "{0} ({1:P}% prefixes are correct, {2} ({3:P}%) prefixes have not been dumped, {4} ({5:P}%) prefixes have been written to image", + correctPrefixes, correctPrefixes / imageInfo.Sectors, + notDumpedPrefixes, notDumpedPrefixes / imageInfo.Sectors, + writtenPrefixes, writtenPrefixes / imageInfo.Sectors); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "{0} ({1:P}% suffixes are correct, {2} ({3:P}%) suffixes have not been dumped, {4} ({5:P}%) suffixes have been written to image", + correctSuffixes, correctSuffixes / imageInfo.Sectors, + notDumpedSuffixes, notDumpedSuffixes / imageInfo.Sectors, + writtenSuffixes, writtenSuffixes / imageInfo.Sectors); + + idxEntry = new IndexEntry + { + blockType = BlockType.DeDuplicationTable, + dataType = DataType.CdSectorPrefixCorrected, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CompactDisc sector prefix DDT to position {0}", + idxEntry.offset); + + DdtHeader ddtHeader = new DdtHeader + { + identifier = BlockType.DeDuplicationTable, + type = DataType.CdSectorPrefixCorrected, + compression = CompressionType.Lzma, + entries = (ulong)sectorPrefixDdt.LongLength, + length = (ulong)(sectorPrefixDdt.LongLength * sizeof(uint)) + }; + + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + crc64 = new Crc64Context(); + for(ulong i = 0; i < (ulong)sectorPrefixDdt.LongLength; i++) + { + byte[] ddtEntry = BitConverter.GetBytes(sectorPrefixDdt[i]); + crc64.Update(ddtEntry); + lzmaBlockStream.Write(ddtEntry, 0, ddtEntry.Length); + } + + byte[] lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + ddtHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + Crc64Context cmpCrc64Context = new Crc64Context(); + cmpCrc64Context.Update(lzmaProperties); + cmpCrc64Context.Update(blockStream.ToArray()); + ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0); + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + Marshal.StructureToPtr(ddtHeader, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + structureBytes = null; + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + blockStream = null; + lzmaBlockStream = null; + + index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable && + t.dataType == DataType.CdSectorPrefixCorrected); + + index.Add(idxEntry); + + idxEntry = new IndexEntry + { + blockType = BlockType.DeDuplicationTable, + dataType = DataType.CdSectorSuffixCorrected, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CompactDisc sector suffix DDT to position {0}", + idxEntry.offset); + + ddtHeader = new DdtHeader + { + identifier = BlockType.DeDuplicationTable, + type = DataType.CdSectorSuffixCorrected, + compression = CompressionType.Lzma, + entries = (ulong)sectorSuffixDdt.LongLength, + length = (ulong)(sectorSuffixDdt.LongLength * sizeof(uint)) + }; + + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + crc64 = new Crc64Context(); + for(ulong i = 0; i < (ulong)sectorSuffixDdt.LongLength; i++) + { + byte[] ddtEntry = BitConverter.GetBytes(sectorSuffixDdt[i]); + crc64.Update(ddtEntry); + lzmaBlockStream.Write(ddtEntry, 0, ddtEntry.Length); + } + + lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + ddtHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + cmpCrc64Context = new Crc64Context(); + cmpCrc64Context.Update(lzmaProperties); + cmpCrc64Context.Update(blockStream.ToArray()); + ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0); + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + Marshal.StructureToPtr(ddtHeader, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + structureBytes = null; + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + blockStream = null; + lzmaBlockStream = null; + + index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable && + t.dataType == DataType.CdSectorSuffixCorrected); + + index.Add(idxEntry); + + idxEntry = new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = DataType.CdSectorPrefixCorrected, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CD sector corrected prefix block to position {0}", + idxEntry.offset); + + Crc64Context.Data(sectorPrefixMs.GetBuffer(), (uint)sectorPrefixMs.Length, out byte[] blockCrc); + + BlockHeader prefixBlock = new BlockHeader + { + identifier = BlockType.DataBlock, + type = DataType.CdSectorPrefixCorrected, + length = (uint)sectorPrefixMs.Length, + crc64 = BitConverter.ToUInt64(blockCrc, 0), + sectorSize = 16 + }; + + lzmaProperties = null; + + if(nocompress) + { + prefixBlock.compression = CompressionType.None; + prefixBlock.cmpCrc64 = prefixBlock.crc64; + prefixBlock.cmpLength = prefixBlock.length; + blockStream = sectorPrefixMs; + } + else + { + startCompress = DateTime.Now; + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + sectorPrefixMs.WriteTo(lzmaBlockStream); + lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + + Crc64Context cmpCrc = new Crc64Context(); + cmpCrc.Update(lzmaProperties); + cmpCrc.Update(blockStream.ToArray()); + blockCrc = cmpCrc.Final(); + prefixBlock.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + prefixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0); + prefixBlock.compression = CompressionType.Lzma; + + lzmaBlockStream = null; + endCompress = DateTime.Now; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Took {0} seconds to compress prefix", + (endCompress - startCompress).TotalSeconds); + } + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(prefixBlock)); + structureBytes = new byte[Marshal.SizeOf(prefixBlock)]; + Marshal.StructureToPtr(prefixBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + if(prefixBlock.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + + index.RemoveAll(t => t.blockType == BlockType.DataBlock && + t.dataType == DataType.CdSectorPrefixCorrected); + + index.Add(idxEntry); + + idxEntry = new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = DataType.CdSectorSuffixCorrected, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CD sector corrected suffix block to position {0}", + idxEntry.offset); + + Crc64Context.Data(sectorSuffixMs.GetBuffer(), (uint)sectorSuffixMs.Length, out blockCrc); + + BlockHeader suffixBlock = new BlockHeader + { + identifier = BlockType.DataBlock, + type = DataType.CdSectorSuffixCorrected, + length = (uint)sectorSuffixMs.Length, + crc64 = BitConverter.ToUInt64(blockCrc, 0), + sectorSize = 288 + }; + + lzmaProperties = null; + + if(nocompress) + { + suffixBlock.compression = CompressionType.None; + suffixBlock.cmpCrc64 = suffixBlock.crc64; + suffixBlock.cmpLength = suffixBlock.length; + blockStream = sectorSuffixMs; + } + else + { + startCompress = DateTime.Now; + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + sectorSuffixMs.WriteTo(lzmaBlockStream); + lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + + Crc64Context cmpCrc = new Crc64Context(); + cmpCrc.Update(lzmaProperties); + cmpCrc.Update(blockStream.ToArray()); + blockCrc = cmpCrc.Final(); + suffixBlock.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + suffixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0); + suffixBlock.compression = CompressionType.Lzma; + + lzmaBlockStream = null; + endCompress = DateTime.Now; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Took {0} seconds to compress suffix", + (endCompress - startCompress).TotalSeconds); + } + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(suffixBlock)); + structureBytes = new byte[Marshal.SizeOf(suffixBlock)]; + Marshal.StructureToPtr(suffixBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + if(suffixBlock.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + + index.RemoveAll(t => t.blockType == BlockType.DataBlock && + t.dataType == DataType.CdSectorSuffixCorrected); + + index.Add(idxEntry); + } if(sectorSubchannel != null) { diff --git a/templates/dicformat.bt b/templates/dicformat.bt index 36645c254..de65e558a 100644 --- a/templates/dicformat.bt +++ b/templates/dicformat.bt @@ -622,7 +622,9 @@ enum DataType AppleProfileTag = 72, AppleSonyTag = 73, PriamDataTowerTag = 74, - CompactDiscMediaCatalogueNumber = 75 + CompactDiscMediaCatalogueNumber = 75, + CdSectorPrefixCorrected = 76, + CdSectorSuffixCorrected = 77 }; enum BlockType