336 lines
11 KiB
C#
336 lines
11 KiB
C#
//Copyright (c) 2012 BizHawk team
|
|
|
|
//Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
//this software and associated documentation files (the "Software"), to deal in
|
|
//the Software without restriction, including without limitation the rights to
|
|
//use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
//of the Software, and to permit persons to whom the Software is furnished to do
|
|
//so, subject to the following conditions:
|
|
|
|
//The above copyright notice and this permission notice shall be included in all
|
|
//copies or substantial portions of the Software.
|
|
|
|
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
//SOFTWARE.
|
|
|
|
//CD-ROM ECC/EDC related algorithms
|
|
|
|
//todo - ecm sometimes sets the sector address to 0 before computing the ECC. i cant find any documentation to support this.
|
|
//seems to only take effect for cd-xa (mode 2, form 1). need to ask about this or test further on a cd-xa test disc
|
|
|
|
//regarding ECC:
|
|
//it turns out mame's cdrom.c uses pretty much this exact thing, naming the tables mul2tab->ecclow and div3tab->ecchigh
|
|
//Corlett's ECM uses our same fundamental approach as well.
|
|
//I can't figure out what winUAE is doing.
|
|
|
|
using BizHawk.Common;
|
|
|
|
namespace BizHawk.Emulation.DiscSystem
|
|
{
|
|
static class ECM
|
|
{
|
|
//EDC (crc) acceleration table
|
|
static readonly uint[] edc_table = new uint[256];
|
|
|
|
//math acceleration tables over GF(2^8) with yellowbook specified primitive polynomial 0x11D
|
|
static readonly byte[] mul2tab = new byte[256];
|
|
static readonly byte[] div3tab = new byte[256];
|
|
|
|
static ECM()
|
|
{
|
|
Prep_EDC();
|
|
Prep_ECC();
|
|
}
|
|
|
|
/// <summary>
|
|
/// calculate EDC crc tables
|
|
/// </summary>
|
|
static void Prep_EDC()
|
|
{
|
|
//14.3 of yellowbook specifies EDC crc as P(x) = (x^16 + x^15 + x^2 + 1) . (x^16 + x^2 + x + 1)
|
|
//confirmation at http://cdmagediscussion.yuku.com/topic/742/EDC-calculation
|
|
//int Pa = 0x18005;
|
|
//int Pb = 0x10007;
|
|
//long Px = 0;
|
|
//for (int i = 0; i <= 16; i++)
|
|
// for (int j = 0; j <= 16; j++)
|
|
// {
|
|
// //multiply Pa[i] * Pb[j]
|
|
// int bit = (Pa >> i) & (Pb >> j) & 1;
|
|
// //xor into result, achieving modulo-2 thereby
|
|
// Px ^= (long)bit << (i + j);
|
|
// }
|
|
//uint edc_poly = (uint)Px;
|
|
const uint edc_poly = 0x8001801B;
|
|
|
|
//generate the CRC table
|
|
uint reverse_edc_poly = BitReverse.Reverse32(edc_poly);
|
|
for (uint i = 0; i < 256; ++i)
|
|
{
|
|
uint crc = i;
|
|
for (int j = 8; j > 0; --j)
|
|
{
|
|
if ((crc & 1) == 1)
|
|
crc = ((crc >> 1) ^ reverse_edc_poly);
|
|
else
|
|
crc >>= 1;
|
|
}
|
|
edc_table[i] = crc;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// calculate math lookup tables for ECC calculations.
|
|
/// </summary>
|
|
static void Prep_ECC()
|
|
{
|
|
//create a table implementing f(i) = i*2
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
int n = i * 2;
|
|
int b = n & 0xFF;
|
|
if (n > 0xFF) b ^= 0x1D; //primitive polynomial x^8 + x^4 + x^3 + x^2 + 1 -> 0x11D
|
|
mul2tab[i] = (byte)b;
|
|
}
|
|
|
|
//(here is a more straightforward way of doing it, just to check)
|
|
//byte[] mul2tab_B = new byte[256];
|
|
//for (int i = 1; i < 256; i++)
|
|
// mul2tab_B[i] = FFUtil.gf_mul((byte)i, (byte)2);
|
|
|
|
////(make sure theyre the same)
|
|
//for (int i = 0; i < 256; i++)
|
|
// System.Diagnostics.Debug.Assert(mul2tab[i] == mul2tab_B[i]);
|
|
|
|
//create a table implementing f(i) = i/3
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
byte x1 = (byte)i;
|
|
byte x2 = mul2tab[i];
|
|
byte x3 = (byte)(x2 ^ x1); //2x + x = 3x
|
|
//instead of dividing 1/3 we write the table backwards since its the inverse of multiplying by 3
|
|
//this idea was taken from Corlett's techniques; I know not from whence they came.
|
|
div3tab[x3] = x1;
|
|
}
|
|
|
|
//(here is a more straightforward way of doing it, just to check)
|
|
//byte[] div3tab_B = new byte[256];
|
|
//for (int i = 0; i < 256; i++)
|
|
// div3tab_B[i] = FFUtil.gf_div((byte)i, 3);
|
|
|
|
////(make sure theyre the same)
|
|
//for (int i = 0; i < 256; i++)
|
|
// System.Diagnostics.Debug.Assert(div3tab[i] == div3tab_B[i]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates ECC parity values for the specified data
|
|
/// see annex A of yellowbook
|
|
/// </summary>
|
|
public static void CalcECC(byte[] data, int base_offset, int addr_offset, int addr_add, int todo, out byte p0, out byte p1)
|
|
{
|
|
//let's take the P parity as an example. Q parity will differ by being a (45, 43) code instead of the (25, 23) illustrated here.
|
|
//
|
|
//we're supposed to multiply [ [ 1 1 1 ] [a^25 a^24 a^23 ..] ] by [ [d0] [d1] [d2] ..] and get 0
|
|
//where a is the primitive element a=2 and d0 is data[0]
|
|
//this multiplication yields a matrix equation:
|
|
//[ [d0 + d1 + d2] [d0*a^25 + d1*a^24 + d2*a^23] ] = [ [0] [0] ]
|
|
//so now we have equations:
|
|
//(d0 + d1 + d2 ..) + p0 + p1 = 0
|
|
//(d0*a^25 + d1*a^24 + d2*a^23 ..) + p0*a + p1 = 0
|
|
//lets rename these series expressions for convenience to add_accum and pow_accum, respectively. so we'd have:
|
|
//add_accum + p0 + p1 = 0
|
|
//(pow_accum + p0) * 2 + p1 = 0
|
|
//
|
|
//we can get pow_accum in iterations of multiplying by 2 and adding..
|
|
//
|
|
//I.
|
|
//pow_accum = d0 [add]
|
|
//pow_accum = d0*2 [mul]
|
|
//II.
|
|
//pow_accum = d0*2 + d1 [add]
|
|
//pow_accum = (d0*2 + d1)*2 [mul]
|
|
//
|
|
//.. and we can get add_accum in the obvious way in iterations by adding.
|
|
//
|
|
//now all that remains is to solve the equations:
|
|
//(pow_accum + p0) * 2 + p1 = 0
|
|
//(pow_accum + p0) * 2 + add_accum + p0 = 0
|
|
//2*pow_accum + 2*p0 + add_accum + p0 = 0
|
|
//3*p0 + p0 = -2*pow_accum - add_accum
|
|
//3*p0 = (2*pow_accum) ^ add_accum
|
|
//p0 = ((2*pow_accum) ^ add_accum) / 3
|
|
//..and..
|
|
//add_accum + p0 + p1 = 0
|
|
//p1 = - p0 - add_accum
|
|
//p1 = p0 ^ add_accum
|
|
|
|
byte pow_accum = 0;
|
|
byte add_accum = 0;
|
|
for (int i = 0; i < todo; i++)
|
|
{
|
|
addr_offset %= (1118 * 2); //modulo addressing is irrelevant for P-parity calculation but comes into play for Q-parity
|
|
byte d = data[base_offset + addr_offset];
|
|
addr_offset += addr_add;
|
|
add_accum ^= d;
|
|
pow_accum ^= d;
|
|
pow_accum = mul2tab[pow_accum];
|
|
}
|
|
|
|
p0 = div3tab[mul2tab[pow_accum] ^ add_accum];
|
|
p1 = (byte)(p0 ^ add_accum);
|
|
}
|
|
|
|
/// <summary>
|
|
/// handy for stashing the EDC somewhere with little endian
|
|
/// </summary>
|
|
public static void PokeUint(byte[] data, int offset, uint value)
|
|
{
|
|
data[offset + 0] = (byte)((value >> 0) & 0xFF);
|
|
data[offset + 1] = (byte)((value >> 8) & 0xFF);
|
|
data[offset + 2] = (byte)((value >> 16) & 0xFF);
|
|
data[offset + 3] = (byte)((value >> 24) & 0xFF);
|
|
}
|
|
|
|
/// <summary>
|
|
/// calculates EDC checksum for the range of data provided
|
|
/// see section 14.3 of yellowbook
|
|
/// </summary>
|
|
public static uint EDC_Calc(byte[] data, int offset, int length)
|
|
{
|
|
uint crc = 0;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
byte b = data[offset + i];
|
|
int entry = ((int)crc ^ b) & 0xFF;
|
|
crc = edc_table[entry] ^ (crc >> 8);
|
|
}
|
|
|
|
return crc;
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// returns the address from a sector. useful for saving it before zeroing it for ECC calculations
|
|
/// </summary>
|
|
static uint GetSectorAddress(byte[] sector, int sector_offset)
|
|
{
|
|
return (uint)(
|
|
(sector[sector_offset + 12 + 0] << 0)
|
|
| (sector[sector_offset + 12 + 1] << 8)
|
|
| (sector[sector_offset + 12 + 2] << 16)
|
|
| (sector[sector_offset + 12 + 3] << 24));
|
|
}
|
|
|
|
/// <summary>
|
|
/// sets the address for a sector. useful for restoring it after zeroing it for ECC calculations
|
|
/// </summary>
|
|
static void SetSectorAddress(byte[] sector, int sector_offset, uint address)
|
|
{
|
|
sector[sector_offset + 12 + 0] = (byte)((address >> 0) & 0xFF);
|
|
sector[sector_offset + 12 + 1] = (byte)((address >> 8) & 0xFF);
|
|
sector[sector_offset + 12 + 2] = (byte)((address >> 16) & 0xFF);
|
|
sector[sector_offset + 12 + 3] = (byte)((address >> 24) & 0xFF);
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// populates a sector with valid ECC information.
|
|
/// it is safe to supply the same array for sector and dest.
|
|
/// </summary>
|
|
public static void ECC_Populate(byte[] src, int src_offset, byte[] dest, int dest_offset, bool zeroSectorAddress)
|
|
{
|
|
//save the old sector address, so we can restore it later. SOMETIMES ECC is supposed to be calculated without it? see TODO
|
|
uint address = GetSectorAddress(src, src_offset);
|
|
if (zeroSectorAddress) SetSectorAddress(src, src_offset, 0);
|
|
|
|
//all further work takes place relative to offset 12 in the sector
|
|
src_offset += 12;
|
|
dest_offset += 12;
|
|
|
|
//calculate P parity for 86 columns (twice 43 word-columns)
|
|
byte parity0, parity1;
|
|
for (int col = 0; col < 86; col++)
|
|
{
|
|
int offset = col;
|
|
CalcECC(src, src_offset, offset, 86, 24, out parity0, out parity1);
|
|
//store the parities in the sector; theyre read for the Q parity calculations
|
|
dest[dest_offset + 1032 * 2 + col] = parity0;
|
|
dest[dest_offset + 1032 * 2 + col + 43 * 2] = parity1;
|
|
}
|
|
|
|
//calculate Q parity for 52 diagonals (twice 26 word-diagonals)
|
|
//modulo addressing is taken care of in CalcECC
|
|
for (int d = 0; d < 26; d++)
|
|
{
|
|
for (int w = 0; w < 2; w++)
|
|
{
|
|
int offset = d * 86 + w;
|
|
CalcECC(src, src_offset, offset, 88, 43, out parity0, out parity1);
|
|
//store the parities in the sector; thats where theyve got to go anyway
|
|
dest[dest_offset + 1118 * 2 + d * 2 + w] = parity0;
|
|
dest[dest_offset + 1118 * 2 + d * 2 + w + 26 * 2] = parity1;
|
|
}
|
|
}
|
|
|
|
//unadjust the offset back to an absolute sector address, which SetSectorAddress expects
|
|
src_offset -= 12;
|
|
SetSectorAddress(src, src_offset, address);
|
|
}
|
|
|
|
|
|
///// <summary>
|
|
///// Finite Field math helpers. Adapted from: http://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders
|
|
///// Only used by alternative implementations of ECM techniques
|
|
///// </summary>
|
|
//static class FFUtil
|
|
//{
|
|
// public static byte gf_div(byte x, byte y)
|
|
// {
|
|
// if (y == 0)
|
|
// return 0; //? error ?
|
|
// if (x == 0)
|
|
// return 0;
|
|
// int q = gf_log[x] + 255 - gf_log[y];
|
|
// return gf_exp[q];
|
|
// }
|
|
|
|
// public static byte gf_mul(byte x, byte y)
|
|
// {
|
|
// if (x == 0 || y == 0)
|
|
// return 0;
|
|
// return gf_exp[gf_log[x] + gf_log[y]];
|
|
// }
|
|
|
|
// static byte[] gf_exp = new byte[512];
|
|
// static byte[] gf_log = new byte[256];
|
|
// static FFUtil()
|
|
// {
|
|
// for (int i = 0; i < 512; i++) gf_exp[i] = 1;
|
|
// for (int i = 0; i < 256; i++) gf_log[i] = 0;
|
|
// int x = 1;
|
|
// for (int i = 1; i < 255; i++)
|
|
// {
|
|
// x <<= 1;
|
|
// if ((x & 0x100) != 0)
|
|
// x ^= 0x11d; //yellowbook specified primitive polynomial
|
|
// gf_exp[i] = (byte)x;
|
|
// gf_log[x] = (byte)i;
|
|
// }
|
|
// for (int i = 255; i < 512; i++)
|
|
// gf_exp[i] = gf_exp[(byte)(i - 255)];
|
|
// }
|
|
|
|
//} //static class FFUtil
|
|
|
|
} //static class ECM
|
|
|
|
}
|