Add support in dicformat for skipping storing CD prefixes and suffixes that are correct.

This commit is contained in:
2018-07-31 23:27:08 +01:00
parent daab885f2a
commit 9aab686e93
9 changed files with 833 additions and 118 deletions

View File

@@ -684,6 +684,7 @@
<e p="Unsupported.cs" t="Include" />
</e>
<e p="DiscImageChef" t="Include">
<e p="CdEcc.cs" t="Include" />
<e p="ClauniaSubchannelTransform.cs" t="Include" />
<e p="Constants.cs" t="Include" />
<e p="DiscImageChef.cs" t="Include" />

View File

@@ -198,6 +198,7 @@
<Compile Include="DiscFerret\Read.cs" />
<Compile Include="DiscFerret\Structs.cs" />
<Compile Include="DiscFerret\Unsupported.cs" />
<Compile Include="DiscImageChef\CdEcc.cs" />
<Compile Include="DiscImageChef\ClauniaSubchannelTransform.cs" />
<Compile Include="DiscImageChef\Constants.cs" />
<Compile Include="DiscImageChef\DiscImageChef.cs" />

View File

@@ -0,0 +1,123 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : ClauniaSubchannelTransform.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// 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;
}
}
}

View File

@@ -56,5 +56,9 @@ namespace DiscImageChef.DiscImages
/// smaller than 256.
/// </summary>
const int MIN_FLAKE_BLOCK = 256;
/// <summary>This mask is to check for flags in CompactDisc suffix/prefix DDT</summary>
const uint CD_XFIX_MASK = 0xFF000000;
/// <summary>This mask is to check for position in CompactDisc suffix/prefix deduplicated block</summary>
const uint CD_DFIX_MASK = 0x00FFFFFF;
}
}

View File

@@ -136,10 +136,14 @@ namespace DiscImageChef.DiscImages
bool rewinded;
/// <summary>Cache for data that prefixes the user data on a sector (e.g. sync).</summary>
byte[] sectorPrefix;
uint[] sectorPrefixDdt;
MemoryStream sectorPrefixMs;
/// <summary>Cache for data that goes side by side with user data (e.g. CompactDisc subchannel).</summary>
byte[] sectorSubchannel;
/// <summary>Cache for data that suffixes the user data on a sector (e.g. edc, ecc).</summary>
byte[] sectorSuffix;
uint[] sectorSuffixDdt;
MemoryStream sectorSuffixMs;
Sha1Context sha1Provider;
Sha256Context sha256Provider;
/// <summary>Shift for calculating number of sectors in a block.</summary>

View File

@@ -201,7 +201,11 @@ namespace DiscImageChef.DiscImages
/// <summary>Priam Data Tower (24 byte) tag</summary>
PriamDataTowerTag = 74,
/// <summary>CompactDisc Media Catalogue Number (as in Lead-in), 13 bytes, ASCII</summary>
CompactDiscMediaCatalogueNumber = 75
CompactDiscMediaCatalogueNumber = 75,
/// <summary>CompactDisc sector prefix (sync, header), only incorrect stored</summary>
CdSectorPrefixCorrected = 76,
/// <summary>CompactDisc sector suffix (edc, ecc p, ecc q), only incorrect stored</summary>
CdSectorSuffixCorrected = 77
}
/// <summary>List of known blocks types</summary>
@@ -243,5 +247,11 @@ namespace DiscImageChef.DiscImages
Sha256 = 3,
SpamSum = 4
}
enum CdFixFlags : uint
{
NotDumped = 0x10000000,
Correct = 0x20000000
}
}
}

View File

@@ -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,8 +288,8 @@ namespace DiscImageChef.DiscImages
break;
case BlockType.DeDuplicationTable:
// Only user data deduplication tables are used right now
if(entry.dataType != DataType.UserData) break;
if(entry.dataType == DataType.UserData)
{
DdtHeader ddtHeader = new DdtHeader();
structureBytes = new byte[Marshal.SizeOf(ddtHeader)];
imageStream.Read(structureBytes, 0, structureBytes.Length);
@@ -308,7 +322,8 @@ namespace DiscImageChef.DiscImages
compressedDdtMs.Close();
userDataDdt = new ulong[ddtHeader.entries];
for(ulong i = 0; i < ddtHeader.entries; i++)
userDataDdt[i] = BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong)));
userDataDdt[i] =
BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong)));
DateTime ddtEnd = DateTime.UtcNow;
inMemoryDdt = true;
DicConsole.DebugWriteLine("DiscImageChef format plugin",
@@ -325,6 +340,56 @@ namespace DiscImageChef.DiscImages
}
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)));
}
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:
if(sectorPrefix != null)
Array.Copy(sectorPrefix, (int)sectorAddress * 16, sector, 0, 16);
Array.Copy(data, 0, sector, 16, 2048);
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:

View File

@@ -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,8 +415,8 @@ namespace DiscImageChef.DiscImages
break;
case BlockType.DeDuplicationTable:
// Only user data deduplication tables are used right now
if(entry.dataType != DataType.UserData) break;
if(entry.dataType == DataType.UserData)
{
DdtHeader ddtHeader = new DdtHeader();
structureBytes = new byte[Marshal.SizeOf(ddtHeader)];
imageStream.Read(structureBytes, 0, structureBytes.Length);
@@ -429,9 +439,11 @@ namespace DiscImageChef.DiscImages
switch(ddtHeader.compression)
{
case CompressionType.Lzma:
DicConsole.DebugWriteLine("DiscImageChef format plugin", "Decompressing DDT...");
DicConsole.DebugWriteLine("DiscImageChef format plugin",
"Decompressing DDT...");
DateTime ddtStart = DateTime.UtcNow;
byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH];
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);
@@ -461,6 +473,73 @@ namespace DiscImageChef.DiscImages
}
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;
}
}
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,30 +1106,153 @@ 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];
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];
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);
}
break;
case XmlMediaType.BlockMedia:
switch(imageInfo.MediaType)
@@ -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)
{

View File

@@ -622,7 +622,9 @@ enum <ushort> DataType
AppleProfileTag = 72,
AppleSonyTag = 73,
PriamDataTowerTag = 74,
CompactDiscMediaCatalogueNumber = 75
CompactDiscMediaCatalogueNumber = 75,
CdSectorPrefixCorrected = 76,
CdSectorSuffixCorrected = 77
};
enum <uint> BlockType