psx - re-add mednafen's disc error correction capability
This commit is contained in:
parent
62e25eadb0
commit
c942350beb
Binary file not shown.
|
@ -12,6 +12,11 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\cdrom\CDUtility.cpp" />
|
<ClCompile Include="..\cdrom\CDUtility.cpp" />
|
||||||
|
<ClCompile Include="..\cdrom\crc32.cpp" />
|
||||||
|
<ClCompile Include="..\cdrom\galois.cpp" />
|
||||||
|
<ClCompile Include="..\cdrom\l-ec.cpp" />
|
||||||
|
<ClCompile Include="..\cdrom\lec.cpp" />
|
||||||
|
<ClCompile Include="..\cdrom\recover-raw.cpp" />
|
||||||
<ClCompile Include="..\emuware\emuware.cpp" />
|
<ClCompile Include="..\emuware\emuware.cpp" />
|
||||||
<ClCompile Include="..\emuware\EW_state.cpp" />
|
<ClCompile Include="..\emuware\EW_state.cpp" />
|
||||||
<ClCompile Include="..\endian.cpp" />
|
<ClCompile Include="..\endian.cpp" />
|
||||||
|
@ -141,7 +146,7 @@
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>EW_EXPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;OCTOSHOCK_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WANT_LEC_CHECK;EW_EXPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;OCTOSHOCK_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>../emuware/msvc;..</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>../emuware/msvc;..</AdditionalIncludeDirectories>
|
||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
|
@ -163,7 +168,7 @@
|
||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;EW_EXPORT;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WANT_LEC_CHECK;_CRT_SECURE_NO_WARNINGS;EW_EXPORT;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>
|
||||||
</PrecompiledHeaderFile>
|
</PrecompiledHeaderFile>
|
||||||
<PrecompiledHeaderOutputFile>
|
<PrecompiledHeaderOutputFile>
|
||||||
|
|
|
@ -115,6 +115,21 @@
|
||||||
<Filter>cdrom</Filter>
|
<Filter>cdrom</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\tests.cpp" />
|
<ClCompile Include="..\tests.cpp" />
|
||||||
|
<ClCompile Include="..\cdrom\lec.cpp">
|
||||||
|
<Filter>cdrom</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\cdrom\l-ec.cpp">
|
||||||
|
<Filter>cdrom</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\cdrom\recover-raw.cpp">
|
||||||
|
<Filter>cdrom</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\cdrom\galois.cpp">
|
||||||
|
<Filter>cdrom</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\cdrom\crc32.cpp">
|
||||||
|
<Filter>cdrom</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\psx\cdc.h">
|
<ClInclude Include="..\psx\cdc.h">
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "dvdisaster.h"
|
||||||
#include "octoshock.h"
|
#include "octoshock.h"
|
||||||
#include "CDUtility.h"
|
#include "CDUtility.h"
|
||||||
|
|
||||||
|
@ -29,10 +30,6 @@
|
||||||
namespace CDUtility
|
namespace CDUtility
|
||||||
{
|
{
|
||||||
|
|
||||||
void CDUtility_Init()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup table for crc calculation
|
// lookup table for crc calculation
|
||||||
static uint16 subq_crctab[256] =
|
static uint16 subq_crctab[256] =
|
||||||
{
|
{
|
||||||
|
@ -67,6 +64,54 @@ static uint16 subq_crctab[256] =
|
||||||
0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static uint8 scramble_table[2352 - 12];
|
||||||
|
|
||||||
|
static bool CDUtility_Inited = false;
|
||||||
|
|
||||||
|
static void InitScrambleTable(void)
|
||||||
|
{
|
||||||
|
unsigned cv = 1;
|
||||||
|
|
||||||
|
for (unsigned i = 12; i < 2352; i++)
|
||||||
|
{
|
||||||
|
unsigned char z = 0;
|
||||||
|
|
||||||
|
for (int b = 0; b < 8; b++)
|
||||||
|
{
|
||||||
|
z |= (cv & 1) << b;
|
||||||
|
|
||||||
|
int feedback = ((cv >> 1) & 1) ^ (cv & 1);
|
||||||
|
cv = (cv >> 1) | (feedback << 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
scramble_table[i - 12] = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
//for(int i = 0; i < 2352 - 12; i++)
|
||||||
|
// printf("0x%02x, ", scramble_table[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDUtility_Init(void)
|
||||||
|
{
|
||||||
|
if (!CDUtility_Inited)
|
||||||
|
{
|
||||||
|
#ifdef WANT_LEC_CHECK
|
||||||
|
Init_LEC_Correct();
|
||||||
|
InitScrambleTable();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CDUtility_Inited = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa)
|
||||||
|
{
|
||||||
|
CDUtility_Init();
|
||||||
|
|
||||||
|
return !!ValidateRawSector(sector_data, xa);
|
||||||
|
}
|
||||||
|
|
||||||
bool subq_check_checksum(const uint8 *SubQBuf)
|
bool subq_check_checksum(const uint8 *SubQBuf)
|
||||||
{
|
{
|
||||||
|
|
|
@ -152,6 +152,12 @@ namespace CDUtility
|
||||||
return( ((num / 10) << 4) + (num % 10) );
|
return( ((num / 10) << 4) + (num % 10) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check EDC and L-EC data of a mode 1 or mode 2 form 1 sector, and correct bit errors if any exist.
|
||||||
|
// Returns "true" if errors weren't detected, or they were corrected succesfully.
|
||||||
|
// Returns "false" if errors couldn't be corrected.
|
||||||
|
// sector_data should contain 2352 bytes of raw sector data.
|
||||||
|
bool edc_lec_check_and_correct(uint8 *sector_data, bool xa);
|
||||||
|
|
||||||
// Returns false on checksum mismatch, true on match.
|
// Returns false on checksum mismatch, true on match.
|
||||||
bool subq_check_checksum(const uint8 *subq_buf);
|
bool subq_check_checksum(const uint8 *subq_buf);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
/* dvdisaster: Additional error correction for optical media.
|
||||||
|
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||||
|
* Project home page: http://www.dvdisaster.com
|
||||||
|
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||||
|
*
|
||||||
|
* CRC32 code based upon public domain code by Ross Williams (see notes below)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||||
|
* or direct your browser at http://www.gnu.org.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dvdisaster.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** EDC checksum used in CDROM sectors
|
||||||
|
***/
|
||||||
|
|
||||||
|
/*****************************************************************/
|
||||||
|
/* */
|
||||||
|
/* CRC LOOKUP TABLE */
|
||||||
|
/* ================ */
|
||||||
|
/* The following CRC lookup table was generated automagically */
|
||||||
|
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
|
||||||
|
/* Program V1.0 using the following model parameters: */
|
||||||
|
/* */
|
||||||
|
/* Width : 4 bytes. */
|
||||||
|
/* Poly : 0x8001801BL */
|
||||||
|
/* Reverse : TRUE. */
|
||||||
|
/* */
|
||||||
|
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
|
||||||
|
/* see the document titled "A Painless Guide to CRC Error */
|
||||||
|
/* Detection Algorithms" by Ross Williams */
|
||||||
|
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
|
||||||
|
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
|
||||||
|
/* */
|
||||||
|
/*****************************************************************/
|
||||||
|
|
||||||
|
unsigned long edctable[256] =
|
||||||
|
{
|
||||||
|
0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L,
|
||||||
|
0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L,
|
||||||
|
0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L,
|
||||||
|
0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L,
|
||||||
|
0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L,
|
||||||
|
0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L,
|
||||||
|
0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L,
|
||||||
|
0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L,
|
||||||
|
0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L,
|
||||||
|
0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L,
|
||||||
|
0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L,
|
||||||
|
0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L,
|
||||||
|
0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L,
|
||||||
|
0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L,
|
||||||
|
0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L,
|
||||||
|
0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L,
|
||||||
|
0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L,
|
||||||
|
0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L,
|
||||||
|
0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L,
|
||||||
|
0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L,
|
||||||
|
0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L,
|
||||||
|
0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L,
|
||||||
|
0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L,
|
||||||
|
0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L,
|
||||||
|
0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L,
|
||||||
|
0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L,
|
||||||
|
0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L,
|
||||||
|
0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L,
|
||||||
|
0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L,
|
||||||
|
0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L,
|
||||||
|
0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L,
|
||||||
|
0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L,
|
||||||
|
0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L,
|
||||||
|
0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L,
|
||||||
|
0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L,
|
||||||
|
0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L,
|
||||||
|
0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L,
|
||||||
|
0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L,
|
||||||
|
0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L,
|
||||||
|
0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L,
|
||||||
|
0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L,
|
||||||
|
0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L,
|
||||||
|
0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L,
|
||||||
|
0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L,
|
||||||
|
0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L,
|
||||||
|
0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L,
|
||||||
|
0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L,
|
||||||
|
0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L,
|
||||||
|
0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L,
|
||||||
|
0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L,
|
||||||
|
0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L,
|
||||||
|
0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L,
|
||||||
|
0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L,
|
||||||
|
0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L,
|
||||||
|
0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L,
|
||||||
|
0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L,
|
||||||
|
0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L,
|
||||||
|
0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L,
|
||||||
|
0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L,
|
||||||
|
0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L,
|
||||||
|
0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L,
|
||||||
|
0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L,
|
||||||
|
0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L,
|
||||||
|
0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CDROM EDC calculation
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint32 EDCCrc32(const unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
uint32 crc = 0;
|
||||||
|
|
||||||
|
while(len--)
|
||||||
|
crc = edctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
/* dvdisaster: Additional error correction for optical media.
|
||||||
|
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||||
|
* Project home page: http://www.dvdisaster.com
|
||||||
|
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||||
|
* or direct your browser at http://www.gnu.org.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DVDISASTER_H
|
||||||
|
#define DVDISASTER_H
|
||||||
|
|
||||||
|
/* "Dare to be gorgeous and unique.
|
||||||
|
* But don't ever be cryptic or otherwise unfathomable.
|
||||||
|
* Make it unforgettably great."
|
||||||
|
*
|
||||||
|
* From "A Final Note on Style",
|
||||||
|
* Amiga Intuition Reference Manual, 1986, p. 231
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** I'm too lazy to mess with #include dependencies.
|
||||||
|
*** Everything #includeable is rolled up herein...
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "octoshock.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** dvdisaster.c
|
||||||
|
***/
|
||||||
|
|
||||||
|
void PrepareDeadSector(void);
|
||||||
|
|
||||||
|
void CreateEcc(void);
|
||||||
|
void FixEcc(void);
|
||||||
|
void Verify(void);
|
||||||
|
|
||||||
|
uint32 EDCCrc32(const unsigned char*, int);
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** galois.c
|
||||||
|
***
|
||||||
|
* This is currently the hardcoded GF(2**8).
|
||||||
|
* int32 gives abundant space for the GF.
|
||||||
|
* Squeezing it down to uint8 won't probably gain much,
|
||||||
|
* so we implement this defensively here.
|
||||||
|
*
|
||||||
|
* Note that some performance critical stuff needs to
|
||||||
|
* be #included from galois-inlines.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Galois field parameters for 8bit symbol Reed-Solomon code */
|
||||||
|
|
||||||
|
#define GF_SYMBOLSIZE 8
|
||||||
|
#define GF_FIELDSIZE (1<<GF_SYMBOLSIZE)
|
||||||
|
#define GF_FIELDMAX (GF_FIELDSIZE-1)
|
||||||
|
#define GF_ALPHA0 GF_FIELDMAX
|
||||||
|
|
||||||
|
/* Lookup tables for Galois field arithmetic */
|
||||||
|
|
||||||
|
typedef struct _GaloisTables
|
||||||
|
{ int32 gfGenerator; /* GF generator polynomial */
|
||||||
|
int32 *indexOf; /* log */
|
||||||
|
int32 *alphaTo; /* inverse log */
|
||||||
|
int32 *encAlphaTo; /* inverse log optimized for encoder */
|
||||||
|
} GaloisTables;
|
||||||
|
|
||||||
|
/* Lookup and working tables for the ReedSolomon codecs */
|
||||||
|
|
||||||
|
typedef struct _ReedSolomonTables
|
||||||
|
{ GaloisTables *gfTables;/* from above */
|
||||||
|
int32 *gpoly; /* RS code generator polynomial */
|
||||||
|
int32 fcr; /* first consecutive root of RS generator polynomial */
|
||||||
|
int32 primElem; /* primitive field element */
|
||||||
|
int32 nroots; /* degree of RS generator polynomial */
|
||||||
|
int32 ndata; /* data bytes per ecc block */
|
||||||
|
} ReedSolomonTables;
|
||||||
|
|
||||||
|
GaloisTables* CreateGaloisTables(int32);
|
||||||
|
void FreeGaloisTables(GaloisTables*);
|
||||||
|
|
||||||
|
ReedSolomonTables *CreateReedSolomonTables(GaloisTables*, int32, int32, int);
|
||||||
|
void FreeReedSolomonTables(ReedSolomonTables*);
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** l-ec.c
|
||||||
|
***/
|
||||||
|
|
||||||
|
#define N_P_VECTORS 86 /* 43 16bit p vectors */
|
||||||
|
#define P_VECTOR_SIZE 26 /* using RS(26,24) ECC */
|
||||||
|
|
||||||
|
#define N_Q_VECTORS 52 /* 26 16bit q vectors */
|
||||||
|
#define Q_VECTOR_SIZE 45 /* using RS(45,43) ECC */
|
||||||
|
|
||||||
|
#define P_PADDING 229 /* padding values for */
|
||||||
|
#define Q_PADDING 210 /* shortened RS code */
|
||||||
|
|
||||||
|
int PToByteIndex(int, int);
|
||||||
|
int QToByteIndex(int, int);
|
||||||
|
void ByteIndexToP(int, int*, int*);
|
||||||
|
void ByteIndexToQ(int, int*, int*);
|
||||||
|
|
||||||
|
void GetPVector(unsigned char*, unsigned char*, int);
|
||||||
|
void SetPVector(unsigned char*, unsigned char*, int);
|
||||||
|
void FillPVector(unsigned char*, unsigned char, int);
|
||||||
|
void AndPVector(unsigned char*, unsigned char, int);
|
||||||
|
void OrPVector(unsigned char*, unsigned char, int);
|
||||||
|
|
||||||
|
void GetQVector(unsigned char*, unsigned char*, int);
|
||||||
|
void SetQVector(unsigned char*, unsigned char*, int);
|
||||||
|
void FillQVector(unsigned char*, unsigned char, int);
|
||||||
|
void AndQVector(unsigned char*, unsigned char, int);
|
||||||
|
void OrQVector(unsigned char*, unsigned char, int);
|
||||||
|
|
||||||
|
int DecodePQ(ReedSolomonTables*, unsigned char*, int, int*, int);
|
||||||
|
|
||||||
|
int CountC2Errors(unsigned char*);
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** misc.c
|
||||||
|
***/
|
||||||
|
|
||||||
|
char* sgettext(char*);
|
||||||
|
char* sgettext_utf8(char*);
|
||||||
|
|
||||||
|
int64 uchar_to_int64(unsigned char*);
|
||||||
|
void int64_to_uchar(unsigned char*, int64);
|
||||||
|
|
||||||
|
void CalcSectors(int64, int64*, int*);
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** recover-raw.c
|
||||||
|
***/
|
||||||
|
|
||||||
|
#define CD_RAW_SECTOR_SIZE 2352
|
||||||
|
#define CD_RAW_C2_SECTOR_SIZE (2352+294) /* main channel plus C2 vector */
|
||||||
|
|
||||||
|
int CheckEDC(const unsigned char*, bool);
|
||||||
|
int CheckMSF(unsigned char*, int);
|
||||||
|
|
||||||
|
|
||||||
|
int ValidateRawSector(unsigned char *frame, bool xaMode);
|
||||||
|
bool Init_LEC_Correct(void);
|
||||||
|
void Kill_LEC_Correct(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* DVDISASTER_H */
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* dvdisaster: Additional error correction for optical media.
|
||||||
|
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||||
|
* Project home page: http://www.dvdisaster.com
|
||||||
|
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||||
|
*
|
||||||
|
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||||
|
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||||
|
* or direct your browser at http://www.gnu.org.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dvdisaster.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following routine is performance critical.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline int mod_fieldmax(int x)
|
||||||
|
{
|
||||||
|
while (x >= GF_FIELDMAX)
|
||||||
|
{
|
||||||
|
x -= GF_FIELDMAX;
|
||||||
|
x = (x >> GF_SYMBOLSIZE) + (x & GF_FIELDMAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
/* dvdisaster: Additional error correction for optical media.
|
||||||
|
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||||
|
* Project home page: http://www.dvdisaster.com
|
||||||
|
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||||
|
*
|
||||||
|
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||||
|
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||||
|
* or direct your browser at http://www.gnu.org.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dvdisaster.h"
|
||||||
|
|
||||||
|
#include "galois-inlines.h"
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** Galois field arithmetic.
|
||||||
|
***
|
||||||
|
* Calculations are done over the extension field GF(2**n).
|
||||||
|
* Be careful not to overgeneralize these arithmetics;
|
||||||
|
* they only work for the case of GF(p**n) with p being prime.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Initialize the Galois field tables */
|
||||||
|
|
||||||
|
|
||||||
|
GaloisTables* CreateGaloisTables(int32 gf_generator)
|
||||||
|
{
|
||||||
|
GaloisTables *gt = (GaloisTables *)calloc(1, sizeof(GaloisTables));
|
||||||
|
int32 b,log;
|
||||||
|
|
||||||
|
/* Allocate the tables.
|
||||||
|
The encoder uses a special version of alpha_to which has the mod_fieldmax()
|
||||||
|
folded into the table. */
|
||||||
|
|
||||||
|
gt->gfGenerator = gf_generator;
|
||||||
|
|
||||||
|
gt->indexOf = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32));
|
||||||
|
gt->alphaTo = (int32 *)calloc(GF_FIELDSIZE, sizeof(int32));
|
||||||
|
gt->encAlphaTo = (int32 *)calloc(2*GF_FIELDSIZE, sizeof(int32));
|
||||||
|
|
||||||
|
/* create the log/ilog values */
|
||||||
|
|
||||||
|
for(b=1, log=0; log<GF_FIELDMAX; log++)
|
||||||
|
{ gt->indexOf[b] = log;
|
||||||
|
gt->alphaTo[log] = b;
|
||||||
|
b = b << 1;
|
||||||
|
if(b & GF_FIELDSIZE)
|
||||||
|
b = b ^ gf_generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(b!=1)
|
||||||
|
{
|
||||||
|
printf("Failed to create the Galois field log tables!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we're even closed using infinity (makes things easier) */
|
||||||
|
|
||||||
|
gt->indexOf[0] = GF_ALPHA0; /* log(0) = inf */
|
||||||
|
gt->alphaTo[GF_ALPHA0] = 0; /* and the other way around */
|
||||||
|
|
||||||
|
for(b=0; b<2*GF_FIELDSIZE; b++)
|
||||||
|
gt->encAlphaTo[b] = gt->alphaTo[mod_fieldmax(b)];
|
||||||
|
|
||||||
|
return gt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeGaloisTables(GaloisTables *gt)
|
||||||
|
{
|
||||||
|
if(gt->indexOf) free(gt->indexOf);
|
||||||
|
if(gt->alphaTo) free(gt->alphaTo);
|
||||||
|
if(gt->encAlphaTo) free(gt->encAlphaTo);
|
||||||
|
|
||||||
|
free(gt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** Create the the Reed-Solomon generator polynomial
|
||||||
|
*** and some auxiliary data structures.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ReedSolomonTables *CreateReedSolomonTables(GaloisTables *gt,
|
||||||
|
int32 first_consecutive_root,
|
||||||
|
int32 prim_elem,
|
||||||
|
int nroots_in)
|
||||||
|
{ ReedSolomonTables *rt = (ReedSolomonTables *)calloc(1, sizeof(ReedSolomonTables));
|
||||||
|
int32 i,j,root;
|
||||||
|
|
||||||
|
rt->gfTables = gt;
|
||||||
|
rt->fcr = first_consecutive_root;
|
||||||
|
rt->primElem = prim_elem;
|
||||||
|
rt->nroots = nroots_in;
|
||||||
|
rt->ndata = GF_FIELDMAX - rt->nroots;
|
||||||
|
|
||||||
|
rt->gpoly = (int32 *)calloc((rt->nroots+1), sizeof(int32));
|
||||||
|
|
||||||
|
/* Create the RS code generator polynomial */
|
||||||
|
|
||||||
|
rt->gpoly[0] = 1;
|
||||||
|
|
||||||
|
for(i=0, root=first_consecutive_root*prim_elem; i<rt->nroots; i++, root+=prim_elem)
|
||||||
|
{ rt->gpoly[i+1] = 1;
|
||||||
|
|
||||||
|
/* Multiply gpoly by alpha**(root+x) */
|
||||||
|
|
||||||
|
for(j=i; j>0; j--)
|
||||||
|
{
|
||||||
|
if(rt->gpoly[j] != 0)
|
||||||
|
rt->gpoly[j] = rt->gpoly[j-1] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[j]] + root)];
|
||||||
|
else
|
||||||
|
rt->gpoly[j] = rt->gpoly[j-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
rt->gpoly[0] = gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[0]] + root)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the polynomials index for faster encoding */
|
||||||
|
|
||||||
|
for(i=0; i<=rt->nroots; i++)
|
||||||
|
rt->gpoly[i] = gt->indexOf[rt->gpoly[i]];
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* for the precalculated unrolled loops only */
|
||||||
|
|
||||||
|
for(i=gt->nroots-1; i>0; i--)
|
||||||
|
PrintCLI(
|
||||||
|
" par_idx[((++spk)&%d)] ^= enc_alpha_to[feedback + %3d];\n",
|
||||||
|
nroots-1,gt->gpoly[i]);
|
||||||
|
|
||||||
|
PrintCLI(" par_idx[sp] = enc_alpha_to[feedback + %3d];\n",
|
||||||
|
gt->gpoly[0]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeReedSolomonTables(ReedSolomonTables *rt)
|
||||||
|
{
|
||||||
|
if(rt->gpoly) free(rt->gpoly);
|
||||||
|
|
||||||
|
free(rt);
|
||||||
|
}
|
|
@ -0,0 +1,478 @@
|
||||||
|
/* dvdisaster: Additional error correction for optical media.
|
||||||
|
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||||
|
* Project home page: http://www.dvdisaster.com
|
||||||
|
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||||
|
*
|
||||||
|
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
|
||||||
|
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||||
|
* or direct your browser at http://www.gnu.org.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dvdisaster.h"
|
||||||
|
|
||||||
|
#include "galois-inlines.h"
|
||||||
|
|
||||||
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** Mapping between cd frame and parity vectors
|
||||||
|
***/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mapping of frame bytes to P/Q Vectors
|
||||||
|
*/
|
||||||
|
|
||||||
|
int PToByteIndex(int p, int i)
|
||||||
|
{ return 12 + p + i*86;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ByteIndexToP(int b, int *p, int *i)
|
||||||
|
{ *p = (b-12)%86;
|
||||||
|
*i = (b-12)/86;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QToByteIndex(int q, int i)
|
||||||
|
{ int offset = 12 + (q & 1);
|
||||||
|
|
||||||
|
if(i == 43) return 2248+q;
|
||||||
|
if(i == 44) return 2300+q;
|
||||||
|
|
||||||
|
q&=~1;
|
||||||
|
return offset + (q*43 + i*88) % 2236;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ByteIndexToQ(int b, int *q, int *i)
|
||||||
|
{ int x,y,offset;
|
||||||
|
|
||||||
|
if(b >= 2300)
|
||||||
|
{ *i = 44;
|
||||||
|
*q = (b-2300);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(b >= 2248)
|
||||||
|
{ *i = 43;
|
||||||
|
*q = (b-2248);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = b&1;
|
||||||
|
b = (b-12)/2;
|
||||||
|
x = b/43;
|
||||||
|
y = (b-(x*43))%26;
|
||||||
|
*i = b-(x*43);
|
||||||
|
*q = 2*((x+26-y)%26)+offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are 86 vectors of P-parity, yielding a RS(26,24) code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void GetPVector(unsigned char *frame, unsigned char *data, int n)
|
||||||
|
{ int i;
|
||||||
|
int w_idx = n+12;
|
||||||
|
|
||||||
|
for(i=0; i<26; i++, w_idx+=86)
|
||||||
|
data[i] = frame[w_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPVector(unsigned char *frame, unsigned char *data, int n)
|
||||||
|
{ int i;
|
||||||
|
int w_idx = n+12;
|
||||||
|
|
||||||
|
for(i=0; i<26; i++, w_idx+=86)
|
||||||
|
frame[w_idx] = data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillPVector(unsigned char *frame, unsigned char data, int n)
|
||||||
|
{ int i;
|
||||||
|
int w_idx = n+12;
|
||||||
|
|
||||||
|
for(i=0; i<26; i++, w_idx+=86)
|
||||||
|
frame[w_idx] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrPVector(unsigned char *frame, unsigned char value, int n)
|
||||||
|
{ int i;
|
||||||
|
int w_idx = n+12;
|
||||||
|
|
||||||
|
for(i=0; i<26; i++, w_idx+=86)
|
||||||
|
frame[w_idx] |= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndPVector(unsigned char *frame, unsigned char value, int n)
|
||||||
|
{ int i;
|
||||||
|
int w_idx = n+12;
|
||||||
|
|
||||||
|
for(i=0; i<26; i++, w_idx+=86)
|
||||||
|
frame[w_idx] &= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are 52 vectors of Q-parity, yielding a RS(45,43) code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void GetQVector(unsigned char *frame, unsigned char *data, int n)
|
||||||
|
{ int offset = 12 + (n & 1);
|
||||||
|
int w_idx = (n&~1) * 43;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<43; i++, w_idx+=88)
|
||||||
|
data[i] = frame[(w_idx % 2236) + offset];
|
||||||
|
|
||||||
|
data[43] = frame[2248 + n];
|
||||||
|
data[44] = frame[2300 + n];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetQVector(unsigned char *frame, unsigned char *data, int n)
|
||||||
|
{ int offset = 12 + (n & 1);
|
||||||
|
int w_idx = (n&~1) * 43;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<43; i++, w_idx+=88)
|
||||||
|
frame[(w_idx % 2236) + offset] = data[i];
|
||||||
|
|
||||||
|
frame[2248 + n] = data[43];
|
||||||
|
frame[2300 + n] = data[44];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillQVector(unsigned char *frame, unsigned char data, int n)
|
||||||
|
{ int offset = 12 + (n & 1);
|
||||||
|
int w_idx = (n&~1) * 43;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<43; i++, w_idx+=88)
|
||||||
|
frame[(w_idx % 2236) + offset] = data;
|
||||||
|
|
||||||
|
frame[2248 + n] = data;
|
||||||
|
frame[2300 + n] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrQVector(unsigned char *frame, unsigned char data, int n)
|
||||||
|
{ int offset = 12 + (n & 1);
|
||||||
|
int w_idx = (n&~1) * 43;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<43; i++, w_idx+=88)
|
||||||
|
frame[(w_idx % 2236) + offset] |= data;
|
||||||
|
|
||||||
|
frame[2248 + n] |= data;
|
||||||
|
frame[2300 + n] |= data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndQVector(unsigned char *frame, unsigned char data, int n)
|
||||||
|
{ int offset = 12 + (n & 1);
|
||||||
|
int w_idx = (n&~1) * 43;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i<43; i++, w_idx+=88)
|
||||||
|
frame[(w_idx % 2236) + offset] &= data;
|
||||||
|
|
||||||
|
frame[2248 + n] &= data;
|
||||||
|
frame[2300 + n] &= data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** C2 error counting
|
||||||
|
***/
|
||||||
|
|
||||||
|
int CountC2Errors(unsigned char *frame)
|
||||||
|
{ int i,count = 0;
|
||||||
|
frame += 2352;
|
||||||
|
|
||||||
|
for(i=0; i<294; i++, frame++)
|
||||||
|
{ if(*frame & 0x01) count++;
|
||||||
|
if(*frame & 0x02) count++;
|
||||||
|
if(*frame & 0x04) count++;
|
||||||
|
if(*frame & 0x08) count++;
|
||||||
|
if(*frame & 0x10) count++;
|
||||||
|
if(*frame & 0x20) count++;
|
||||||
|
if(*frame & 0x40) count++;
|
||||||
|
if(*frame & 0x80) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** L-EC error correction for CD raw data sectors
|
||||||
|
***/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These could be used from ReedSolomonTables,
|
||||||
|
* but hardcoding them is faster.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NROOTS 2
|
||||||
|
#define LEC_FIRST_ROOT 0 //GF_ALPHA0
|
||||||
|
#define LEC_PRIM_ELEM 1
|
||||||
|
#define LEC_PRIMTH_ROOT 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the error syndrome
|
||||||
|
*/
|
||||||
|
|
||||||
|
int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
|
||||||
|
int *erasure_list, int erasure_count)
|
||||||
|
{ GaloisTables *gt = rt->gfTables;
|
||||||
|
int syndrome[NROOTS];
|
||||||
|
int lambda[NROOTS+1];
|
||||||
|
int omega[NROOTS+1];
|
||||||
|
int b[NROOTS+1];
|
||||||
|
int reg[NROOTS+1];
|
||||||
|
int root[NROOTS];
|
||||||
|
int loc[NROOTS];
|
||||||
|
int syn_error;
|
||||||
|
int deg_lambda,lambda_roots;
|
||||||
|
int deg_omega;
|
||||||
|
int shortened_size = GF_FIELDMAX - padding;
|
||||||
|
int corrected = 0;
|
||||||
|
int i,j,k;
|
||||||
|
int r,el;
|
||||||
|
|
||||||
|
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
|
||||||
|
|
||||||
|
for(i=0; i<NROOTS; i++)
|
||||||
|
syndrome[i] = data[0];
|
||||||
|
|
||||||
|
for(j=1; j<shortened_size; j++)
|
||||||
|
for(i=0; i<NROOTS; i++)
|
||||||
|
if(syndrome[i] == 0)
|
||||||
|
syndrome[i] = data[j];
|
||||||
|
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
|
||||||
|
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
|
||||||
|
|
||||||
|
/*** Convert syndrome to index form, check for nonzero condition. */
|
||||||
|
|
||||||
|
syn_error = 0;
|
||||||
|
for(i=0; i<NROOTS; i++)
|
||||||
|
{ syn_error |= syndrome[i];
|
||||||
|
syndrome[i] = gt->indexOf[syndrome[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** If the syndrome is zero, everything is fine. */
|
||||||
|
|
||||||
|
if(!syn_error)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*** Initialize lambda to be the erasure locator polynomial */
|
||||||
|
|
||||||
|
lambda[0] = 1;
|
||||||
|
lambda[1] = lambda[2] = 0;
|
||||||
|
|
||||||
|
erasure_list[0] += padding;
|
||||||
|
erasure_list[1] += padding;
|
||||||
|
|
||||||
|
if(erasure_count > 2) /* sanity check */
|
||||||
|
erasure_count = 0;
|
||||||
|
|
||||||
|
if(erasure_count > 0)
|
||||||
|
{ lambda[1] = gt->alphaTo[mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))];
|
||||||
|
|
||||||
|
for(i=1; i<erasure_count; i++)
|
||||||
|
{ int u = mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[i]));
|
||||||
|
|
||||||
|
for(j=i+1; j>0; j--)
|
||||||
|
{ int tmp = gt->indexOf[lambda[j-1]];
|
||||||
|
|
||||||
|
if(tmp != GF_ALPHA0)
|
||||||
|
lambda[j] ^= gt->alphaTo[mod_fieldmax(u + tmp)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0; i<NROOTS+1; i++)
|
||||||
|
b[i] = gt->indexOf[lambda[i]];
|
||||||
|
|
||||||
|
/*** Berlekamp-Massey algorithm to determine error+erasure locator polynomial */
|
||||||
|
|
||||||
|
r = erasure_count; /* r is the step number */
|
||||||
|
el = erasure_count;
|
||||||
|
|
||||||
|
/* Compute discrepancy at the r-th step in poly-form */
|
||||||
|
|
||||||
|
while(++r <= NROOTS)
|
||||||
|
{ int discr_r = 0;
|
||||||
|
|
||||||
|
for(i=0; i<r; i++)
|
||||||
|
if((lambda[i] != 0) && (syndrome[r-i-1] != GF_ALPHA0))
|
||||||
|
discr_r ^= gt->alphaTo[mod_fieldmax(gt->indexOf[lambda[i]] + syndrome[r-i-1])];
|
||||||
|
|
||||||
|
discr_r = gt->indexOf[discr_r];
|
||||||
|
|
||||||
|
if(discr_r == GF_ALPHA0)
|
||||||
|
{ /* B(x) = x*B(x) */
|
||||||
|
memmove(b+1, b, NROOTS*sizeof(b[0]));
|
||||||
|
b[0] = GF_ALPHA0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ int t[NROOTS+1];
|
||||||
|
|
||||||
|
/* T(x) = lambda(x) - discr_r*x*b(x) */
|
||||||
|
t[0] = lambda[0];
|
||||||
|
for(i=0; i<NROOTS; i++)
|
||||||
|
{ if(b[i] != GF_ALPHA0)
|
||||||
|
t[i+1] = lambda[i+1] ^ gt->alphaTo[mod_fieldmax(discr_r + b[i])];
|
||||||
|
else t[i+1] = lambda[i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(2*el <= r+erasure_count-1)
|
||||||
|
{ el = r + erasure_count - el;
|
||||||
|
|
||||||
|
/* B(x) <-- inv(discr_r) * lambda(x) */
|
||||||
|
for(i=0; i<=NROOTS; i++)
|
||||||
|
b[i] = (lambda[i] == 0) ? GF_ALPHA0
|
||||||
|
: mod_fieldmax(gt->indexOf[lambda[i]] - discr_r + GF_FIELDMAX);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* 2 lines below: B(x) <-- x*B(x) */
|
||||||
|
memmove(b+1, b, NROOTS*sizeof(b[0]));
|
||||||
|
b[0] = GF_ALPHA0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(lambda, t, (NROOTS+1)*sizeof(t[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Convert lambda to index form and compute deg(lambda(x)) */
|
||||||
|
|
||||||
|
deg_lambda = 0;
|
||||||
|
for(i=0; i<NROOTS+1; i++)
|
||||||
|
{ lambda[i] = gt->indexOf[lambda[i]];
|
||||||
|
if(lambda[i] != GF_ALPHA0)
|
||||||
|
deg_lambda = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Find roots of the error+erasure locator polynomial by Chien search */
|
||||||
|
|
||||||
|
memcpy(reg+1, lambda+1, NROOTS*sizeof(reg[0]));
|
||||||
|
lambda_roots = 0; /* Number of roots of lambda(x) */
|
||||||
|
|
||||||
|
for(i=1, k=LEC_PRIMTH_ROOT-1; i<=GF_FIELDMAX; i++, k=mod_fieldmax(k+LEC_PRIMTH_ROOT))
|
||||||
|
{ int q=1; /* lambda[0] is always 0 */
|
||||||
|
|
||||||
|
for(j=deg_lambda; j>0; j--)
|
||||||
|
{ if(reg[j] != GF_ALPHA0)
|
||||||
|
{ reg[j] = mod_fieldmax(reg[j] + j);
|
||||||
|
q ^= gt->alphaTo[reg[j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(q != 0) continue; /* Not a root */
|
||||||
|
|
||||||
|
/* store root in index-form and the error location number */
|
||||||
|
|
||||||
|
root[lambda_roots] = i;
|
||||||
|
loc[lambda_roots] = k;
|
||||||
|
|
||||||
|
/* If we've already found max possible roots, abort the search to save time */
|
||||||
|
|
||||||
|
if(++lambda_roots == deg_lambda) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deg(lambda) unequal to number of roots => uncorrectable error detected
|
||||||
|
This is not reliable for very small numbers of roots, e.g. nroots = 2 */
|
||||||
|
|
||||||
|
if(deg_lambda != lambda_roots)
|
||||||
|
{ return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute err+eras evaluator poly omega(x) = syn(x)*lambda(x)
|
||||||
|
(modulo x**nroots). in index form. Also find deg(omega). */
|
||||||
|
|
||||||
|
deg_omega = deg_lambda-1;
|
||||||
|
|
||||||
|
for(i=0; i<=deg_omega; i++)
|
||||||
|
{ int tmp = 0;
|
||||||
|
|
||||||
|
for(j=i; j>=0; j--)
|
||||||
|
{ if((syndrome[i - j] != GF_ALPHA0) && (lambda[j] != GF_ALPHA0))
|
||||||
|
tmp ^= gt->alphaTo[mod_fieldmax(syndrome[i - j] + lambda[j])];
|
||||||
|
}
|
||||||
|
|
||||||
|
omega[i] = gt->indexOf[tmp];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute error values in poly-form.
|
||||||
|
num1 = omega(inv(X(l))),
|
||||||
|
num2 = inv(X(l))**(FIRST_ROOT-1) and
|
||||||
|
den = lambda_pr(inv(X(l))) all in poly-form. */
|
||||||
|
|
||||||
|
for(j=lambda_roots-1; j>=0; j--)
|
||||||
|
{ int num1 = 0;
|
||||||
|
int num2;
|
||||||
|
int den;
|
||||||
|
int location = loc[j];
|
||||||
|
|
||||||
|
for(i=deg_omega; i>=0; i--)
|
||||||
|
{ if(omega[i] != GF_ALPHA0)
|
||||||
|
num1 ^= gt->alphaTo[mod_fieldmax(omega[i] + i * root[j])];
|
||||||
|
}
|
||||||
|
|
||||||
|
num2 = gt->alphaTo[mod_fieldmax(root[j] * (LEC_FIRST_ROOT - 1) + GF_FIELDMAX)];
|
||||||
|
den = 0;
|
||||||
|
|
||||||
|
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
|
||||||
|
|
||||||
|
for(i=MIN(deg_lambda, NROOTS-1) & ~1; i>=0; i-=2)
|
||||||
|
{ if(lambda[i+1] != GF_ALPHA0)
|
||||||
|
den ^= gt->alphaTo[mod_fieldmax(lambda[i+1] + i * root[j])];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply error to data */
|
||||||
|
|
||||||
|
if(num1 != 0 && location >= padding)
|
||||||
|
{
|
||||||
|
corrected++;
|
||||||
|
data[location-padding] ^= gt->alphaTo[mod_fieldmax(gt->indexOf[num1] + gt->indexOf[num2]
|
||||||
|
+ GF_FIELDMAX - gt->indexOf[den])];
|
||||||
|
|
||||||
|
/* If no erasures were given, at most one error was corrected.
|
||||||
|
Return its position in erasure_list[0]. */
|
||||||
|
|
||||||
|
if(!erasure_count)
|
||||||
|
erasure_list[0] = location-padding;
|
||||||
|
}
|
||||||
|
#if 1
|
||||||
|
else return -3;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
|
||||||
|
|
||||||
|
for(i=0; i<NROOTS; i++)
|
||||||
|
syndrome[i] = data[0];
|
||||||
|
|
||||||
|
for(j=1; j<shortened_size; j++)
|
||||||
|
for(i=0; i<NROOTS; i++)
|
||||||
|
{ if(syndrome[i] == 0)
|
||||||
|
syndrome[i] = data[j];
|
||||||
|
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
|
||||||
|
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Convert syndrome to index form, check for nonzero condition. */
|
||||||
|
#if 1
|
||||||
|
for(i=0; i<NROOTS; i++)
|
||||||
|
if(syndrome[i])
|
||||||
|
return -2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,691 @@
|
||||||
|
/* cdrdao - write audio CD-Rs in disc-at-once mode
|
||||||
|
*
|
||||||
|
* Copyright (C) 1998-2002 Andreas Mueller <andreas@daneb.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "lec.h"
|
||||||
|
|
||||||
|
#define GF8_PRIM_POLY 0x11d /* x^8 + x^4 + x^3 + x^2 + 1 */
|
||||||
|
|
||||||
|
#define EDC_POLY 0x8001801b /* (x^16 + x^15 + x^2 + 1) (x^16 + x^2 + x + 1) */
|
||||||
|
|
||||||
|
#define LEC_HEADER_OFFSET 12
|
||||||
|
#define LEC_DATA_OFFSET 16
|
||||||
|
#define LEC_MODE1_DATA_LEN 2048
|
||||||
|
#define LEC_MODE1_EDC_OFFSET 2064
|
||||||
|
#define LEC_MODE1_INTERMEDIATE_OFFSET 2068
|
||||||
|
#define LEC_MODE1_P_PARITY_OFFSET 2076
|
||||||
|
#define LEC_MODE1_Q_PARITY_OFFSET 2248
|
||||||
|
#define LEC_MODE2_FORM1_DATA_LEN (2048+8)
|
||||||
|
#define LEC_MODE2_FORM1_EDC_OFFSET 2072
|
||||||
|
#define LEC_MODE2_FORM2_DATA_LEN (2324+8)
|
||||||
|
#define LEC_MODE2_FORM2_EDC_OFFSET 2348
|
||||||
|
|
||||||
|
|
||||||
|
typedef u_int8_t gf8_t;
|
||||||
|
|
||||||
|
static u_int8_t GF8_LOG[256];
|
||||||
|
static gf8_t GF8_ILOG[256];
|
||||||
|
|
||||||
|
static const class Gf8_Q_Coeffs_Results_01 {
|
||||||
|
private:
|
||||||
|
u_int16_t table[43][256];
|
||||||
|
public:
|
||||||
|
Gf8_Q_Coeffs_Results_01();
|
||||||
|
~Gf8_Q_Coeffs_Results_01() {}
|
||||||
|
const u_int16_t *operator[] (int i) const { return &table[i][0]; }
|
||||||
|
operator const u_int16_t *() const { return &table[0][0]; }
|
||||||
|
} CF8_Q_COEFFS_RESULTS_01;
|
||||||
|
|
||||||
|
static const class CrcTable {
|
||||||
|
private:
|
||||||
|
u_int32_t table[256];
|
||||||
|
public:
|
||||||
|
CrcTable();
|
||||||
|
~CrcTable() {}
|
||||||
|
u_int32_t operator[](int i) const { return table[i]; }
|
||||||
|
operator const u_int32_t *() const { return table; }
|
||||||
|
} CRCTABLE;
|
||||||
|
|
||||||
|
static const class ScrambleTable {
|
||||||
|
private:
|
||||||
|
u_int8_t table[2340];
|
||||||
|
public:
|
||||||
|
ScrambleTable();
|
||||||
|
~ScrambleTable() {}
|
||||||
|
u_int8_t operator[](int i) const { return table[i]; }
|
||||||
|
operator const u_int8_t *() const { return table; }
|
||||||
|
} SCRAMBLE_TABLE;
|
||||||
|
|
||||||
|
/* Creates the logarithm and inverse logarithm table that is required
|
||||||
|
* for performing multiplication in the GF(8) domain.
|
||||||
|
*/
|
||||||
|
static void gf8_create_log_tables()
|
||||||
|
{
|
||||||
|
u_int8_t log;
|
||||||
|
u_int16_t b;
|
||||||
|
|
||||||
|
for (b = 0; b <= 255; b++) {
|
||||||
|
GF8_LOG[b] = 0;
|
||||||
|
GF8_ILOG[b] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = 1;
|
||||||
|
|
||||||
|
for (log = 0; log < 255; log++) {
|
||||||
|
GF8_LOG[(u_int8_t)b] = log;
|
||||||
|
GF8_ILOG[log] = (u_int8_t)b;
|
||||||
|
|
||||||
|
b <<= 1;
|
||||||
|
|
||||||
|
if ((b & 0x100) != 0)
|
||||||
|
b ^= GF8_PRIM_POLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Addition in the GF(8) domain: just the XOR of the values.
|
||||||
|
*/
|
||||||
|
#define gf8_add(a, b) (a) ^ (b)
|
||||||
|
|
||||||
|
|
||||||
|
/* Multiplication in the GF(8) domain: add the logarithms (modulo 255)
|
||||||
|
* and return the inverse logarithm. Not used!
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
static gf8_t gf8_mult(gf8_t a, gf8_t b)
|
||||||
|
{
|
||||||
|
int16_t sum;
|
||||||
|
|
||||||
|
if (a == 0 || b == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sum = GF8_LOG[a] + GF8_LOG[b];
|
||||||
|
|
||||||
|
if (sum >= 255)
|
||||||
|
sum -= 255;
|
||||||
|
|
||||||
|
return GF8_ILOG[sum];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Division in the GF(8) domain: Like multiplication but logarithms a
|
||||||
|
* subtracted.
|
||||||
|
*/
|
||||||
|
static gf8_t gf8_div(gf8_t a, gf8_t b)
|
||||||
|
{
|
||||||
|
int16_t sum;
|
||||||
|
|
||||||
|
assert(b != 0);
|
||||||
|
|
||||||
|
if (a == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sum = GF8_LOG[a] - GF8_LOG[b];
|
||||||
|
|
||||||
|
if (sum < 0)
|
||||||
|
sum += 255;
|
||||||
|
|
||||||
|
return GF8_ILOG[sum];
|
||||||
|
}
|
||||||
|
|
||||||
|
Gf8_Q_Coeffs_Results_01::Gf8_Q_Coeffs_Results_01()
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
u_int16_t c;
|
||||||
|
gf8_t GF8_COEFFS_HELP[2][45];
|
||||||
|
u_int8_t GF8_Q_COEFFS[2][45];
|
||||||
|
|
||||||
|
|
||||||
|
gf8_create_log_tables();
|
||||||
|
|
||||||
|
/* build matrix H:
|
||||||
|
* 1 1 ... 1 1
|
||||||
|
* a^44 a^43 ... a^1 a^0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (j = 0; j < 45; j++) {
|
||||||
|
GF8_COEFFS_HELP[0][j] = 1; /* e0 */
|
||||||
|
GF8_COEFFS_HELP[1][j] = GF8_ILOG[44-j]; /* e1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* resolve equation system for parity byte 0 and 1 */
|
||||||
|
|
||||||
|
/* e1' = e1 + e0 */
|
||||||
|
for (j = 0; j < 45; j++) {
|
||||||
|
GF8_Q_COEFFS[1][j] = gf8_add(GF8_COEFFS_HELP[1][j],
|
||||||
|
GF8_COEFFS_HELP[0][j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* e1'' = e1' / (a^1 + 1) */
|
||||||
|
for (j = 0; j < 45; j++) {
|
||||||
|
GF8_Q_COEFFS[1][j] = gf8_div(GF8_Q_COEFFS[1][j], GF8_Q_COEFFS[1][43]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* e0' = e0 + e1 / a^1 */
|
||||||
|
for (j = 0; j < 45; j++) {
|
||||||
|
GF8_Q_COEFFS[0][j] = gf8_add(GF8_COEFFS_HELP[0][j],
|
||||||
|
gf8_div(GF8_COEFFS_HELP[1][j],
|
||||||
|
GF8_ILOG[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* e0'' = e0' / (1 + 1 / a^1) */
|
||||||
|
for (j = 0; j < 45; j++) {
|
||||||
|
GF8_Q_COEFFS[0][j] = gf8_div(GF8_Q_COEFFS[0][j], GF8_Q_COEFFS[0][44]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the products of 0..255 with all of the Q coefficients in
|
||||||
|
* advance. When building the scalar product between the data vectors
|
||||||
|
* and the P/Q vectors the individual products can be looked up in
|
||||||
|
* this table
|
||||||
|
*
|
||||||
|
* The P parity coefficients are just a subset of the Q coefficients so
|
||||||
|
* that we do not need to create a separate table for them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (j = 0; j < 43; j++) {
|
||||||
|
|
||||||
|
table[j][0] = 0;
|
||||||
|
|
||||||
|
for (i = 1; i < 256; i++) {
|
||||||
|
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[0][j]];
|
||||||
|
if (c >= 255) c -= 255;
|
||||||
|
table[j][i] = GF8_ILOG[c];
|
||||||
|
|
||||||
|
c = GF8_LOG[i] + GF8_LOG[GF8_Q_COEFFS[1][j]];
|
||||||
|
if (c >= 255) c -= 255;
|
||||||
|
table[j][i] |= GF8_ILOG[c]<<8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reverses the bits in 'd'. 'bits' defines the bit width of 'd'.
|
||||||
|
*/
|
||||||
|
static u_int32_t mirror_bits(u_int32_t d, int bits)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u_int32_t r = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < bits; i++) {
|
||||||
|
r <<= 1;
|
||||||
|
|
||||||
|
if ((d & 0x1) != 0)
|
||||||
|
r |= 0x1;
|
||||||
|
|
||||||
|
d >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build the CRC lookup table for EDC_POLY poly. The CRC is 32 bit wide
|
||||||
|
* and reversed (i.e. the bit stream is divided by the EDC_POLY with the
|
||||||
|
* LSB first order).
|
||||||
|
*/
|
||||||
|
CrcTable::CrcTable ()
|
||||||
|
{
|
||||||
|
u_int32_t i, j;
|
||||||
|
u_int32_t r;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++) {
|
||||||
|
r = mirror_bits(i, 8);
|
||||||
|
|
||||||
|
r <<= 24;
|
||||||
|
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
if ((r & 0x80000000) != 0) {
|
||||||
|
r <<= 1;
|
||||||
|
r ^= EDC_POLY;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
r <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mirror_bits(r, 32);
|
||||||
|
|
||||||
|
table[i] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculates the CRC of given data with given lengths based on the
|
||||||
|
* table lookup algorithm.
|
||||||
|
*/
|
||||||
|
static u_int32_t calc_edc(u_int8_t *data, int len)
|
||||||
|
{
|
||||||
|
u_int32_t crc = 0;
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
crc = CRCTABLE[(int)(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build the scramble table as defined in the yellow book. The bytes
|
||||||
|
12 to 2351 of a sector will be XORed with the data of this table.
|
||||||
|
*/
|
||||||
|
ScrambleTable::ScrambleTable()
|
||||||
|
{
|
||||||
|
u_int16_t i, j;
|
||||||
|
u_int16_t reg = 1;
|
||||||
|
u_int8_t d;
|
||||||
|
|
||||||
|
for (i = 0; i < 2340; i++) {
|
||||||
|
d = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
d >>= 1;
|
||||||
|
|
||||||
|
if ((reg & 0x1) != 0)
|
||||||
|
d |= 0x80;
|
||||||
|
|
||||||
|
if ((reg & 0x1) != ((reg >> 1) & 0x1)) {
|
||||||
|
reg >>= 1;
|
||||||
|
reg |= 0x4000; /* 15-bit register */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
reg >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table[i] = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calc EDC for a MODE 1 sector
|
||||||
|
*/
|
||||||
|
static void calc_mode1_edc(u_int8_t *sector)
|
||||||
|
{
|
||||||
|
u_int32_t crc = calc_edc(sector, LEC_MODE1_DATA_LEN + 16);
|
||||||
|
|
||||||
|
sector[LEC_MODE1_EDC_OFFSET] = crc & 0xffL;
|
||||||
|
sector[LEC_MODE1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||||
|
sector[LEC_MODE1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||||
|
sector[LEC_MODE1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calc EDC for a XA form 1 sector
|
||||||
|
*/
|
||||||
|
static void calc_mode2_form1_edc(u_int8_t *sector)
|
||||||
|
{
|
||||||
|
u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||||
|
LEC_MODE2_FORM1_DATA_LEN);
|
||||||
|
|
||||||
|
sector[LEC_MODE2_FORM1_EDC_OFFSET] = crc & 0xffL;
|
||||||
|
sector[LEC_MODE2_FORM1_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||||
|
sector[LEC_MODE2_FORM1_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||||
|
sector[LEC_MODE2_FORM1_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calc EDC for a XA form 2 sector
|
||||||
|
*/
|
||||||
|
static void calc_mode2_form2_edc(u_int8_t *sector)
|
||||||
|
{
|
||||||
|
u_int32_t crc = calc_edc(sector + LEC_DATA_OFFSET,
|
||||||
|
LEC_MODE2_FORM2_DATA_LEN);
|
||||||
|
|
||||||
|
sector[LEC_MODE2_FORM2_EDC_OFFSET] = crc & 0xffL;
|
||||||
|
sector[LEC_MODE2_FORM2_EDC_OFFSET + 1] = (crc >> 8) & 0xffL;
|
||||||
|
sector[LEC_MODE2_FORM2_EDC_OFFSET + 2] = (crc >> 16) & 0xffL;
|
||||||
|
sector[LEC_MODE2_FORM2_EDC_OFFSET + 3] = (crc >> 24) & 0xffL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes the sync pattern to the given sector.
|
||||||
|
*/
|
||||||
|
static void set_sync_pattern(u_int8_t *sector)
|
||||||
|
{
|
||||||
|
sector[0] = 0;
|
||||||
|
|
||||||
|
sector[1] = sector[2] = sector[3] = sector[4] = sector[5] =
|
||||||
|
sector[6] = sector[7] = sector[8] = sector[9] = sector[10] = 0xff;
|
||||||
|
|
||||||
|
sector[11] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static u_int8_t bin2bcd(u_int8_t b)
|
||||||
|
{
|
||||||
|
return (((b/10) << 4) & 0xf0) | ((b%10) & 0x0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Builds the sector header.
|
||||||
|
*/
|
||||||
|
static void set_sector_header(u_int8_t mode, u_int32_t adr, u_int8_t *sector)
|
||||||
|
{
|
||||||
|
sector[LEC_HEADER_OFFSET] = bin2bcd(adr / (60*75));
|
||||||
|
sector[LEC_HEADER_OFFSET + 1] = bin2bcd((adr / 75) % 60);
|
||||||
|
sector[LEC_HEADER_OFFSET + 2] = bin2bcd(adr % 75);
|
||||||
|
sector[LEC_HEADER_OFFSET + 3] = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the P parities for the sector.
|
||||||
|
* The 43 P vectors of length 24 are combined with the GF8_P_COEFFS.
|
||||||
|
*/
|
||||||
|
static void calc_P_parity(u_int8_t *sector)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
u_int16_t p01_msb, p01_lsb;
|
||||||
|
u_int8_t *p_lsb_start;
|
||||||
|
u_int8_t *p_lsb;
|
||||||
|
u_int8_t *p0, *p1;
|
||||||
|
u_int8_t d0,d1;
|
||||||
|
|
||||||
|
p_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||||
|
|
||||||
|
p1 = sector + LEC_MODE1_P_PARITY_OFFSET;
|
||||||
|
p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43;
|
||||||
|
|
||||||
|
for (i = 0; i <= 42; i++) {
|
||||||
|
p_lsb = p_lsb_start;
|
||||||
|
|
||||||
|
p01_lsb = p01_msb = 0;
|
||||||
|
|
||||||
|
for (j = 19; j <= 42; j++) {
|
||||||
|
d0 = *p_lsb;
|
||||||
|
d1 = *(p_lsb+1);
|
||||||
|
|
||||||
|
p01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0];
|
||||||
|
p01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1];
|
||||||
|
|
||||||
|
p_lsb += 2 * 43;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p0 = p01_lsb;
|
||||||
|
*(p0 + 1) = p01_msb;
|
||||||
|
|
||||||
|
*p1 = p01_lsb>>8;
|
||||||
|
*(p1 + 1) = p01_msb>>8;
|
||||||
|
|
||||||
|
p0 += 2;
|
||||||
|
p1 += 2;
|
||||||
|
|
||||||
|
p_lsb_start += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the Q parities for the sector.
|
||||||
|
* The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS.
|
||||||
|
*/
|
||||||
|
static void calc_Q_parity(u_int8_t *sector)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
u_int16_t q01_lsb, q01_msb;
|
||||||
|
u_int8_t *q_lsb_start;
|
||||||
|
u_int8_t *q_lsb;
|
||||||
|
u_int8_t *q0, *q1, *q_start;
|
||||||
|
u_int8_t d0,d1;
|
||||||
|
|
||||||
|
q_lsb_start = sector + LEC_HEADER_OFFSET;
|
||||||
|
|
||||||
|
q_start = sector + LEC_MODE1_Q_PARITY_OFFSET;
|
||||||
|
q1 = sector + LEC_MODE1_Q_PARITY_OFFSET;
|
||||||
|
q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26;
|
||||||
|
|
||||||
|
for (i = 0; i <= 25; i++) {
|
||||||
|
q_lsb = q_lsb_start;
|
||||||
|
|
||||||
|
q01_lsb = q01_msb = 0;
|
||||||
|
|
||||||
|
for (j = 0; j <= 42; j++) {
|
||||||
|
d0 = *q_lsb;
|
||||||
|
d1 = *(q_lsb+1);
|
||||||
|
|
||||||
|
q01_lsb ^= CF8_Q_COEFFS_RESULTS_01[j][d0];
|
||||||
|
q01_msb ^= CF8_Q_COEFFS_RESULTS_01[j][d1];
|
||||||
|
|
||||||
|
q_lsb += 2 * 44;
|
||||||
|
|
||||||
|
if (q_lsb >= q_start) {
|
||||||
|
q_lsb -= 2 * 1118;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*q0 = q01_lsb;
|
||||||
|
*(q0 + 1) = q01_msb;
|
||||||
|
|
||||||
|
*q1 = q01_lsb>>8;
|
||||||
|
*(q1 + 1) = q01_msb>>8;
|
||||||
|
|
||||||
|
q0 += 2;
|
||||||
|
q1 += 2;
|
||||||
|
|
||||||
|
q_lsb_start += 2 * 43;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encodes a MODE 0 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide
|
||||||
|
*/
|
||||||
|
void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector)
|
||||||
|
{
|
||||||
|
u_int16_t i;
|
||||||
|
|
||||||
|
set_sync_pattern(sector);
|
||||||
|
set_sector_header(0, adr, sector);
|
||||||
|
|
||||||
|
sector += 16;
|
||||||
|
|
||||||
|
for (i = 0; i < 2336; i++)
|
||||||
|
*sector++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encodes a MODE 1 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
|
||||||
|
* offset 16
|
||||||
|
*/
|
||||||
|
void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector)
|
||||||
|
{
|
||||||
|
set_sync_pattern(sector);
|
||||||
|
set_sector_header(1, adr, sector);
|
||||||
|
|
||||||
|
calc_mode1_edc(sector);
|
||||||
|
|
||||||
|
/* clear the intermediate field */
|
||||||
|
sector[LEC_MODE1_INTERMEDIATE_OFFSET] =
|
||||||
|
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 1] =
|
||||||
|
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 2] =
|
||||||
|
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 3] =
|
||||||
|
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 4] =
|
||||||
|
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 5] =
|
||||||
|
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 6] =
|
||||||
|
sector[LEC_MODE1_INTERMEDIATE_OFFSET + 7] = 0;
|
||||||
|
|
||||||
|
calc_P_parity(sector);
|
||||||
|
calc_Q_parity(sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encodes a MODE 2 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
|
||||||
|
* offset 16
|
||||||
|
*/
|
||||||
|
void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector)
|
||||||
|
{
|
||||||
|
set_sync_pattern(sector);
|
||||||
|
set_sector_header(2, adr, sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encodes a XA form 1 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
|
||||||
|
* offset 16
|
||||||
|
*/
|
||||||
|
void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector)
|
||||||
|
{
|
||||||
|
set_sync_pattern(sector);
|
||||||
|
|
||||||
|
calc_mode2_form1_edc(sector);
|
||||||
|
|
||||||
|
/* P/Q partiy must not contain the sector header so clear it */
|
||||||
|
sector[LEC_HEADER_OFFSET] =
|
||||||
|
sector[LEC_HEADER_OFFSET + 1] =
|
||||||
|
sector[LEC_HEADER_OFFSET + 2] =
|
||||||
|
sector[LEC_HEADER_OFFSET + 3] = 0;
|
||||||
|
|
||||||
|
calc_P_parity(sector);
|
||||||
|
calc_Q_parity(sector);
|
||||||
|
|
||||||
|
/* finally add the sector header */
|
||||||
|
set_sector_header(2, adr, sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encodes a XA form 2 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
|
||||||
|
* offset 16
|
||||||
|
*/
|
||||||
|
void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector)
|
||||||
|
{
|
||||||
|
set_sync_pattern(sector);
|
||||||
|
|
||||||
|
calc_mode2_form2_edc(sector);
|
||||||
|
|
||||||
|
set_sector_header(2, adr, sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrambles and byte swaps an encoded sector.
|
||||||
|
* 'sector' must be 2352 byte wide.
|
||||||
|
*/
|
||||||
|
void lec_scramble(u_int8_t *sector)
|
||||||
|
{
|
||||||
|
u_int16_t i;
|
||||||
|
const u_int8_t *stable = SCRAMBLE_TABLE;
|
||||||
|
u_int8_t *p = sector;
|
||||||
|
u_int8_t tmp;
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
/* just swap bytes of sector sync */
|
||||||
|
tmp = *p;
|
||||||
|
*p = *(p + 1);
|
||||||
|
p++;
|
||||||
|
*p++ = tmp;
|
||||||
|
}
|
||||||
|
for (;i < (2352 / 2); i++) {
|
||||||
|
/* scramble and swap bytes */
|
||||||
|
tmp = *p ^ *stable++;
|
||||||
|
*p = *(p + 1) ^ *stable++;
|
||||||
|
p++;
|
||||||
|
*p++ = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *infile;
|
||||||
|
char *outfile;
|
||||||
|
int fd_in, fd_out;
|
||||||
|
u_int8_t buffer1[2352];
|
||||||
|
u_int8_t buffer2[2352];
|
||||||
|
u_int32_t lba;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
for (i = 0; i < 2048; i++)
|
||||||
|
buffer1[i + 16] = 234;
|
||||||
|
|
||||||
|
lba = 150;
|
||||||
|
|
||||||
|
for (i = 0; i < 100000; i++) {
|
||||||
|
lec_encode_mode1_sector(lba, buffer1);
|
||||||
|
lec_scramble(buffer2);
|
||||||
|
lba++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
if (argc != 3)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
infile = argv[1];
|
||||||
|
outfile = argv[2];
|
||||||
|
|
||||||
|
|
||||||
|
if ((fd_in = open(infile, O_RDONLY)) < 0) {
|
||||||
|
perror("Cannot open input file");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fd_out = open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
|
||||||
|
perror("Cannot open output file");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lba = 150;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (read(fd_in, buffer1, 2352) != 2352)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (*(buffer1 + 12 + 3)) {
|
||||||
|
case 1:
|
||||||
|
memcpy(buffer2 + 16, buffer1 + 16, 2048);
|
||||||
|
|
||||||
|
lec_encode_mode1_sector(lba, buffer2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
if ((*(buffer1 + 12 + 4 + 2) & 0x20) != 0) {
|
||||||
|
/* form 2 sector */
|
||||||
|
memcpy(buffer2 + 16, buffer1 + 16, 2324 + 8);
|
||||||
|
lec_encode_mode2_form2_sector(lba, buffer2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* form 1 sector */
|
||||||
|
memcpy(buffer2 + 16, buffer1 + 16, 2048 + 8);
|
||||||
|
lec_encode_mode2_form1_sector(lba, buffer2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(buffer1, buffer2, 2352) != 0) {
|
||||||
|
printf("Verify error at lba %ld\n", lba);
|
||||||
|
}
|
||||||
|
|
||||||
|
lec_scramble(buffer2);
|
||||||
|
write(fd_out, buffer2, 2352);
|
||||||
|
|
||||||
|
lba++;
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
close(fd_in);
|
||||||
|
close(fd_out);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,77 @@
|
||||||
|
/* cdrdao - write audio CD-Rs in disc-at-once mode
|
||||||
|
*
|
||||||
|
* Copyright (C) 1998-2002 Andreas Mueller <mueller@daneb.ping.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LEC_H__
|
||||||
|
#define __LEC_H__
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
typedef uint32_t u_int32_t;
|
||||||
|
typedef uint16_t u_int16_t;
|
||||||
|
typedef uint8_t u_int8_t;
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Encodes a MODE 0 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide
|
||||||
|
*/
|
||||||
|
void lec_encode_mode0_sector(u_int32_t adr, u_int8_t *sector);
|
||||||
|
|
||||||
|
/* Encodes a MODE 1 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide containing 2048 bytes user data at
|
||||||
|
* offset 16
|
||||||
|
*/
|
||||||
|
void lec_encode_mode1_sector(u_int32_t adr, u_int8_t *sector);
|
||||||
|
|
||||||
|
/* Encodes a MODE 2 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide containing 2336 bytes user data at
|
||||||
|
* offset 16
|
||||||
|
*/
|
||||||
|
void lec_encode_mode2_sector(u_int32_t adr, u_int8_t *sector);
|
||||||
|
|
||||||
|
/* Encodes a XA form 1 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide containing 2048+8 bytes user data at
|
||||||
|
* offset 16
|
||||||
|
*/
|
||||||
|
void lec_encode_mode2_form1_sector(u_int32_t adr, u_int8_t *sector);
|
||||||
|
|
||||||
|
/* Encodes a XA form 2 sector.
|
||||||
|
* 'adr' is the current physical sector address
|
||||||
|
* 'sector' must be 2352 byte wide containing 2324+8 bytes user data at
|
||||||
|
* offset 16
|
||||||
|
*/
|
||||||
|
void lec_encode_mode2_form2_sector(u_int32_t adr, u_int8_t *sector);
|
||||||
|
|
||||||
|
/* Scrambles and byte swaps an encoded sector.
|
||||||
|
* 'sector' must be 2352 byte wide.
|
||||||
|
*/
|
||||||
|
void lec_scramble(u_int8_t *sector);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,203 @@
|
||||||
|
/* dvdisaster: Additional error correction for optical media.
|
||||||
|
* Copyright (C) 2004-2007 Carsten Gnoerlich.
|
||||||
|
* Project home page: http://www.dvdisaster.com
|
||||||
|
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
||||||
|
* or direct your browser at http://www.gnu.org.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dvdisaster.h"
|
||||||
|
|
||||||
|
static GaloisTables *gt = NULL; /* for L-EC Reed-Solomon */
|
||||||
|
static ReedSolomonTables *rt = NULL;
|
||||||
|
|
||||||
|
bool Init_LEC_Correct(void)
|
||||||
|
{
|
||||||
|
gt = CreateGaloisTables(0x11d);
|
||||||
|
rt = CreateReedSolomonTables(gt, 0, 1, 10);
|
||||||
|
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kill_LEC_Correct(void)
|
||||||
|
{
|
||||||
|
FreeGaloisTables(gt);
|
||||||
|
FreeReedSolomonTables(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** CD level CRC calculation
|
||||||
|
***/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test raw sector against its 32bit CRC.
|
||||||
|
* Returns TRUE if frame is good.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int CheckEDC(const unsigned char *cd_frame, bool xa_mode)
|
||||||
|
{
|
||||||
|
unsigned int expected_crc, real_crc;
|
||||||
|
unsigned int crc_base = xa_mode ? 2072 : 2064;
|
||||||
|
|
||||||
|
expected_crc = cd_frame[crc_base + 0] << 0;
|
||||||
|
expected_crc |= cd_frame[crc_base + 1] << 8;
|
||||||
|
expected_crc |= cd_frame[crc_base + 2] << 16;
|
||||||
|
expected_crc |= cd_frame[crc_base + 3] << 24;
|
||||||
|
|
||||||
|
if(xa_mode)
|
||||||
|
real_crc = EDCCrc32(cd_frame+16, 2056);
|
||||||
|
else
|
||||||
|
real_crc = EDCCrc32(cd_frame, 2064);
|
||||||
|
|
||||||
|
if(expected_crc == real_crc)
|
||||||
|
return(1);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//printf("Bad EDC CRC: Calculated: %08x, Recorded: %08x\n", real_crc, expected_crc);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** A very simple L-EC error correction.
|
||||||
|
***
|
||||||
|
* Perform just one pass over the Q and P vectors to see if everything
|
||||||
|
* is okay respectively correct minor errors. This is pretty much the
|
||||||
|
* same stuff the drive is supposed to do in the final L-EC stage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int simple_lec(unsigned char *frame)
|
||||||
|
{
|
||||||
|
unsigned char byte_state[2352];
|
||||||
|
unsigned char p_vector[P_VECTOR_SIZE];
|
||||||
|
unsigned char q_vector[Q_VECTOR_SIZE];
|
||||||
|
unsigned char p_state[P_VECTOR_SIZE];
|
||||||
|
int erasures[Q_VECTOR_SIZE], erasure_count;
|
||||||
|
int ignore[2];
|
||||||
|
int p_failures, q_failures;
|
||||||
|
int p_corrected, q_corrected;
|
||||||
|
int p,q;
|
||||||
|
|
||||||
|
/* Setup */
|
||||||
|
|
||||||
|
memset(byte_state, 0, 2352);
|
||||||
|
|
||||||
|
p_failures = q_failures = 0;
|
||||||
|
p_corrected = q_corrected = 0;
|
||||||
|
|
||||||
|
/* Perform Q-Parity error correction */
|
||||||
|
|
||||||
|
for(q=0; q<N_Q_VECTORS; q++)
|
||||||
|
{ int err;
|
||||||
|
|
||||||
|
/* We have no erasure information for Q vectors */
|
||||||
|
|
||||||
|
GetQVector(frame, q_vector, q);
|
||||||
|
err = DecodePQ(rt, q_vector, Q_PADDING, ignore, 0);
|
||||||
|
|
||||||
|
/* See what we've got */
|
||||||
|
|
||||||
|
if(err < 0) /* Uncorrectable. Mark bytes are erasure. */
|
||||||
|
{ q_failures++;
|
||||||
|
FillQVector(byte_state, 1, q);
|
||||||
|
}
|
||||||
|
else /* Correctable */
|
||||||
|
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||||
|
{ SetQVector(frame, q_vector, q);
|
||||||
|
q_corrected++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform P-Parity error correction */
|
||||||
|
|
||||||
|
for(p=0; p<N_P_VECTORS; p++)
|
||||||
|
{ int err,i;
|
||||||
|
|
||||||
|
/* Try error correction without erasure information */
|
||||||
|
|
||||||
|
GetPVector(frame, p_vector, p);
|
||||||
|
err = DecodePQ(rt, p_vector, P_PADDING, ignore, 0);
|
||||||
|
|
||||||
|
/* If unsuccessful, try again using erasures.
|
||||||
|
Erasure information is uncertain, so try this last. */
|
||||||
|
|
||||||
|
if(err < 0 || err > 2)
|
||||||
|
{ GetPVector(byte_state, p_state, p);
|
||||||
|
erasure_count = 0;
|
||||||
|
|
||||||
|
for(i=0; i<P_VECTOR_SIZE; i++)
|
||||||
|
if(p_state[i])
|
||||||
|
erasures[erasure_count++] = i;
|
||||||
|
|
||||||
|
if(erasure_count > 0 && erasure_count <= 2)
|
||||||
|
{ GetPVector(frame, p_vector, p);
|
||||||
|
err = DecodePQ(rt, p_vector, P_PADDING, erasures, erasure_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See what we've got */
|
||||||
|
|
||||||
|
if(err < 0) /* Uncorrectable. */
|
||||||
|
{ p_failures++;
|
||||||
|
}
|
||||||
|
else /* Correctable. */
|
||||||
|
{ if(err == 1 || err == 2) /* Store back corrected vector */
|
||||||
|
{ SetPVector(frame, p_vector, p);
|
||||||
|
p_corrected++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sum up */
|
||||||
|
|
||||||
|
if(q_failures || p_failures || q_corrected || p_corrected)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** Validate CD raw sector
|
||||||
|
***/
|
||||||
|
|
||||||
|
int ValidateRawSector(unsigned char *frame, bool xaMode)
|
||||||
|
{
|
||||||
|
int lec_did_sth = FALSE_0;
|
||||||
|
|
||||||
|
/* Do simple L-EC.
|
||||||
|
It seems that drives stop their internal L-EC as soon as the
|
||||||
|
EDC is okay, so we may see uncorrected errors in the parity bytes.
|
||||||
|
Since we are also interested in the user data only and doing the
|
||||||
|
L-EC is expensive, we skip our L-EC as well when the EDC is fine. */
|
||||||
|
|
||||||
|
if(!CheckEDC(frame, xaMode))
|
||||||
|
{
|
||||||
|
lec_did_sth = simple_lec(frame);
|
||||||
|
}
|
||||||
|
/* Test internal sector checksum again */
|
||||||
|
|
||||||
|
if(!CheckEDC(frame, xaMode))
|
||||||
|
{
|
||||||
|
/* EDC failure in RAW sector */
|
||||||
|
return FALSE_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE_1;
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ PS_CDC::PS_CDC() : DMABuffer(4096)
|
||||||
IsPSXDisc = false;
|
IsPSXDisc = false;
|
||||||
Cur_disc = NULL;
|
Cur_disc = NULL;
|
||||||
Open_disc = NULL;
|
Open_disc = NULL;
|
||||||
|
EnableLEC = false;
|
||||||
|
|
||||||
DriveStatus = DS_STOPPED;
|
DriveStatus = DS_STOPPED;
|
||||||
PendingCommandPhase = 0;
|
PendingCommandPhase = 0;
|
||||||
|
@ -1016,11 +1017,14 @@ void PS_CDC::HandlePlayRead(void)
|
||||||
// maybe if(!(Mode & 0x30)) too?
|
// maybe if(!(Mode & 0x30)) too?
|
||||||
if(!(buf[12 + 6] & 0x20))
|
if(!(buf[12 + 6] & 0x20))
|
||||||
{
|
{
|
||||||
#ifdef LEC_CHECK
|
#ifdef WANT_LEC_CHECK
|
||||||
if(!edc_lec_check_and_correct(buf, true))
|
if (EnableLEC)
|
||||||
{
|
{
|
||||||
printf("Bad sector? - %d", CurSector);
|
if (!edc_lec_check_and_correct(buf, true))
|
||||||
}
|
{
|
||||||
|
printf("Bad sector? - %d", CurSector);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,12 @@ class PS_CDC
|
||||||
void SoftReset(void);
|
void SoftReset(void);
|
||||||
|
|
||||||
void GetCDAudio(int32 samples[2]);
|
void GetCDAudio(int32 samples[2]);
|
||||||
|
void SetLEC(bool enable) { EnableLEC = enable; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CDIF *Cur_CDIF;
|
CDIF *Cur_CDIF;
|
||||||
ShockDiscRef* Cur_disc;
|
ShockDiscRef* Cur_disc;
|
||||||
|
bool EnableLEC;
|
||||||
bool TrayOpen;
|
bool TrayOpen;
|
||||||
|
|
||||||
ShockDiscRef* Open_disc; //the disc that's in the tray, while the tray is open. pending, kind of. used because Cur_disc != NULL is used as a tray-closed marker in the CDC code
|
ShockDiscRef* Open_disc; //the disc that's in the tray, while the tray is open. pending, kind of. used because Cur_disc != NULL is used as a tray-closed marker in the CDC code
|
||||||
|
|
|
@ -2698,5 +2698,12 @@ EW_EXPORT s32 shock_SetTraceCallback(void* psx, void* opaque, ShockCallback_Trac
|
||||||
g_ShockTraceCallbackOpaque = opaque;
|
g_ShockTraceCallbackOpaque = opaque;
|
||||||
g_ShockTraceCallback = callback;
|
g_ShockTraceCallback = callback;
|
||||||
|
|
||||||
|
return SHOCK_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sets whether LEC is enabled (sector level error correction). Defaults to FALSE (disabled)
|
||||||
|
EW_EXPORT s32 shock_SetLEC(void* psx, bool enabled)
|
||||||
|
{
|
||||||
|
CDC->SetLEC(enabled);
|
||||||
return SHOCK_OK;
|
return SHOCK_OK;
|
||||||
}
|
}
|
|
@ -403,7 +403,7 @@ EW_EXPORT s32 shock_GetSamples(void* psx, void* buffer);
|
||||||
//Returns information about a memory buffer for peeking (main memory, spu memory, etc.)
|
//Returns information about a memory buffer for peeking (main memory, spu memory, etc.)
|
||||||
EW_EXPORT s32 shock_GetMemData(void* psx, void** ptr, s32* size, s32 memType);
|
EW_EXPORT s32 shock_GetMemData(void* psx, void** ptr, s32* size, s32 memType);
|
||||||
|
|
||||||
//savestate work. Returns the size if that's what was requested, otherwise error codes
|
//Savestate work. Returns the size if that's what was requested, otherwise error codes
|
||||||
EW_EXPORT s32 shock_StateTransaction(void *psx, ShockStateTransaction* transaction);
|
EW_EXPORT s32 shock_StateTransaction(void *psx, ShockStateTransaction* transaction);
|
||||||
|
|
||||||
//Retrieves the CPU registers in a compact struct
|
//Retrieves the CPU registers in a compact struct
|
||||||
|
@ -414,3 +414,6 @@ EW_EXPORT s32 shock_SetRegister_CPU(void* psx, s32 index, u32 value);
|
||||||
|
|
||||||
//Sets the callback to be used for CPU tracing
|
//Sets the callback to be used for CPU tracing
|
||||||
EW_EXPORT s32 shock_SetTraceCallback(void* psx, void* opaque, ShockCallback_Trace callback);
|
EW_EXPORT s32 shock_SetTraceCallback(void* psx, void* opaque, ShockCallback_Trace callback);
|
||||||
|
|
||||||
|
//Sets whether LEC is enabled (sector level error correction). Defaults to FALSE (disabled)
|
||||||
|
EW_EXPORT s32 shock_SetLEC(void* psx, bool enabled);
|
Loading…
Reference in New Issue