mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 11:14:25 +00:00
359 lines
11 KiB
C#
359 lines
11 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// 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-2023 Natalia Portillo
|
|
// ECC algorithm from ECM(c) 2002-2011 Neill Corlett
|
|
// ****************************************************************************/
|
|
|
|
using System;
|
|
using Aaru.CommonTypes.Enums;
|
|
|
|
namespace Aaru.Images;
|
|
|
|
public sealed partial class AaruFormat
|
|
{
|
|
byte[] _eccBTable;
|
|
byte[] _eccFTable;
|
|
uint[] _edcTable;
|
|
bool _initedEdc;
|
|
|
|
void EccInit()
|
|
{
|
|
if(_initedEdc)
|
|
return;
|
|
|
|
_eccFTable = new byte[256];
|
|
_eccBTable = new byte[256];
|
|
_edcTable = new uint[256];
|
|
|
|
for(uint i = 0; i < 256; i++)
|
|
{
|
|
uint edc = i;
|
|
var 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;
|
|
}
|
|
|
|
_initedEdc = true;
|
|
}
|
|
|
|
bool SuffixIsCorrect(byte[] sector)
|
|
{
|
|
if(!_initedEdc)
|
|
EccInit();
|
|
|
|
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;
|
|
|
|
bool correctEccP = CheckEcc(sector, sector, 86, 24, 2, 86, sector, 0xC, 0x10, 0x81C);
|
|
|
|
if(!correctEccP)
|
|
return false;
|
|
|
|
bool correctEccQ = CheckEcc(sector, sector, 52, 43, 86, 88, sector, 0xC, 0x10, 0x81C + 0xAC);
|
|
|
|
if(!correctEccQ)
|
|
return false;
|
|
|
|
var storedEdc = BitConverter.ToUInt32(sector, 0x810);
|
|
uint edc = 0;
|
|
var size = 0x810;
|
|
var pos = 0;
|
|
|
|
for(; size > 0; size--)
|
|
edc = edc >> 8 ^ _edcTable[(edc ^ sector[pos++]) & 0xFF];
|
|
|
|
uint calculatedEdc = edc;
|
|
|
|
return calculatedEdc == storedEdc;
|
|
}
|
|
|
|
bool SuffixIsCorrectMode2(byte[] sector)
|
|
{
|
|
if(!_initedEdc)
|
|
EccInit();
|
|
|
|
var zeroAddress = new byte[4];
|
|
|
|
bool correctEccP = CheckEcc(zeroAddress, sector, 86, 24, 2, 86, sector, 0, 0x10, 0x81C);
|
|
|
|
if(!correctEccP)
|
|
return false;
|
|
|
|
bool correctEccQ = CheckEcc(zeroAddress, sector, 52, 43, 86, 88, sector, 0, 0x10, 0x81C + 0xAC);
|
|
|
|
if(!correctEccQ)
|
|
return false;
|
|
|
|
var storedEdc = BitConverter.ToUInt32(sector, 0x818);
|
|
uint edc = 0;
|
|
var size = 0x808;
|
|
var pos = 0x10;
|
|
|
|
for(; size > 0; size--)
|
|
edc = edc >> 8 ^ _edcTable[(edc ^ sector[pos++]) & 0xFF];
|
|
|
|
uint calculatedEdc = edc;
|
|
|
|
return calculatedEdc == storedEdc;
|
|
}
|
|
|
|
bool CheckEcc(byte[] address, byte[] data, uint majorCount, uint minorCount, uint majorMult, uint minorInc,
|
|
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];
|
|
|
|
if(ecc[major + eccOffset] != eccA || ecc[major + majorCount + eccOffset] != (eccA ^ eccB))
|
|
return false;
|
|
}
|
|
|
|
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) =>
|
|
((byte)((pos + 150) / 75 / 60), (byte)((pos + 150) / 75 % 60), (byte)((pos + 150) % 75));
|
|
|
|
static 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;
|
|
|
|
if(!_initedEdc)
|
|
EccInit();
|
|
|
|
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;
|
|
}
|
|
|
|
var 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)
|
|
{
|
|
if(!_initedEdc)
|
|
EccInit();
|
|
|
|
int pos = srcOffset;
|
|
|
|
for(; size > 0; size--)
|
|
edc = edc >> 8 ^ _edcTable[(edc ^ src[pos++]) & 0xFF];
|
|
|
|
return edc;
|
|
}
|
|
} |