Loading...
Searching...
No Matches
ecc_cd.c
Go to the documentation of this file.
/*
* This file is part of the Aaru Data Preservation Suite.
* Copyright (c) 2019-2026 Natalia Portillo.
* ECC algorithm from ECM(c) 2002-2011 Neill Corlett
*
* 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/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "aaruformat.h"
#include "log.h"
AARU_EXPORT void *AARU_CALL aaruf_ecc_cd_init()
{
TRACE("Entering aaruf_ecc_cd_init()");
CdEccContext *context = NULL;
uint32_t edc = 0, i = 0, j = 0;
TRACE("Creating context");
context = (CdEccContext *)malloc(sizeof(CdEccContext));
if(context == NULL) return NULL;
TRACE("Allocating memory for ECC F table");
context->ecc_f_table = (uint8_t *)malloc(sizeof(uint8_t) * 256);
if(context->ecc_f_table == NULL)
{
free(context);
return NULL;
}
TRACE("Allocating memory for ECC B table");
context->ecc_b_table = (uint8_t *)malloc(sizeof(uint8_t) * 256);
if(context->ecc_b_table == NULL)
{
free(context->ecc_f_table);
free(context);
return NULL;
}
TRACE("Allocating memory for EDC table");
context->edc_table = (uint32_t *)malloc(sizeof(uint32_t) * 256);
if(context->edc_table == NULL)
{
free(context->ecc_f_table);
free(context->ecc_b_table);
free(context);
return NULL;
}
TRACE("Initializing EDC tables");
for(i = 0; i < 256; i++)
{
edc = i;
j = i << 1 ^ ((i & 0x80) == 0x80 ? 0x11D : 0);
context->ecc_f_table[i] = (uint8_t)j;
context->ecc_b_table[i ^ j] = (uint8_t)i;
for(j = 0; j < 8; j++) edc = edc >> 1 ^ ((edc & 1) > 0 ? 0xD8018001 : 0);
context->edc_table[i] = edc;
}
context->inited_edc = true;
TRACE("Exiting aaruf_ecc_cd_init()");
return context;
}
AARU_EXPORT void AARU_CALL aaruf_ecc_cd_free(void *ctx)
{
if(!ctx) return;
CdEccContext *context = (CdEccContext *)ctx;
free(context->ecc_f_table);
free(context->ecc_b_table);
free(context->edc_table);
free(context);
}
AARU_EXPORT bool AARU_CALL aaruf_ecc_cd_is_suffix_correct(void *context, const uint8_t *sector)
{
TRACE("Entering aaruf_ecc_cd_is_suffix_correct(%p, %p)", context, sector);
uint32_t edc;
int size, pos;
if(context == NULL || sector == NULL)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() without initialized context");
return false;
}
const CdEccContext *ctx = context;
if(!ctx->inited_edc)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() without initialized context");
return false;
}
if(sector[0x814] != 0x00 ||
// reserved (8 bytes)
sector[0x815] != 0x00 || sector[0x816] != 0x00 || sector[0x817] != 0x00 || sector[0x818] != 0x00 ||
sector[0x819] != 0x00 || sector[0x81A] != 0x00 || sector[0x81B] != 0x00)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = false");
return false;
}
const bool correct_ecc_p = aaruf_ecc_cd_check(context, sector, sector, 86, 24, 2, 86, sector, 0xC, 0x10, 0x81C);
if(!correct_ecc_p)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = false");
return false;
}
const bool correct_ecc_q =
aaruf_ecc_cd_check(context, sector, sector, 52, 43, 86, 88, sector, 0xC, 0x10, 0x81C + 0xAC);
if(!correct_ecc_q)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = false");
return false;
}
uint32_t stored_edc;
memcpy(&stored_edc, sector + 0x810, 4);
uint32_t calculated_edc = aaruf_edc_cd_compute(context, 0, sector, 0x810, 0);
if(stored_edc != calculated_edc)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = false");
return false;
}
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = %u == %u", calculated_edc, stored_edc);
return calculated_edc == stored_edc;
}
AARU_EXPORT bool AARU_CALL aaruf_ecc_cd_is_suffix_correct_mode2(void *context, const uint8_t *sector)
{
TRACE("Entering aaruf_ecc_cd_is_suffix_correct_mode2(%p, %p)", context, sector);
uint32_t edc;
int size, pos;
uint8_t zeroaddress[4] = {0};
if(context == NULL || sector == NULL)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() without initialized context");
return false;
}
CdEccContext *ctx = context;
if(!ctx->inited_edc)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() without initialized context");
return false;
}
const int form2 = sector[0x12] & 0x20;
const bool correct_ecc_p = aaruf_ecc_cd_check(context, zeroaddress, sector, 86, 24, 2, 86, sector, 0, 0x10, 0x81C);
if(!correct_ecc_p)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() = false");
return false;
}
const bool correct_ecc_q =
aaruf_ecc_cd_check(context, zeroaddress, sector, 52, 43, 86, 88, sector, 0, 0x10, 0x81C + 0xAC);
if(!correct_ecc_q)
{
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() = false");
return false;
}
uint32_t stored_edc;
memcpy(&stored_edc, form2 ? sector + 0x92C : sector + 0x818, 4);
const uint32_t calculated_edc = aaruf_edc_cd_compute(context, 0, sector, form2 ? 0x91C : 0x808, 0x10);
TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() = %u == %u", calculated_edc, stored_edc);
return calculated_edc == stored_edc;
}
AARU_EXPORT bool AARU_CALL aaruf_ecc_cd_check(void *context, const uint8_t *address, const uint8_t *data,
const uint32_t major_count, const uint32_t minor_count,
const uint32_t major_mult, const uint32_t minor_inc, const uint8_t *ecc,
const int32_t address_offset, const int32_t data_offset,
const int32_t ecc_offset)
{
TRACE("Entering aaruf_ecc_cd_check(%p, %p, %p, %u, %u, %u, %u, %p, %d, %d, %d)", context, address, data,
major_count, minor_count, major_mult, minor_inc, ecc, address_offset, data_offset, ecc_offset);
if(context == NULL || address == NULL || data == NULL || ecc == NULL)
{
TRACE("Exiting aaruf_ecc_cd_check() with missing data");
return false;
}
CdEccContext *ctx = context;
if(!ctx->inited_edc)
{
TRACE("Exiting aaruf_ecc_cd_check() without initialized context");
return false;
}
uint32_t size = major_count * minor_count;
for(uint32_t major = 0; major < major_count; major++)
{
uint32_t idx = (major >> 1) * major_mult + (major & 1);
uint8_t ecc_a = 0;
uint8_t ecc_b = 0;
for(uint32_t minor = 0; minor < minor_count; minor++)
{
uint8_t temp = idx < 4 ? address[idx + address_offset] : data[idx + data_offset - 4];
idx += minor_inc;
if(idx >= size) idx -= size;
ecc_a ^= temp;
ecc_b ^= temp;
ecc_a = ctx->ecc_f_table[ecc_a];
}
ecc_a = ctx->ecc_b_table[ctx->ecc_f_table[ecc_a] ^ ecc_b];
if(ecc[major + ecc_offset] != ecc_a || ecc[major + major_count + ecc_offset] != (ecc_a ^ ecc_b))
{
TRACE("Exiting aaruf_ecc_cd_check() = false, ECC mismatch at major %u", major);
return false;
}
}
TRACE("Exiting aaruf_ecc_cd_check() = true, ECC matches");
return true;
}
AARU_EXPORT void AARU_CALL aaruf_ecc_cd_write(void *context, const uint8_t *address, const uint8_t *data,
const uint32_t major_count, const uint32_t minor_count,
const uint32_t major_mult, const uint32_t minor_inc, uint8_t *ecc,
const int32_t address_offset, const int32_t data_offset,
const int32_t ecc_offset)
{
TRACE("Entering aaruf_ecc_cd_write(%p, %p, %p, %u, %u, %u, %u, %p, %d, %d, %d)", context, address, data,
major_count, minor_count, major_mult, minor_inc, ecc, address_offset, data_offset, ecc_offset);
if(context == NULL || address == NULL || data == NULL || ecc == NULL)
{
TRACE("Exiting aaruf_ecc_cd_write() with missing data");
return;
}
CdEccContext *ctx = context;
if(!ctx->inited_edc)
{
TRACE("Exiting aaruf_ecc_cd_write() without initialized context");
return;
}
uint32_t size = major_count * minor_count;
for(uint32_t major = 0; major < major_count; major++)
{
uint32_t idx = (major >> 1) * major_mult + (major & 1);
uint8_t ecc_a = 0;
uint8_t ecc_b = 0;
for(uint32_t minor = 0; minor < minor_count; minor++)
{
uint8_t temp = idx < 4 ? address[idx + address_offset] : data[idx + data_offset - 4];
idx += minor_inc;
if(idx >= size) idx -= size;
ecc_a ^= temp;
ecc_b ^= temp;
ecc_a = ctx->ecc_f_table[ecc_a];
}
ecc_a = ctx->ecc_b_table[ctx->ecc_f_table[ecc_a] ^ ecc_b];
ecc[major + ecc_offset] = ecc_a;
ecc[major + major_count + ecc_offset] = ecc_a ^ ecc_b;
}
TRACE("Exiting aaruf_ecc_cd_write()");
}
AARU_EXPORT void AARU_CALL aaruf_ecc_cd_write_sector(void *context, const uint8_t *address, const uint8_t *data,
uint8_t *ecc, const int32_t address_offset,
const int32_t data_offset, const int32_t ecc_offset)
{
TRACE("Entering aaruf_ecc_cd_write_sector(%p, %p, %p, %p, %d, %d, %d)", context, address, data, ecc, address_offset,
data_offset, ecc_offset);
aaruf_ecc_cd_write(context, address, data, 86, 24, 2, 86, ecc, address_offset, data_offset, ecc_offset); // P
aaruf_ecc_cd_write(context, address, data, 52, 43, 86, 88, ecc, address_offset, data_offset,
ecc_offset + 0xAC); // Q
TRACE("Exiting aaruf_ecc_cd_write_sector()");
}
AARU_LOCAL void AARU_CALL aaruf_cd_lba_to_msf(const int64_t pos, uint8_t *minute, uint8_t *second, uint8_t *frame)
{
TRACE("Entering aaruf_cd_lba_to_msf(%lld, %p, %p, %p)", pos, minute, second, frame);
*minute = (uint8_t)((pos + 150) / 75 / 60);
*second = (uint8_t)((pos + 150) / 75 % 60);
*frame = (uint8_t)((pos + 150) % 75);
TRACE("Exiting aaruf_cd_lba_to_msf() = %u:%u:%u", *minute, *second, *frame);
}
AARU_EXPORT void AARU_CALL aaruf_ecc_cd_reconstruct_prefix(uint8_t *sector, const uint8_t type, const int64_t lba)
{
TRACE("Entering aaruf_cd_reconstruct_prefix(%p, %u, %lld)", sector, type, lba);
uint8_t minute, second, frame;
if(sector == NULL) return;
//
// 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;
aaruf_cd_lba_to_msf(lba, &minute, &second, &frame);
sector[0x00C] = (uint8_t)(((minute / 10) << 4) + minute % 10);
sector[0x00D] = (uint8_t)(((second / 10) << 4) + second % 10);
sector[0x00E] = (uint8_t)(((frame / 10) << 4) + frame % 10);
switch((TrackType)type)
{
case CdMode1:
//
// Mode
//
sector[0x00F] = 0x01;
break;
case CdMode2Form1:
case CdMode2Form2:
case 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;
}
TRACE("Exiting aaruf_ecc_cd_reconstruct_prefix()");
}
AARU_EXPORT void AARU_CALL aaruf_ecc_cd_reconstruct(void *context, uint8_t *sector, const uint8_t type)
{
TRACE("Entering aaruf_ecc_cd_reconstruct(%p, %p, %u)", context, sector, type);
uint32_t computed_edc;
uint8_t zeroaddress[4];
if(context == NULL || sector == NULL)
{
TRACE("Exiting aaruf_ecc_cd_reconstruct() with missing data");
return;
}
CdEccContext *ctx = context;
if(!ctx->inited_edc)
{
TRACE("Exiting aaruf_ecc_cd_reconstruct() without initialized context");
return;
}
switch(type)
{
//
// Compute EDC
//
case CdMode1:
computed_edc = aaruf_edc_cd_compute(context, 0, sector, 0x810, 0);
memcpy(sector + 0x810, &computed_edc, 4);
break;
case CdMode2Form1:
computed_edc = aaruf_edc_cd_compute(context, 0, sector, 0x808, 0x10);
memcpy(sector + 0x818, &computed_edc, 4);
break;
case CdMode2Form2:
computed_edc = aaruf_edc_cd_compute(context, 0, sector, 0x91C, 0x10);
memcpy(sector + 0x92C, &computed_edc, 4);
break;
default:
TRACE("Exiting aaruf_ecc_cd_reconstruct() with unknown type %u", type);
return;
}
memset(&zeroaddress, 0, 4);
switch(type)
{
//
// Compute ECC
//
case 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;
aaruf_ecc_cd_write_sector(context, sector, sector, sector, 0xC, 0x10, 0x81C);
break;
case CdMode2Form1:
aaruf_ecc_cd_write_sector(context, zeroaddress, sector, sector, 0, 0x10, 0x81C);
break;
default:
TRACE("Exiting aaruf_ecc_cd_reconstruct() with unknown type %u", type);
return;
}
//
// Done
//
TRACE("Exiting aaruf_ecc_cd_reconstruct()");
}
AARU_EXPORT uint32_t AARU_CALL aaruf_edc_cd_compute(void *context, uint32_t edc, const uint8_t *src, int size, int pos)
{
TRACE("Entering aaruf_edc_cd_compute(%p, %u, %p, %d, %d)", context, edc, src, size, pos);
if(context == NULL || src == NULL)
{
TRACE("Exiting aaruf_edc_cd_compute() with missing data");
return 0;
}
CdEccContext *ctx = context;
if(!ctx->inited_edc)
{
TRACE("Exiting aaruf_edc_cd_compute() without initialized context");
return 0;
}
for(; size > 0; size--) edc = edc >> 8 ^ ctx->edc_table[(edc ^ src[pos++]) & 0xFF];
TRACE("Exiting aaruf_edc_cd_compute() = 0x%08X", edc);
return edc;
}
aaruformat.h
AARU_CALL
#define AARU_CALL
Definition decls.h:46
AARU_LOCAL
#define AARU_LOCAL
Definition decls.h:56
AARU_EXPORT
#define AARU_EXPORT
Definition decls.h:55
aaruf_ecc_cd_check
bool aaruf_ecc_cd_check(void *context, const uint8_t *address, const uint8_t *data, const uint32_t major_count, const uint32_t minor_count, const uint32_t major_mult, const uint32_t minor_inc, const uint8_t *ecc, const int32_t address_offset, const int32_t data_offset, const int32_t ecc_offset)
Checks the ECC of a CD sector.
Definition ecc_cd.c:244
aaruf_ecc_cd_write
void aaruf_ecc_cd_write(void *context, const uint8_t *address, const uint8_t *data, const uint32_t major_count, const uint32_t minor_count, const uint32_t major_mult, const uint32_t minor_inc, uint8_t *ecc, const int32_t address_offset, const int32_t data_offset, const int32_t ecc_offset)
Writes ECC for a CD sector.
Definition ecc_cd.c:310
aaruf_cd_lba_to_msf
void aaruf_cd_lba_to_msf(const int64_t pos, uint8_t *minute, uint8_t *second, uint8_t *frame)
Converts a CD LBA (Logical Block Address) to MSF (Minute:Second:Frame) format.
Definition ecc_cd.c:391
aaruf_ecc_cd_reconstruct
void aaruf_ecc_cd_reconstruct(void *context, uint8_t *sector, const uint8_t type)
Reconstructs the EDC and ECC fields of a CD sector.
Definition ecc_cd.c:476
aaruf_edc_cd_compute
uint32_t aaruf_edc_cd_compute(void *context, uint32_t edc, const uint8_t *src, int size, int pos)
Computes the EDC (Error Detection Code) for a CD sector.
Definition ecc_cd.c:564
aaruf_ecc_cd_reconstruct_prefix
void aaruf_ecc_cd_reconstruct_prefix(uint8_t *sector, const uint8_t type, const int64_t lba)
Reconstructs the prefix (sync, address, mode) of a CD sector.
Definition ecc_cd.c:409
aaruf_ecc_cd_write_sector
void aaruf_ecc_cd_write_sector(void *context, const uint8_t *address, const uint8_t *data, uint8_t *ecc, const int32_t address_offset, const int32_t data_offset, const int32_t ecc_offset)
Writes ECC for a full CD sector (both P and Q ECC).
Definition ecc_cd.c:369
aaruf_ecc_cd_is_suffix_correct_mode2
bool aaruf_ecc_cd_is_suffix_correct_mode2(void *context, const uint8_t *sector)
Checks if the suffix (EDC/ECC) of a CD sector is correct (Mode 2).
Definition ecc_cd.c:182
aaruf_ecc_cd_init
void * aaruf_ecc_cd_init()
Initializes a Compact Disc ECC context.
Definition ecc_cd.c:35
aaruf_ecc_cd_free
void aaruf_ecc_cd_free(void *ctx)
Frees a Compact Disc ECC context and its internal tables.
Definition ecc_cd.c:99
aaruf_ecc_cd_is_suffix_correct
bool aaruf_ecc_cd_is_suffix_correct(void *context, const uint8_t *sector)
Checks if the suffix (EDC/ECC) of a CD sector is correct (Mode 1).
Definition ecc_cd.c:118
TrackType
TrackType
Track (partitioning element) types for optical media.
Definition enums.h:200
CdMode1
@ CdMode1
Compact Disc Mode 1 data track.
Definition enums.h:203
CdMode2Form2
@ CdMode2Form2
Compact Disc Mode 2 Form 2 data track.
Definition enums.h:206
CdMode2Form1
@ CdMode2Form1
Compact Disc Mode 2 Form 1 data track.
Definition enums.h:205
CdMode2Formless
@ CdMode2Formless
Compact Disc Mode 2 (formless) data track.
Definition enums.h:204
log.h
TRACE
#define TRACE(fmt,...)
Definition log.h:25
CdEccContext
Lookup tables and state for Compact Disc EDC/ECC (P/Q) regeneration / verification.
Definition context.h:89
CdEccContext::ecc_f_table
uint8_t * ecc_f_table
Forward (F) ECC table.
Definition context.h:92
CdEccContext::inited_edc
bool inited_edc
True once EDC/ECC tables have been initialized.
Definition context.h:90
CdEccContext::edc_table
uint32_t * edc_table
EDC (CRC) lookup table.
Definition context.h:93
CdEccContext::ecc_b_table
uint8_t * ecc_b_table
Backward (B) ECC table (allocated, size implementation-defined).
Definition context.h:91