diff --git a/DiscImageChef.DiscImages/DiscImageChef/CdEcc.cs b/DiscImageChef.DiscImages/DiscImageChef/CdEcc.cs index 6523da92f..f1aeaec39 100644 --- a/DiscImageChef.DiscImages/DiscImageChef/CdEcc.cs +++ b/DiscImageChef.DiscImages/DiscImageChef/CdEcc.cs @@ -32,6 +32,7 @@ // ****************************************************************************/ using System; +using DiscImageChef.CommonTypes.Enums; namespace DiscImageChef.DiscImages { @@ -58,11 +59,11 @@ namespace DiscImageChef.DiscImages } } - bool SuffixIsCorrect(byte[] channel) + bool SuffixIsCorrect(byte[] sector) { - 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; + if(sector[0x814] != 0x00 || // reserved (8 bytes) + sector[0x815] != 0x00 || sector[0x816] != 0x00 || sector[0x817] != 0x00 || sector[0x818] != 0x00 || + sector[0x819] != 0x00 || sector[0x81A] != 0x00 || sector[0x81B] != 0x00) return false; byte[] address = new byte[4]; byte[] data = new byte[2060]; @@ -70,11 +71,11 @@ namespace DiscImageChef.DiscImages 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); + Array.Copy(sector, 0x0C, address, 0, 4); + Array.Copy(sector, 0x10, data, 0, 2060); + Array.Copy(sector, 0x10, data2, 0, 2232); + Array.Copy(sector, 0x81C, eccP, 0, 172); + Array.Copy(sector, 0x8C8, eccQ, 0, 104); bool correctEccP = CheckEcc(ref address, ref data, 86, 24, 2, 86, ref eccP); if(!correctEccP) return false; @@ -82,11 +83,11 @@ namespace DiscImageChef.DiscImages bool correctEccQ = CheckEcc(ref address, ref data2, 52, 43, 86, 88, ref eccQ); if(!correctEccQ) return false; - uint storedEdc = BitConverter.ToUInt32(channel, 0x810); + uint storedEdc = BitConverter.ToUInt32(sector, 0x810); uint edc = 0; int size = 0x810; int pos = 0; - for(; size > 0; size--) edc = (edc >> 8) ^ edcTable[(edc ^ channel[pos++]) & 0xFF]; + for(; size > 0; size--) edc = (edc >> 8) ^ edcTable[(edc ^ sector[pos++]) & 0xFF]; uint calculatedEdc = edc; return calculatedEdc == storedEdc; @@ -119,5 +120,171 @@ namespace DiscImageChef.DiscImages return true; } + + void WriteEcc(byte[] address, byte[] data, uint majorCount, uint minorCount, uint majorMult, + uint minorInc, + ref byte[] ecc, int addressOffset, int dataOffset, int eccOffset) + { + 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 + addressOffset] : data[idx + dataOffset - 4]; + idx += minorInc; + if(idx >= size) idx -= size; + eccA ^= temp; + eccB ^= temp; + eccA = eccFTable[eccA]; + } + + eccA = eccBTable[eccFTable[eccA] ^ eccB]; + ecc[major + eccOffset] = eccA; + ecc[major + majorCount + eccOffset] = (byte)(eccA ^ eccB); + } + } + + void EccWriteSector(byte[] address, byte[] data, ref byte[] ecc, int addressOffset, int dataOffset, + int eccOffset) + { + WriteEcc(address, data, 86, 24, 2, 86, ref ecc, addressOffset, dataOffset, eccOffset); // P + WriteEcc(address, data, 52, 43, 86, 88, ref ecc, addressOffset, dataOffset, eccOffset + 0xAC); // Q + } + + static (byte minute, byte second, byte frame) LbaToMsf(long pos) + { + return ((byte)((pos + 150) / 75 / 60), (byte)((pos + 150) / 75 % 60), (byte)((pos + 150) % 75)); + } + + void ReconstructPrefix(ref byte[] sector, // must point to a full 2352-byte sector + TrackType type, long lba) + { + // + // Sync + // + sector[0x000] = 0x00; + sector[0x001] = 0xFF; + sector[0x002] = 0xFF; + sector[0x003] = 0xFF; + sector[0x004] = 0xFF; + sector[0x005] = 0xFF; + sector[0x006] = 0xFF; + sector[0x007] = 0xFF; + sector[0x008] = 0xFF; + sector[0x009] = 0xFF; + sector[0x00A] = 0xFF; + sector[0x00B] = 0x00; + + (byte minute, byte second, byte frame) msf = LbaToMsf(lba); + + sector[0x00C] = (byte)(((msf.minute / 10) << 4) + msf.minute % 10); + sector[0x00D] = (byte)(((msf.second / 10) << 4) + msf.second % 10); + sector[0x00E] = (byte)(((msf.frame / 10) << 4) + msf.frame % 10); + + switch(type) + { + case TrackType.CdMode1: + // + // Mode + // + sector[0x00F] = 0x01; + break; + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + case TrackType.CdMode2Formless: + // + // Mode + // + sector[0x00F] = 0x02; + // + // Flags + // + sector[0x010] = sector[0x014]; + sector[0x011] = sector[0x015]; + sector[0x012] = sector[0x016]; + sector[0x013] = sector[0x017]; + break; + default: return; + } + } + + void ReconstructEcc(ref byte[] sector, // must point to a full 2352-byte sector + TrackType type) + { + byte[] computedEdc; + + switch(type) + { + // + // Compute EDC + // + case TrackType.CdMode1: + computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x810)); + sector[0x810] = computedEdc[0]; + sector[0x811] = computedEdc[1]; + sector[0x812] = computedEdc[2]; + sector[0x813] = computedEdc[3]; + break; + case TrackType.CdMode2Form1: + computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x808, 0x10)); + sector[0x818] = computedEdc[0]; + sector[0x819] = computedEdc[1]; + sector[0x81A] = computedEdc[2]; + sector[0x81B] = computedEdc[3]; + break; + case TrackType.CdMode2Form2: + computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x91C, 0x10)); + sector[0x92C] = computedEdc[0]; + sector[0x92D] = computedEdc[1]; + sector[0x92E] = computedEdc[2]; + sector[0x92F] = computedEdc[3]; + break; + default: return; + } + + byte[] zeroaddress = new byte[4]; + + switch(type) + { + // + // Compute ECC + // + case TrackType.CdMode1: + // + // Reserved + // + sector[0x814] = 0x00; + sector[0x815] = 0x00; + sector[0x816] = 0x00; + sector[0x817] = 0x00; + sector[0x818] = 0x00; + sector[0x819] = 0x00; + sector[0x81A] = 0x00; + sector[0x81B] = 0x00; + EccWriteSector(sector, sector, ref sector, 0xC, 0x10, 0x81C); + break; + case TrackType.CdMode2Form1: + EccWriteSector(zeroaddress, sector, ref sector, 0, 0x10, 0x81C); + break; + default: return; + } + + // + // Done + // + } + + uint ComputeEdc(uint edc, byte[] src, int size, int srcOffset = 0) + { + int pos = srcOffset; + for(; size > 0; size--) edc = (edc >> 8) ^ edcTable[(edc ^ src[pos++]) & 0xFF]; + + return edc; + } } } \ No newline at end of file diff --git a/DiscImageChef.DiscImages/DiscImageChef/Read.cs b/DiscImageChef.DiscImages/DiscImageChef/Read.cs index 9982200a2..badf43f21 100644 --- a/DiscImageChef.DiscImages/DiscImageChef/Read.cs +++ b/DiscImageChef.DiscImages/DiscImageChef/Read.cs @@ -388,6 +388,9 @@ namespace DiscImageChef.DiscImages for(ulong i = 0; i < ddtHeader.entries; i++) cdDdt[i] = BitConverter.ToUInt32(decompressedDdt, (int)(i * sizeof(uint))); + + if(entry.dataType == DataType.CdSectorPrefixCorrected) sectorPrefixDdt = cdDdt; + else if(entry.dataType == DataType.CdSectorSuffixCorrected) sectorSuffixDdt = cdDdt; } break; @@ -903,7 +906,10 @@ namespace DiscImageChef.DiscImages byte[] sector = ReadSector(tracks[i].TrackStartSector); tracks[i].TrackBytesPerSector = sector.Length; tracks[i].TrackRawBytesPerSector = - sectorPrefix != null && sectorSuffix != null ? 2352 : sector.Length; + sectorPrefix != null && sectorSuffix != null || + sectorPrefixDdt != null && sectorSuffixDdt != null + ? 2352 + : sector.Length; if(sectorSubchannel == null) continue; @@ -923,6 +929,8 @@ namespace DiscImageChef.DiscImages SetMetadataFromTags(); + if(sectorSuffixDdt != null) EccInit(); + return true; } @@ -1310,23 +1318,70 @@ namespace DiscImageChef.DiscImages case TrackType.Audio: case TrackType.Data: return data; case TrackType.CdMode1: + Array.Copy(data, 0, sector, 16, 2048); + if(sectorPrefix != null) Array.Copy(sectorPrefix, (int)sectorAddress * 16, sector, 0, 16); - else if(sectorPrefixMs != null) throw new NotImplementedException(); + else if(sectorPrefixDdt != null) + { + if((sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (ulong)CdFixFlags.Correct) + ReconstructPrefix(ref sector, trk.TrackType, (long)sectorAddress); + else if((sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (ulong)CdFixFlags.NotDumped) + { + // Do nothing + } + else + { + sectorPrefixMs.Position = + ((sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16; + sectorPrefixMs.Read(sector, 0, 16); + } + } 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 if(sectorSuffixDdt != null) + { + if((sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == (ulong)CdFixFlags.Correct) + ReconstructEcc(ref sector, trk.TrackType); + else if((sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == (ulong)CdFixFlags.NotDumped) + { + // Do nothing + } + else + { + sectorSuffixMs.Position = + ((sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288; + sectorSuffixMs.Read(sector, 0, 288); + } + } else throw new InvalidProgramException("Should not have arrived here"); - Array.Copy(data, 0, sector, 16, 2048); return sector; case TrackType.CdMode2Formless: case TrackType.CdMode2Form1: case TrackType.CdMode2Form2: - Array.Copy(sectorPrefix, (int)sectorAddress * 16, sector, 0, 16); - Array.Copy(data, 0, sector, 16, 2336); + Array.Copy(data, 0, sector, 16, 2336); + if(sectorPrefix != null) + Array.Copy(sectorPrefix, (int)sectorAddress * 16, sector, 0, 16); + else if(sectorPrefixMs != null) + { + if((sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (ulong)CdFixFlags.Correct) + ReconstructPrefix(ref sector, trk.TrackType, (long)sectorAddress); + else if((sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (ulong)CdFixFlags.NotDumped) + { + // Do nothing + } + else + { + sectorPrefixMs.Position = + ((sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16; + sectorPrefixMs.Read(sector, 0, 16); + } + } + else throw new InvalidProgramException("Should not have arrived here"); + return sector; } @@ -1385,38 +1440,64 @@ namespace DiscImageChef.DiscImages case TrackType.Data: return ReadSectors(sectorAddress, length); // Join prefix (sync, header) with user data with suffix (edc, ecc p, ecc q) case TrackType.CdMode1: - if(sectorPrefix == null || sectorSuffix == null) return ReadSectors(sectorAddress, length); - - sectors = new byte[2352 * length]; - data = ReadSectors(sectorAddress, length); - for(uint i = 0; i < length; i++) + if(sectorPrefix != null && sectorSuffix != null) { - Array.Copy(sectorPrefix, (int)((sectorAddress + i) * 16), sectors, (int)(i * 2352), - 16); - Array.Copy(data, (int)(i * 2048), sectors, - (int)(i * 2352) + 16, 2048); - Array.Copy(sectorSuffix, (int)((sectorAddress + i) * 288), sectors, - (int)(i * 2352) + 2064, 288); - } + sectors = new byte[2352 * length]; + data = ReadSectors(sectorAddress, length); + for(uint i = 0; i < length; i++) + { + Array.Copy(sectorPrefix, (int)((sectorAddress + i) * 16), sectors, (int)(i * 2352), + 16); + Array.Copy(data, (int)(i * 2048), sectors, (int)(i * 2352) + 16, 2048); + Array.Copy(sectorSuffix, (int)((sectorAddress + i) * 288), sectors, + (int)(i * 2352) + 2064, 288); + } + + return sectors; + } + else if(sectorPrefixDdt != null && sectorSuffixDdt != null) + { + sectors = new byte[2352 * length]; + for(uint i = 0; i < length; i++) + { + byte[] temp = ReadSectorLong(sectorAddress + i); + Array.Copy(temp, 0, sectors, 2352 * i, 2352); + } + + return sectors; + } + else return ReadSectors(sectorAddress, length); - return sectors; // Join prefix (sync, header) with user data case TrackType.CdMode2Formless: case TrackType.CdMode2Form1: case TrackType.CdMode2Form2: - if(sectorPrefix == null || sectorSuffix == null) return ReadSectors(sectorAddress, length); - - sectors = new byte[2352 * length]; - data = ReadSectors(sectorAddress, length); - for(uint i = 0; i < length; i++) + if(sectorPrefix != null && sectorSuffix != null) { - Array.Copy(sectorPrefix, (int)((sectorAddress + i) * 16), sectors, (int)(i * 2352), - 16); - Array.Copy(data, (int)(i * 2336), sectors, - (int)(i * 2352) + 16, 2336); + sectors = new byte[2352 * length]; + data = ReadSectors(sectorAddress, length); + for(uint i = 0; i < length; i++) + { + Array.Copy(sectorPrefix, (int)((sectorAddress + i) * 16), sectors, (int)(i * 2352), + 16); + Array.Copy(data, (int)(i * 2336), sectors, (int)(i * 2352) + 16, 2336); + } + + return sectors; + } + else if(sectorPrefixDdt != null && sectorSuffixDdt != null) + { + sectors = new byte[2352 * length]; + for(uint i = 0; i < length; i++) + { + byte[] temp = ReadSectorLong(sectorAddress + i); + Array.Copy(temp, 0, sectors, 2352 * i, 2352); + } + + return sectors; } - return sectors; + return ReadSectors(sectorAddress, length); } break;