mirror of
https://github.com/aaru-dps/libaaruformat.git
synced 2025-12-16 19:24:40 +00:00
Implement CD ECC and EDC.
This commit is contained in:
@@ -5,6 +5,6 @@ set(CMAKE_C_STANDARD 99)
|
||||
add_compile_definitions(__STDC_FORMAT_MACROS=1)
|
||||
|
||||
add_library(dicformat SHARED include/dicformat/consts.h include/dicformat/enums.h include/dic.h include/dicformat.h
|
||||
include/dicformat/decls.h include/dicformat/structs.h src/identify.c src/open.c include/dicformat/context.h src/close.c include/dicformat/errors.h src/read.c src/crc64.c src/cst.c)
|
||||
include/dicformat/decls.h include/dicformat/structs.h src/identify.c src/open.c include/dicformat/context.h src/close.c include/dicformat/errors.h src/read.c src/crc64.c src/cst.c src/ecc_cd.c)
|
||||
|
||||
include_directories(include include/dicformat)
|
||||
@@ -67,6 +67,7 @@ typedef struct dicformatContext
|
||||
DumpHardwareHeader dumpHardwareHeader;
|
||||
struct DumpHardwareEntriesWithData *dumpHardwareEntriesWithData;
|
||||
struct ImageInfo imageInfo;
|
||||
CdEccContext *eccCdContext;
|
||||
} dicformatContext;
|
||||
|
||||
typedef struct dataLinkedList
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int identify(const char *filename);
|
||||
|
||||
@@ -64,4 +65,50 @@ int32_t cst_transform(const uint8_t *interleaved, uint8_t *sequential, size_t le
|
||||
|
||||
int32_t cst_untransform(const uint8_t *sequential, uint8_t *interleaved, size_t length);
|
||||
|
||||
void *ecc_cd_init();
|
||||
|
||||
bool ecc_cd_is_suffix_correct(void *context, const uint8_t *sector);
|
||||
|
||||
bool ecc_cd_is_suffix_correct_mode2(void *context, const uint8_t *sector);
|
||||
|
||||
bool ecc_cd_check(void *context,
|
||||
const uint8_t *address,
|
||||
const uint8_t *data,
|
||||
uint32_t majorCount,
|
||||
uint32_t minorCount,
|
||||
uint32_t majorMult,
|
||||
uint32_t minorInc,
|
||||
const uint8_t *ecc,
|
||||
int32_t addressOffset,
|
||||
int32_t dataOffset,
|
||||
int32_t eccOffset);
|
||||
|
||||
void ecc_cd_write(void *context,
|
||||
const uint8_t *address,
|
||||
const uint8_t *data,
|
||||
uint32_t majorCount,
|
||||
uint32_t minorCount,
|
||||
uint32_t majorMult,
|
||||
uint32_t minorInc,
|
||||
uint8_t *ecc,
|
||||
int32_t addressOffset,
|
||||
int32_t dataOffset,
|
||||
int32_t eccOffset);
|
||||
|
||||
void ecc_cd_write_sector(void *context,
|
||||
const uint8_t *address,
|
||||
const uint8_t *data,
|
||||
uint8_t *ecc,
|
||||
int32_t addressOffset,
|
||||
int32_t dataOffset,
|
||||
int32_t eccOffset);
|
||||
|
||||
void cd_lba_to_msf(int64_t pos, uint8_t *minute, uint8_t *second, uint8_t *frame);
|
||||
|
||||
void ecc_cd_reconstruct_prefix(uint8_t *sector, uint8_t type, int64_t lba);
|
||||
|
||||
void ecc_cd_reconstruct(void *context, uint8_t *sector, uint8_t type);
|
||||
|
||||
uint32_t edc_cd_compute(void *context, uint32_t edc, const uint8_t *src, int size, int pos);
|
||||
|
||||
#endif //LIBDICFORMAT_DECLS_H
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <dic.h>
|
||||
#include "enums.h"
|
||||
|
||||
@@ -309,6 +310,14 @@ typedef struct Crc64Context
|
||||
uint64_t hashInt;
|
||||
} Crc64Context;
|
||||
|
||||
typedef struct CdEccContext
|
||||
{
|
||||
bool initedEdc;
|
||||
uint8_t *eccBTable;
|
||||
uint8_t *eccFTable;
|
||||
uint32_t *edcTable;
|
||||
} CdEccContext;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif //LIBDICFORMAT_STRUCTS_H
|
||||
|
||||
428
src/ecc_cd.c
Normal file
428
src/ecc_cd.c
Normal file
@@ -0,0 +1,428 @@
|
||||
// /***************************************************************************
|
||||
// The Disc Image Chef
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ecc_cd.c
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Disk image plugins.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Contains the CD ECC algorithm.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2019 Natalia Portillo
|
||||
// ECC algorithm from ECM(c) 2002-2011 Neill Corlett
|
||||
// ****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <dicformat.h>
|
||||
|
||||
void *ecc_cd_init()
|
||||
{
|
||||
CdEccContext *context;
|
||||
uint32_t edc, i, j;
|
||||
|
||||
context = (CdEccContext *)malloc(sizeof(CdEccContext));
|
||||
|
||||
if(context == NULL)
|
||||
return NULL;
|
||||
|
||||
context->eccFTable = (uint8_t *)malloc(sizeof(uint8_t) * 256);
|
||||
|
||||
if(context->eccFTable == NULL)
|
||||
{
|
||||
free(context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context->eccBTable = (uint8_t *)malloc(sizeof(uint8_t) * 256);
|
||||
|
||||
if(context->eccBTable == NULL)
|
||||
{
|
||||
free(context->eccFTable);
|
||||
free(context);
|
||||
return NULL;
|
||||
}
|
||||
context->edcTable = (uint32_t *)malloc(sizeof(uint32_t) * 256);
|
||||
|
||||
if(context->edcTable == NULL)
|
||||
{
|
||||
free(context->eccFTable);
|
||||
free(context->eccBTable);
|
||||
free(context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(i = 0; i < 256; i++)
|
||||
{
|
||||
edc = i;
|
||||
j = (uint32_t)((i << 1) ^ ((i & 0x80) == 0x80 ? 0x11D : 0));
|
||||
context->eccFTable[i] = (uint8_t)j;
|
||||
context->eccBTable[i ^ j] = (uint8_t)i;
|
||||
for(j = 0; j < 8; j++)
|
||||
edc = (edc >> 1) ^ ((edc & 1) > 0 ? 0xD8018001 : 0);
|
||||
context->edcTable[i] = edc;
|
||||
}
|
||||
|
||||
context->initedEdc = true;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
bool ecc_cd_is_suffix_correct(void *context, const uint8_t *sector)
|
||||
{
|
||||
CdEccContext *ctx;
|
||||
uint32_t storedEdc, edc, calculatedEdc;
|
||||
int size, pos;
|
||||
|
||||
if(context == NULL || sector == NULL)
|
||||
return false;
|
||||
|
||||
ctx = (CdEccContext *)context;
|
||||
|
||||
if(!ctx->initedEdc)
|
||||
return false;
|
||||
|
||||
if(sector[0x814] != 0x00 ||
|
||||
// reserved (8 bytes)
|
||||
sector[0x815] != 0x00 ||
|
||||
sector[0x816] != 0x00 ||
|
||||
sector[0x817] != 0x00 ||
|
||||
sector[0x818] != 0x00 ||
|
||||
sector[0x819] != 0x00 ||
|
||||
sector[0x81A] != 0x00 ||
|
||||
sector[0x81B] != 0x00)
|
||||
return false;
|
||||
|
||||
bool correctEccP = ecc_cd_check(context, sector, sector, 86, 24, 2, 86, sector, 0xC, 0x10, 0x81C);
|
||||
if(!correctEccP)
|
||||
return false;
|
||||
|
||||
bool correctEccQ = ecc_cd_check(context, sector, sector, 52, 43, 86, 88, sector, 0xC, 0x10, 0x81C + 0xAC);
|
||||
if(!correctEccQ)
|
||||
return false;
|
||||
|
||||
storedEdc = sector[0x810]; // TODO: Check casting
|
||||
edc = 0;
|
||||
size = 0x810;
|
||||
pos = 0;
|
||||
for(; size > 0; size--)
|
||||
edc = (edc >> 8) ^ ctx->edcTable[(edc ^ sector[pos++]) & 0xFF];
|
||||
calculatedEdc = edc;
|
||||
|
||||
return calculatedEdc == storedEdc;
|
||||
}
|
||||
|
||||
bool ecc_cd_is_suffix_correct_mode2(void *context, const uint8_t *sector)
|
||||
{
|
||||
CdEccContext *ctx;
|
||||
uint32_t storedEdc, edc, calculatedEdc;
|
||||
int size, pos;
|
||||
uint8_t zeroaddress[4];
|
||||
|
||||
if(context == NULL || sector == NULL)
|
||||
return false;
|
||||
|
||||
ctx = (CdEccContext *)context;
|
||||
|
||||
if(!ctx->initedEdc)
|
||||
return false;
|
||||
|
||||
memset(&zeroaddress, 4, sizeof(uint8_t));
|
||||
|
||||
bool correctEccP = ecc_cd_check(context, zeroaddress, sector, 86, 24, 2, 86, sector, 0, 0x10, 0x81C);
|
||||
if(!correctEccP)
|
||||
return false;
|
||||
|
||||
bool
|
||||
correctEccQ =
|
||||
ecc_cd_check(context, zeroaddress, sector, 52, 43, 86, 88, sector, 0, 0x10, 0x81C + 0xAC);
|
||||
if(!correctEccQ)
|
||||
return false;
|
||||
|
||||
storedEdc = sector[0x818]; // TODO: Check cast
|
||||
edc = 0;
|
||||
size = 0x808;
|
||||
pos = 0x10;
|
||||
for(; size > 0; size--)
|
||||
edc = (edc >> 8) ^ ctx->edcTable[(edc ^ sector[pos++]) & 0xFF];
|
||||
calculatedEdc = edc;
|
||||
|
||||
return calculatedEdc == storedEdc;
|
||||
}
|
||||
|
||||
bool ecc_cd_check(void *context,
|
||||
const uint8_t *address,
|
||||
const uint8_t *data,
|
||||
uint32_t majorCount,
|
||||
uint32_t minorCount,
|
||||
uint32_t majorMult,
|
||||
uint32_t minorInc,
|
||||
const uint8_t *ecc,
|
||||
int32_t addressOffset,
|
||||
int32_t dataOffset,
|
||||
int32_t eccOffset)
|
||||
{
|
||||
CdEccContext *ctx;
|
||||
uint32_t size, major, idx, minor;
|
||||
uint8_t eccA, eccB, temp;
|
||||
|
||||
if(context == NULL || address == NULL || data == NULL || ecc == NULL)
|
||||
return false;
|
||||
|
||||
ctx = (CdEccContext *)context;
|
||||
|
||||
if(!ctx->initedEdc)
|
||||
return false;
|
||||
|
||||
size = majorCount * minorCount;
|
||||
for(major = 0; major < majorCount; major++)
|
||||
{
|
||||
idx = (major >> 1) * majorMult + (major & 1);
|
||||
eccA = 0;
|
||||
eccB = 0;
|
||||
for(minor = 0; minor < minorCount; minor++)
|
||||
{
|
||||
temp = idx < 4 ? address[idx + addressOffset] : data[idx + dataOffset - 4];
|
||||
idx += minorInc;
|
||||
if(idx >= size)
|
||||
idx -= size;
|
||||
eccA ^= temp;
|
||||
eccB ^= temp;
|
||||
eccA = ctx->eccFTable[eccA];
|
||||
}
|
||||
|
||||
eccA = ctx->eccBTable[ctx->eccFTable[eccA] ^ eccB];
|
||||
if(ecc[major + eccOffset] != eccA || ecc[major + majorCount + eccOffset] != (eccA ^ eccB))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ecc_cd_write(void *context,
|
||||
const uint8_t *address,
|
||||
const uint8_t *data,
|
||||
uint32_t majorCount,
|
||||
uint32_t minorCount,
|
||||
uint32_t majorMult,
|
||||
uint32_t minorInc,
|
||||
uint8_t *ecc,
|
||||
int32_t addressOffset,
|
||||
int32_t dataOffset,
|
||||
int32_t eccOffset)
|
||||
{
|
||||
CdEccContext *ctx;
|
||||
uint32_t size, major, idx, minor;
|
||||
uint8_t eccA, eccB, temp;
|
||||
|
||||
if(context == NULL || address == NULL || data == NULL || ecc == NULL)
|
||||
return;
|
||||
|
||||
ctx = (CdEccContext *)context;
|
||||
|
||||
if(!ctx->initedEdc)
|
||||
return;
|
||||
|
||||
size = majorCount * minorCount;
|
||||
for(major = 0; major < majorCount; major++)
|
||||
{
|
||||
idx = (major >> 1) * majorMult + (major & 1);
|
||||
eccA = 0;
|
||||
eccB = 0;
|
||||
|
||||
for(minor = 0; minor < minorCount; minor++)
|
||||
{
|
||||
temp = idx < 4 ? address[idx + addressOffset] : data[idx + dataOffset - 4];
|
||||
idx += minorInc;
|
||||
if(idx >= size)
|
||||
idx -= size;
|
||||
eccA ^= temp;
|
||||
eccB ^= temp;
|
||||
eccA = ctx->eccFTable[eccA];
|
||||
}
|
||||
|
||||
eccA = ctx->eccBTable[ctx->eccFTable[eccA] ^ eccB];
|
||||
ecc[major + eccOffset] = eccA;
|
||||
ecc[major + majorCount + eccOffset] = (eccA ^ eccB);
|
||||
}
|
||||
}
|
||||
|
||||
void ecc_cd_write_sector(void *context,
|
||||
const uint8_t *address,
|
||||
const uint8_t *data,
|
||||
uint8_t *ecc,
|
||||
int32_t addressOffset,
|
||||
int32_t dataOffset,
|
||||
int32_t eccOffset)
|
||||
{
|
||||
ecc_cd_write(context, address, data, 86, 24, 2, 86, ecc, addressOffset, dataOffset, eccOffset); // P
|
||||
ecc_cd_write(context, address, data, 52, 43, 86, 88, ecc, addressOffset, dataOffset, eccOffset + 0xAC); // Q
|
||||
}
|
||||
|
||||
void cd_lba_to_msf(int64_t pos, uint8_t *minute, uint8_t *second, uint8_t *frame)
|
||||
{
|
||||
*minute = (uint8_t)((pos + 150) / 75 / 60);
|
||||
*second = (uint8_t)((pos + 150) / 75 % 60);
|
||||
*frame = (uint8_t)((pos + 150) % 75);
|
||||
}
|
||||
|
||||
|
||||
void ecc_cd_reconstruct_prefix(uint8_t *sector, // must point to a full 2352-byte sector
|
||||
uint8_t type, int64_t 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void ecc_cd_reconstruct(void *context, uint8_t *sector, // must point to a full 2352-byte sector
|
||||
uint8_t type)
|
||||
{
|
||||
uint32_t computedEdc;
|
||||
uint8_t zeroaddress[4];
|
||||
|
||||
CdEccContext *ctx;
|
||||
|
||||
if(context == NULL || sector == NULL)
|
||||
return;
|
||||
|
||||
ctx = (CdEccContext *)context;
|
||||
|
||||
if(!ctx->initedEdc)
|
||||
return;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
//
|
||||
// Compute EDC
|
||||
//
|
||||
case CdMode1:computedEdc = edc_cd_compute(context, 0, sector, 0x810, 0);
|
||||
memcpy(sector + 0x810, &computedEdc, 4);
|
||||
break;
|
||||
case CdMode2Form1:computedEdc = edc_cd_compute(context, 0, sector, 0x808, 0x10);
|
||||
memcpy(sector + 0x818, &computedEdc, 4);
|
||||
break;
|
||||
case CdMode2Form2:computedEdc = edc_cd_compute(context, 0, sector, 0x91C, 0x10);
|
||||
memcpy(sector + 0x92C, &computedEdc, 4);
|
||||
break;
|
||||
default: 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;
|
||||
ecc_cd_write_sector(context, sector, sector, sector, 0xC, 0x10, 0x81C);
|
||||
break;
|
||||
case CdMode2Form1:ecc_cd_write_sector(context, zeroaddress, sector, sector, 0, 0x10, 0x81C);
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
//
|
||||
// Done
|
||||
//
|
||||
}
|
||||
|
||||
uint32_t edc_cd_compute(void *context, uint32_t edc, const uint8_t *src, int size, int pos)
|
||||
{
|
||||
CdEccContext *ctx;
|
||||
|
||||
if(context == NULL || src == NULL)
|
||||
return 0;
|
||||
|
||||
ctx = (CdEccContext *)context;
|
||||
|
||||
if(!ctx->initedEdc)
|
||||
return 0;
|
||||
|
||||
for(; size > 0; size--)
|
||||
edc = (edc >> 8) ^ ctx->edcTable[(edc ^ src[pos++]) & 0xFF];
|
||||
|
||||
return edc;
|
||||
}
|
||||
@@ -1160,7 +1160,9 @@ void *open(const char *filepath)
|
||||
*/
|
||||
|
||||
// TODO: Cache tracks and sessions?
|
||||
// TODO: Initialize ECC for Compact Disc
|
||||
|
||||
// Initialize ECC for Compact Disc
|
||||
ctx->eccCdContext = (CdEccContext *)ecc_cd_init();
|
||||
|
||||
ctx->magic = DIC_MAGIC;
|
||||
ctx->libraryMajorVersion = LIBDICFORMAT_MAJOR_VERSION;
|
||||
|
||||
Reference in New Issue
Block a user