libaaruformat 1.0
Aaru Data Preservation Suite - Format Library
Loading...
Searching...
No Matches
ecc_cd.c
Go to the documentation of this file.
1/*
2 * This file is part of the Aaru Data Preservation Suite.
3 * Copyright (c) 2019-2025 Natalia Portillo.
4 * ECC algorithm from ECM(c) 2002-2011 Neill Corlett
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of the
9 * License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <stdbool.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "aaruformat.h"
26#include "log.h"
27
36{
37 TRACE("Entering aaruf_ecc_cd_init()");
38 CdEccContext *context = NULL;
39 uint32_t edc = 0, i = 0, j = 0;
40
41 TRACE("Creating context");
42 context = (CdEccContext *)malloc(sizeof(CdEccContext));
43
44 if(context == NULL) return NULL;
45
46 TRACE("Allocating memory for ECC F table");
47 context->ecc_f_table = (uint8_t *)malloc(sizeof(uint8_t) * 256);
48
49 if(context->ecc_f_table == NULL)
50 {
51 free(context);
52 return NULL;
53 }
54
55 TRACE("Allocating memory for ECC B table");
56 context->ecc_b_table = (uint8_t *)malloc(sizeof(uint8_t) * 256);
57
58 if(context->ecc_b_table == NULL)
59 {
60 free(context->ecc_f_table);
61 free(context);
62 return NULL;
63 }
64
65 TRACE("Allocating memory for EDC table");
66
67 context->edc_table = (uint32_t *)malloc(sizeof(uint32_t) * 256);
68
69 if(context->edc_table == NULL)
70 {
71 free(context->ecc_f_table);
72 free(context->ecc_b_table);
73 free(context);
74 return NULL;
75 }
76
77 TRACE("Initializing EDC tables");
78 for(i = 0; i < 256; i++)
79 {
80 edc = i;
81 j = i << 1 ^ ((i & 0x80) == 0x80 ? 0x11D : 0);
82 context->ecc_f_table[i] = (uint8_t)j;
83 context->ecc_b_table[i ^ j] = (uint8_t)i;
84 for(j = 0; j < 8; j++) edc = edc >> 1 ^ ((edc & 1) > 0 ? 0xD8018001 : 0);
85 context->edc_table[i] = edc;
86 }
87
88 context->inited_edc = true;
89
90 TRACE("Exiting aaruf_ecc_cd_init()");
91 return context;
92}
93
101bool aaruf_ecc_cd_is_suffix_correct(void *context, const uint8_t *sector)
102{
103 TRACE("Entering aaruf_ecc_cd_is_suffix_correct(%p, %p)", context, sector);
104 uint32_t edc;
105 int size, pos;
106
107 if(context == NULL || sector == NULL)
108 {
109 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() without initialized context");
110 return false;
111 }
112
113 const CdEccContext *ctx = context;
114 if(!ctx->inited_edc)
115 {
116 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() without initialized context");
117 return false;
118 }
119
120 if(sector[0x814] != 0x00 ||
121 // reserved (8 bytes)
122 sector[0x815] != 0x00 || sector[0x816] != 0x00 || sector[0x817] != 0x00 || sector[0x818] != 0x00 ||
123 sector[0x819] != 0x00 || sector[0x81A] != 0x00 || sector[0x81B] != 0x00)
124 {
125 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = false");
126 return false;
127 }
128
129 const bool correct_ecc_p = aaruf_ecc_cd_check(context, sector, sector, 86, 24, 2, 86, sector, 0xC, 0x10, 0x81C);
130 if(!correct_ecc_p)
131 {
132 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = false");
133 return false;
134 }
135
136 const bool correct_ecc_q =
137 aaruf_ecc_cd_check(context, sector, sector, 52, 43, 86, 88, sector, 0xC, 0x10, 0x81C + 0xAC);
138 if(!correct_ecc_q)
139 {
140 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = false");
141 return false;
142 }
143
144 uint32_t stored_edc;
145 memcpy(&stored_edc, sector + 0x810, 4);
146 uint32_t calculated_edc = aaruf_edc_cd_compute(context, 0, sector, 0x810, 0);
147
148 if(stored_edc != calculated_edc)
149 {
150 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = false");
151 return false;
152 }
153
154 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct() = %u == %u", calculated_edc, stored_edc);
155 return calculated_edc == stored_edc;
156}
157
165bool aaruf_ecc_cd_is_suffix_correct_mode2(void *context, const uint8_t *sector)
166{
167 TRACE("Entering aaruf_ecc_cd_is_suffix_correct_mode2(%p, %p)", context, sector);
168 uint32_t edc;
169 int size, pos;
170 uint8_t zeroaddress[4] = {0};
171
172 if(context == NULL || sector == NULL)
173 {
174 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() without initialized context");
175 return false;
176 }
177
178 CdEccContext *ctx = context;
179
180 if(!ctx->inited_edc)
181 {
182 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() without initialized context");
183 return false;
184 }
185
186 const int form2 = sector[0x12] & 0x20;
187
188 const bool correct_ecc_p = aaruf_ecc_cd_check(context, zeroaddress, sector, 86, 24, 2, 86, sector, 0, 0x10, 0x81C);
189 if(!correct_ecc_p)
190 {
191 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() = false");
192 return false;
193 }
194
195 const bool correct_ecc_q =
196 aaruf_ecc_cd_check(context, zeroaddress, sector, 52, 43, 86, 88, sector, 0, 0x10, 0x81C + 0xAC);
197 if(!correct_ecc_q)
198 {
199 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() = false");
200 return false;
201 }
202
203 uint32_t stored_edc;
204 memcpy(&stored_edc, form2 ? sector + 0x92C : sector + 0x818, 4);
205 const uint32_t calculated_edc = aaruf_edc_cd_compute(context, 0, sector, form2 ? 0x91C : 0x808, 0x10);
206
207 TRACE("Exiting aaruf_ecc_cd_is_suffix_correct_mode2() = %u == %u", calculated_edc, stored_edc);
208 return calculated_edc == stored_edc;
209}
210
227bool aaruf_ecc_cd_check(void *context, const uint8_t *address, const uint8_t *data, const uint32_t major_count,
228 const uint32_t minor_count, const uint32_t major_mult, const uint32_t minor_inc,
229 const uint8_t *ecc, const int32_t address_offset, const int32_t data_offset,
230 const int32_t ecc_offset)
231{
232 TRACE("Entering aaruf_ecc_cd_check(%p, %p, %p, %u, %u, %u, %u, %p, %d, %d, %d)", context, address, data,
233 major_count, minor_count, major_mult, minor_inc, ecc, address_offset, data_offset, ecc_offset);
234
235 if(context == NULL || address == NULL || data == NULL || ecc == NULL)
236 {
237 TRACE("Exiting aaruf_ecc_cd_check() with missing data");
238 return false;
239 }
240
241 CdEccContext *ctx = context;
242
243 if(!ctx->inited_edc)
244 {
245 TRACE("Exiting aaruf_ecc_cd_check() without initialized context");
246 return false;
247 }
248
249 uint32_t size = major_count * minor_count;
250 for(uint32_t major = 0; major < major_count; major++)
251 {
252 uint32_t idx = (major >> 1) * major_mult + (major & 1);
253 uint8_t ecc_a = 0;
254 uint8_t ecc_b = 0;
255 for(uint32_t minor = 0; minor < minor_count; minor++)
256 {
257 uint8_t temp = idx < 4 ? address[idx + address_offset] : data[idx + data_offset - 4];
258 idx += minor_inc;
259 if(idx >= size) idx -= size;
260 ecc_a ^= temp;
261 ecc_b ^= temp;
262 ecc_a = ctx->ecc_f_table[ecc_a];
263 }
264
265 ecc_a = ctx->ecc_b_table[ctx->ecc_f_table[ecc_a] ^ ecc_b];
266 if(ecc[major + ecc_offset] != ecc_a || ecc[major + major_count + ecc_offset] != (ecc_a ^ ecc_b))
267 {
268 TRACE("Exiting aaruf_ecc_cd_check() = false, ECC mismatch at major %u", major);
269 return false;
270 }
271 }
272
273 TRACE("Exiting aaruf_ecc_cd_check() = true, ECC matches");
274 return true;
275}
276
292void aaruf_ecc_cd_write(void *context, const uint8_t *address, const uint8_t *data, const uint32_t major_count,
293 const uint32_t minor_count, const uint32_t major_mult, const uint32_t minor_inc, uint8_t *ecc,
294 const int32_t address_offset, const int32_t data_offset, const int32_t ecc_offset)
295{
296 TRACE("Entering aaruf_ecc_cd_write(%p, %p, %p, %u, %u, %u, %u, %p, %d, %d, %d)", context, address, data,
297 major_count, minor_count, major_mult, minor_inc, ecc, address_offset, data_offset, ecc_offset);
298
299 if(context == NULL || address == NULL || data == NULL || ecc == NULL)
300 {
301 TRACE("Exiting aaruf_ecc_cd_write() with missing data");
302 return;
303 }
304
305 CdEccContext *ctx = context;
306
307 if(!ctx->inited_edc)
308 {
309 TRACE("Exiting aaruf_ecc_cd_write() without initialized context");
310 return;
311 }
312
313 uint32_t size = major_count * minor_count;
314 for(uint32_t major = 0; major < major_count; major++)
315 {
316 uint32_t idx = (major >> 1) * major_mult + (major & 1);
317 uint8_t ecc_a = 0;
318 uint8_t ecc_b = 0;
319
320 for(uint32_t minor = 0; minor < minor_count; minor++)
321 {
322 uint8_t temp = idx < 4 ? address[idx + address_offset] : data[idx + data_offset - 4];
323 idx += minor_inc;
324 if(idx >= size) idx -= size;
325 ecc_a ^= temp;
326 ecc_b ^= temp;
327 ecc_a = ctx->ecc_f_table[ecc_a];
328 }
329
330 ecc_a = ctx->ecc_b_table[ctx->ecc_f_table[ecc_a] ^ ecc_b];
331 ecc[major + ecc_offset] = ecc_a;
332 ecc[major + major_count + ecc_offset] = ecc_a ^ ecc_b;
333 }
334
335 TRACE("Exiting aaruf_ecc_cd_write()");
336}
337
349void aaruf_ecc_cd_write_sector(void *context, const uint8_t *address, const uint8_t *data, uint8_t *ecc,
350 const int32_t address_offset, const int32_t data_offset, const int32_t ecc_offset)
351{
352 TRACE("Entering aaruf_ecc_cd_write_sector(%p, %p, %p, %p, %d, %d, %d)", context, address, data, ecc, address_offset,
353 data_offset, ecc_offset);
354
355 aaruf_ecc_cd_write(context, address, data, 86, 24, 2, 86, ecc, address_offset, data_offset, ecc_offset); // P
356 aaruf_ecc_cd_write(context, address, data, 52, 43, 86, 88, ecc, address_offset, data_offset,
357 ecc_offset + 0xAC); // Q
358
359 TRACE("Exiting aaruf_ecc_cd_write_sector()");
360}
361
370void aaruf_cd_lba_to_msf(const int64_t pos, uint8_t *minute, uint8_t *second, uint8_t *frame)
371{
372 TRACE("Entering aaruf_cd_lba_to_msf(%lld, %p, %p, %p)", pos, minute, second, frame);
373
374 *minute = (uint8_t)((pos + 150) / 75 / 60);
375 *second = (uint8_t)((pos + 150) / 75 % 60);
376 *frame = (uint8_t)((pos + 150) % 75);
377
378 TRACE("Exiting aaruf_cd_lba_to_msf() = %u:%u:%u", *minute, *second, *frame);
379}
380
388void aaruf_ecc_cd_reconstruct_prefix(uint8_t *sector, const uint8_t type, const int64_t lba)
389{
390 TRACE("Entering aaruf_ecc_cd_reconstruct_prefix(%p, %u, %lld)", sector, type, lba);
391
392 uint8_t minute, second, frame;
393
394 if(sector == NULL) return;
395
396 //
397 // Sync
398 //
399 sector[0x000] = 0x00;
400 sector[0x001] = 0xFF;
401 sector[0x002] = 0xFF;
402 sector[0x003] = 0xFF;
403 sector[0x004] = 0xFF;
404 sector[0x005] = 0xFF;
405 sector[0x006] = 0xFF;
406 sector[0x007] = 0xFF;
407 sector[0x008] = 0xFF;
408 sector[0x009] = 0xFF;
409 sector[0x00A] = 0xFF;
410 sector[0x00B] = 0x00;
411
412 aaruf_cd_lba_to_msf(lba, &minute, &second, &frame);
413
414 sector[0x00C] = (uint8_t)(((minute / 10) << 4) + minute % 10);
415 sector[0x00D] = (uint8_t)(((second / 10) << 4) + second % 10);
416 sector[0x00E] = (uint8_t)(((frame / 10) << 4) + frame % 10);
417
418 switch((TrackType)type)
419 {
420 case CdMode1:
421 //
422 // Mode
423 //
424 sector[0x00F] = 0x01;
425 break;
426 case CdMode2Form1:
427 case CdMode2Form2:
428 case CdMode2Formless:
429 //
430 // Mode
431 //
432 sector[0x00F] = 0x02;
433 //
434 // Flags
435 //
436 sector[0x010] = sector[0x014];
437 sector[0x011] = sector[0x015];
438 sector[0x012] = sector[0x016];
439 sector[0x013] = sector[0x017];
440 break;
441 default:
442 return;
443 }
444
445 TRACE("Exiting aaruf_ecc_cd_reconstruct_prefix()");
446}
447
455void aaruf_ecc_cd_reconstruct(void *context, uint8_t *sector, const uint8_t type)
456{
457 TRACE("Entering aaruf_ecc_cd_reconstruct(%p, %p, %u)", context, sector, type);
458
459 uint32_t computed_edc;
460 uint8_t zeroaddress[4];
461
462 if(context == NULL || sector == NULL)
463 {
464 TRACE("Exiting aaruf_ecc_cd_reconstruct() with missing data");
465 return;
466 }
467
468 CdEccContext *ctx = context;
469
470 if(!ctx->inited_edc)
471 {
472 TRACE("Exiting aaruf_ecc_cd_reconstruct() without initialized context");
473 return;
474 }
475
476 switch(type)
477 {
478 //
479 // Compute EDC
480 //
481 case CdMode1:
482 computed_edc = aaruf_edc_cd_compute(context, 0, sector, 0x810, 0);
483 memcpy(sector + 0x810, &computed_edc, 4);
484 break;
485 case CdMode2Form1:
486 computed_edc = aaruf_edc_cd_compute(context, 0, sector, 0x808, 0x10);
487 memcpy(sector + 0x818, &computed_edc, 4);
488 break;
489 case CdMode2Form2:
490 computed_edc = aaruf_edc_cd_compute(context, 0, sector, 0x91C, 0x10);
491 memcpy(sector + 0x92C, &computed_edc, 4);
492 break;
493 default:
494 TRACE("Exiting aaruf_ecc_cd_reconstruct() with unknown type %u", type);
495 return;
496 }
497
498 memset(&zeroaddress, 0, 4);
499
500 switch(type)
501 {
502 //
503 // Compute ECC
504 //
505 case CdMode1:
506 //
507 // Reserved
508 //
509 sector[0x814] = 0x00;
510 sector[0x815] = 0x00;
511 sector[0x816] = 0x00;
512 sector[0x817] = 0x00;
513 sector[0x818] = 0x00;
514 sector[0x819] = 0x00;
515 sector[0x81A] = 0x00;
516 sector[0x81B] = 0x00;
517 aaruf_ecc_cd_write_sector(context, sector, sector, sector, 0xC, 0x10, 0x81C);
518 break;
519 case CdMode2Form1:
520 aaruf_ecc_cd_write_sector(context, zeroaddress, sector, sector, 0, 0x10, 0x81C);
521 break;
522 default:
523 TRACE("Exiting aaruf_ecc_cd_reconstruct() with unknown type %u", type);
524 return;
525 }
526
527 //
528 // Done
529 //
530 TRACE("Exiting aaruf_ecc_cd_reconstruct()");
531}
532
543uint32_t aaruf_edc_cd_compute(void *context, uint32_t edc, const uint8_t *src, int size, int pos)
544{
545 TRACE("Entering aaruf_edc_cd_compute(%p, %u, %p, %d, %d)", context, edc, src, size, pos);
546
547 if(context == NULL || src == NULL)
548 {
549 TRACE("Exiting aaruf_edc_cd_compute() with missing data");
550 return 0;
551 }
552
553 CdEccContext *ctx = context;
554
555 if(!ctx->inited_edc)
556 {
557 TRACE("Exiting aaruf_edc_cd_compute() without initialized context");
558 return 0;
559 }
560
561 for(; size > 0; size--) edc = edc >> 8 ^ ctx->edc_table[(edc ^ src[pos++]) & 0xFF];
562
563 TRACE("Exiting aaruf_edc_cd_compute() = 0x%08X", edc);
564 return edc;
565}
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:227
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:292
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:370
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:455
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:543
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:388
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:349
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:165
void * aaruf_ecc_cd_init()
Initializes a Compact Disc ECC context.
Definition ecc_cd.c:35
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:101
TrackType
Track (partitioning element) types for optical media.
Definition enums.h:194
@ CdMode1
Compact Disc Mode 1 data track.
Definition enums.h:197
@ CdMode2Form2
Compact Disc Mode 2 Form 2 data track.
Definition enums.h:200
@ CdMode2Form1
Compact Disc Mode 2 Form 1 data track.
Definition enums.h:199
@ CdMode2Formless
Compact Disc Mode 2 (formless) data track.
Definition enums.h:198
#define TRACE(fmt,...)
Definition log.h:25
Lookup tables and state for Compact Disc EDC/ECC (P/Q) regeneration / verification.
Definition context.h:86
uint8_t * ecc_f_table
Forward (F) ECC table.
Definition context.h:89
bool inited_edc
True once EDC/ECC tables have been initialized.
Definition context.h:87
uint32_t * edc_table
EDC (CRC) lookup table.
Definition context.h:90
uint8_t * ecc_b_table
Backward (B) ECC table (allocated, size implementation-defined).
Definition context.h:88