ECM (Error Code Modeler) encode / decode utilities
Introduction
The ECM format allows you to reduce the size of a typical CD image file (BIN, CDI, NRG, CCD, or any other format that uses raw sectors; results may vary).
It works by eliminating the Error Correction/Detection Codes (ECC/EDC) from each sector whenever possible. The encoder automatically adjusts to different sector types and automatically skips any headers it encounters.
The results will vary depending on how much redundant ECC/EDC data is present. Note that for "cooked" ISO files, there will be no reduction.
Setup / Usage
Compile ecm.c and unecm.c if necessary, or use the included Win32 EXE files.
Run ECM with no parameters to see a simple usage reference:
usage: ecm cdimagefile [ecmfile]
Where "cdimagefile" is the name of the CD image file, and "ecmfile" (optional) is the name of the ECM file. If you don't specify ecmfile, it defaults to cdimagefile plus a .ecm suffix.
UNECM works the same way, but in reverse:
usage: unecm ecmfile [outputfile]
"ecmfile" must end in .ecm. If outputfile is not specified, it defaults to ecmfile minus the .ecm suffix.
Thanks to
ProtoCat for inspiring me to write this.
Where to find me
email: corlett@lfx.org
www: http://lfx.org/~corlett/
Introduction
For many people who distribute images of CD-ROMs, the common practice is to create a raw copy in either BIN/CUE (CDRWin), CDI (DiscJuggler), NRG (Nero), or CCD (CloneCD) format, and then compress that image using an archive format such as RAR. The goal is to reduce the CD image to the smallest possible size for quicker Internet transfers. However, there is a major problem with this technique.
Raw CD images contain both the useful sector data as well as subcodes, including ECC and EDC (Error Correction/Detection Codes). In many cases, it's necessary to include all subcode information to ensure a complete and accurate CD image. However, most raw CD images include a large number of "plain" sectors, for which the subcode data is merely redundant. Since the RAR format includes CRC verification and the TCP/IP transport layer also includes error detection, there is no purpose to transferring this redundant data.
Making the problem worse, the ECC/EDC data in a typical CD sector is very poorly modeled by typical Lempel-Ziv compression schemes (such as the one used by RAR). This means that, for practical purposes, ECC/EDC data is uncompressible.
However, with a simple algorithm, the ECC/EDC data can be reconstructed for a typical CD sector. What's needed is a compressed file format which uses the ECC/EDC algorithms as a model to eliminate the redundant data.
This is ECM, the Error Code Modeler format.
Sector types
There are three basic types of CD data sectors:
Type #1: Mode 1
-----------------------------------------------------
0 1 2 3 4 5 6 7 8 9 A B C D E F
0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 01
0010h [---DATA...
...
0800h ...DATA---]
0810h [---EDC---] 00 00 00 00 00 00 00 00 [---ECC...
...
0920h ...ECC---]
-----------------------------------------------------
Type #2: Mode 2 (XA), form 1
-----------------------------------------------------
0 1 2 3 4 5 6 7 8 9 A B C D E F
0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 02
0010h [--FLAGS--] [--FLAGS--] [---DATA...
...
0810h ...DATA---] [---EDC---] [---ECC...
...
0920h ...ECC---]
-----------------------------------------------------
Type #3: Mode 2 (XA), form 2
-----------------------------------------------------
0 1 2 3 4 5 6 7 8 9 A B C D E F
0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 02
0010h [--FLAGS--] [--FLAGS--] [---DATA...
...
0920h ...DATA---] [---EDC---]
-----------------------------------------------------
Key:
- ADDR: Sector address, encoded as minutes:seconds:frames in BCD
- FLAGS: Used in Mode 2 (XA) sectors describing the type of sector; repeated twice for redundancy
- DATA: Area of the sector which contains the actual data itself
- EDC: Error Detection Code
- ECC: Error Correction Code
The ECM format uses these three basic types to model CD sectors.
The ECM file format
The first 4 bytes are the magic identifier: 45 43 4D 00, or "ECM".
After this comes an arbitrary (can be infinitely long) number of records.
Each record has 3 parts: Type (0...3), Count (1...2^31-1), and Data.
- Type 0: Literal bytes follow; length is indicated by Count.
- Type 1: Sectors of type #1 follow; Count tells how many.
- Type 2: Sectors of type #2 follow; Count tells how many.
- Type 3: Sectors of type #3 follow; Count tells how many.
The Type and Count are encoded first, in the following format.
(Second through fifth bytes are OPTIONAL.)
first byte second byte third byte fourth byte fifth byte
AaaaaaTT Bbbbbbbb Cccccccc Dddddddd 00eeeeee
- T - Type.
- a - Bits 0-4 of Count.
- A - Set if the second byte exists.
- b - Bits 5-11 of Count.
- B - Set if the third byte exists.
- c - Bits 12-18 of Count.
- C - Set if the fourth byte exists.
- d - Bits 19-25 of Count.
- D - Set if the fifth byte exists.
- e - Bits 26-31 of Count.
NOTE: The Count value that is encoded is the actual Count minus 1.
Type 0 and a count of exactly 2^32 (meaning it's encoded as FFFFFFFF) indicates that there are no more records. Immediately following this marker is a 4-byte EDC for the entire original (unencoded) file. This is computed using the same algorithm as the EDC for a CD sector. After this, the ECM file ends.
Counts of 2^31 and higher are invalid.
Sector type #1
Stored in the ECM file as follows:
3 bytes - ADDR
2048 bytes - DATA
This expands to a complete 2352-byte Mode 1 sector.
The sync, reserved, mode (01), EDC, and ECC bytes are reconstructed upon decoding.
Sector type #2
Stored in the ECM file as follows:
4 bytes - FLAGS
2048 bytes - DATA
This expands to a 2336-byte Mode 2 Form 1 sector. The sync, address, and mode bytes are NOT included.
The redundant flags, EDC, and ECC bytes are reconstructed upon decoding.
Sector type #3
Stored in the ECM file as follows:
4 bytes - FLAGS
2324 bytes - DATA
This expands to a 2336-byte Mode 2 Form 2 sector. The sync, address, and mode bytes are NOT included.
The redundant flags and EDC bytes are reconstructed upon decoding.
unecm.c
/***************************************************************************/
/*
** UNECM - Decoder for ECM (Error Code Modeler) format.
** Version 1.0
** Copyright (C) 2002 Neill Corlett
**
** 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.
*/
/***************************************************************************/
/*
** Portability notes:
**
** - Assumes a 32-bit or higher integer size
** - No assumptions about byte order
** - No assumptions about struct packing
** - No unaligned memory access
*/
/***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/***************************************************************************/
void banner(void) {
fprintf(stderr,
"UNECM - Decoder for Error Code Modeler format v1.0\n"
"Copyright (C) 2002 Neill Corlett\n\n"
);
}
/***************************************************************************/
/* Data types */
#define ecc_uint8 unsigned char
#define ecc_uint16 unsigned short
#define ecc_uint32 unsigned
/* LUTs used for computing ECC/EDC */
static ecc_uint8 ecc_f_lut[256];
static ecc_uint8 ecc_b_lut[256];
static ecc_uint32 edc_lut[256];
/* Init routine */
static void eccedc_init(void) {
ecc_uint32 i, j, edc;
for(i = 0; i < 256; i++) {
j = (i << 1) ^ (i & 0x80 ? 0x11D : 0);
ecc_f_lut[i] = j;
ecc_b_lut[i ^ j] = i;
edc = i;
for(j = 0; j < 8; j++) edc = (edc >> 1) ^ (edc & 1 ? 0xD8018001 : 0);
edc_lut[i] = edc;
}
}
/***************************************************************************/
/*
** Compute EDC for a block
*/
ecc_uint32 edc_partial_computeblock(
ecc_uint32 edc,
const ecc_uint8 *src,
ecc_uint16 size
) {
while(size--) edc = (edc >> 8) ^ edc_lut[(edc ^ (*src++)) & 0xFF];
return edc;
}
void edc_computeblock(
const ecc_uint8 *src,
ecc_uint16 size,
ecc_uint8 *dest
) {
ecc_uint32 edc = edc_partial_computeblock(0, src, size);
dest[0] = (edc >> 0) & 0xFF;
dest[1] = (edc >> 8) & 0xFF;
dest[2] = (edc >> 16) & 0xFF;
dest[3] = (edc >> 24) & 0xFF;
}
/***************************************************************************/
/*
** Compute ECC for a block (can do either P or Q)
*/
static void ecc_computeblock(
ecc_uint8 *src,
ecc_uint32 major_count,
ecc_uint32 minor_count,
ecc_uint32 major_mult,
ecc_uint32 minor_inc,
ecc_uint8 *dest
) {
ecc_uint32 size = major_count * minor_count;
ecc_uint32 major, minor;
for(major = 0; major < major_count; major++) {
ecc_uint32 index = (major >> 1) * major_mult + (major & 1);
ecc_uint8 ecc_a = 0;
ecc_uint8 ecc_b = 0;
for(minor = 0; minor < minor_count; minor++) {
ecc_uint8 temp = src[index];
index += minor_inc;
if(index >= size) index -= size;
ecc_a ^= temp;
ecc_b ^= temp;
ecc_a = ecc_f_lut[ecc_a];
}
ecc_a = ecc_b_lut[ecc_f_lut[ecc_a] ^ ecc_b];
dest[major ] = ecc_a;
dest[major + major_count] = ecc_a ^ ecc_b;
}
}
/*
** Generate ECC P and Q codes for a block
*/
static void ecc_generate(
ecc_uint8 *sector,
int zeroaddress
) {
ecc_uint8 address[4], i;
/* Save the address and zero it out */
if(zeroaddress) for(i = 0; i < 4; i++) {
address[i] = sector[12 + i];
sector[12 + i] = 0;
}
/* Compute ECC P code */
ecc_computeblock(sector + 0xC, 86, 24, 2, 86, sector + 0x81C);
/* Compute ECC Q code */
ecc_computeblock(sector + 0xC, 52, 43, 86, 88, sector + 0x8C8);
/* Restore the address */
if(zeroaddress) for(i = 0; i < 4; i++) sector[12 + i] = address[i];
}
/***************************************************************************/
/*
** Generate ECC/EDC information for a sector (must be 2352 = 0x930 bytes)
** Returns 0 on success
*/
void eccedc_generate(ecc_uint8 *sector, int type) {
ecc_uint32 i;
switch(type) {
case 1: /* Mode 1 */
/* Compute EDC */
edc_computeblock(sector + 0x00, 0x810, sector + 0x810);
/* Write out zero bytes */
for(i = 0; i < 8; i++) sector[0x814 + i] = 0;
/* Generate ECC P/Q codes */
ecc_generate(sector, 0);
break;
case 2: /* Mode 2 form 1 */
/* Compute EDC */
edc_computeblock(sector + 0x10, 0x808, sector + 0x818);
/* Generate ECC P/Q codes */
ecc_generate(sector, 1);
break;
case 3: /* Mode 2 form 2 */
/* Compute EDC */
edc_computeblock(sector + 0x10, 0x91C, sector + 0x92C);
break;
}
}
/***************************************************************************/
unsigned mycounter;
unsigned mycounter_total;
void resetcounter(unsigned total) {
mycounter = 0;
mycounter_total = total;
}
void setcounter(unsigned n) {
if((n >> 20) != (mycounter >> 20)) {
unsigned a = (n+64)/128;
unsigned d = (mycounter_total+64)/128;
if(!d) d = 1;
fprintf(stderr, "Decoding (%02d%%)\r", (100*a) / d);
}
mycounter = n;
}
int unecmify(
FILE *in,
FILE *out
) {
unsigned checkedc = 0;
unsigned char sector[2352];
unsigned type;
unsigned num;
fseek(in, 0, SEEK_END);
resetcounter(ftell(in));
fseek(in, 0, SEEK_SET);
if(
(fgetc(in) != 'E') ||
(fgetc(in) != 'C') ||
(fgetc(in) != 'M') ||
(fgetc(in) != 0x00)
) {
fprintf(stderr, "Header not found!\n");
goto corrupt;
}
for(;;) {
int c = fgetc(in);
int bits = 5;
if(c == EOF) goto uneof;
type = c & 3;
num = (c >> 2) & 0x1F;
while(c & 0x80) {
c = fgetc(in);
if(c == EOF) goto uneof;
num |= ((unsigned)(c & 0x7F)) << bits;
bits += 7;
}
if(num == 0xFFFFFFFF) break;
num++;
if(num >= 0x80000000) goto corrupt;
if(!type) {
while(num) {
int b = num;
if(b > 2352) b = 2352;
if(fread(sector, 1, b, in) != b) goto uneof;
checkedc = edc_partial_computeblock(checkedc, sector, b);
fwrite(sector, 1, b, out);
num -= b;
setcounter(ftell(in));
}
} else {
while(num--) {
memset(sector, 0, sizeof(sector));
memset(sector + 1, 0xFF, 10);
switch(type) {
case 1:
sector[0x0F] = 0x01;
if(fread(sector + 0x00C, 1, 0x003, in) != 0x003) goto uneof;
if(fread(sector + 0x010, 1, 0x800, in) != 0x800) goto uneof;
eccedc_generate(sector, 1);
checkedc = edc_partial_computeblock(checkedc, sector, 2352);
fwrite(sector, 2352, 1, out);
setcounter(ftell(in));
break;
case 2:
sector[0x0F] = 0x02;
if(fread(sector + 0x014, 1, 0x804, in) != 0x804) goto uneof;
sector[0x10] = sector[0x14];
sector[0x11] = sector[0x15];
sector[0x12] = sector[0x16];
sector[0x13] = sector[0x17];
eccedc_generate(sector, 2);
checkedc = edc_partial_computeblock(checkedc, sector + 0x10, 2336);
fwrite(sector + 0x10, 2336, 1, out);
setcounter(ftell(in));
break;
case 3:
sector[0x0F] = 0x02;
if(fread(sector + 0x014, 1, 0x918, in) != 0x918) goto uneof;
sector[0x10] = sector[0x14];
sector[0x11] = sector[0x15];
sector[0x12] = sector[0x16];
sector[0x13] = sector[0x17];
eccedc_generate(sector, 3);
checkedc = edc_partial_computeblock(checkedc, sector + 0x10, 2336);
fwrite(sector + 0x10, 2336, 1, out);
setcounter(ftell(in));
break;
}
}
}
}
if(fread(sector, 1, 4, in) != 4) goto uneof;
fprintf(stderr, "Decoded %ld bytes -> %ld bytes\n", ftell(in), ftell(out));
if(
(sector[0] != ((checkedc >> 0) & 0xFF)) ||
(sector[1] != ((checkedc >> 8) & 0xFF)) ||
(sector[2] != ((checkedc >> 16) & 0xFF)) ||
(sector[3] != ((checkedc >> 24) & 0xFF))
) {
fprintf(stderr, "EDC error (%08X, should be %02X%02X%02X%02X)\n",
checkedc,
sector[3],
sector[2],
sector[1],
sector[0]
);
goto corrupt;
}
fprintf(stderr, "Done; file is OK\n");
return 0;
uneof:
fprintf(stderr, "Unexpected EOF!\n");
corrupt:
fprintf(stderr, "Corrupt ECM file!\n");
return 1;
}
/***************************************************************************/
int main(int argc, char **argv) {
FILE *fin, *fout;
char *infilename;
char *outfilename;
banner();
/*
** Initialize the ECC/EDC tables
*/
eccedc_init();
/*
** Check command line
*/
if((argc != 2) && (argc != 3)) {
fprintf(stderr, "usage: %s ecmfile [outputfile]\n", argv[0]);
return 1;
}
/*
** Verify that the input filename is valid
*/
infilename = argv[1];
if(strlen(infilename) < 5) {
fprintf(stderr, "filename '%s' is too short\n", infilename);
return 1;
}
if(strcasecmp(infilename + strlen(infilename) - 4, ".ecm")) {
fprintf(stderr, "filename must end in .ecm\n");
return 1;
}
/*
** Figure out what the output filename should be
*/
if(argc == 3) {
outfilename = argv[2];
} else {
outfilename = malloc(strlen(infilename) - 3);
if(!outfilename) abort();
memcpy(outfilename, infilename, strlen(infilename) - 4);
outfilename[strlen(infilename) - 4] = 0;
}
fprintf(stderr, "Decoding %s to %s.\n", infilename, outfilename);
/*
** Open both files
*/
fin = fopen(infilename, "rb");
if(!fin) {
perror(infilename);
return 1;
}
fout = fopen(outfilename, "wb");
if(!fout) {
perror(outfilename);
fclose(fin);
return 1;
}
/*
** Decode
*/
unecmify(fin, fout);
/*
** Close everything
*/
fclose(fout);
fclose(fin);
return 0;
}
ecm.c
/***************************************************************************/
/*
** ECM - Encoder for ECM (Error Code Modeler) format.
** Version 1.0
** Copyright (C) 2002 Neill Corlett
**
** 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.
*/
/***************************************************************************/
/*
** Portability notes:
**
** - Assumes a 32-bit or higher integer size
** - No assumptions about byte order
** - No assumptions about struct packing
** - No unaligned memory access
*/
/***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/***************************************************************************/
void banner(void) {
fprintf(stderr,
"ECM - Encoder for Error Code Modeler format v1.0\n"
"Copyright (C) 2002 Neill Corlett\n\n"
);
}
/***************************************************************************/
/* Data types */
#define ecc_uint8 unsigned char
#define ecc_uint16 unsigned short
#define ecc_uint32 unsigned
/* LUTs used for computing ECC/EDC */
static ecc_uint8 ecc_f_lut[256];
static ecc_uint8 ecc_b_lut[256];
static ecc_uint32 edc_lut[256];
/* Init routine */
static void eccedc_init(void) {
ecc_uint32 i, j, edc;
for(i = 0; i < 256; i++) {
j = (i << 1) ^ (i & 0x80 ? 0x11D : 0);
ecc_f_lut[i] = j;
ecc_b_lut[i ^ j] = i;
edc = i;
for(j = 0; j < 8; j++) edc = (edc >> 1) ^ (edc & 1 ? 0xD8018001 : 0);
edc_lut[i] = edc;
}
}
/***************************************************************************/
/*
** Compute EDC for a block
*/
ecc_uint32 edc_computeblock(
ecc_uint32 edc,
const ecc_uint8 *src,
ecc_uint16 size
) {
while(size--) edc = (edc >> 8) ^ edc_lut[(edc ^ (*src++)) & 0xFF];
return edc;
}
/***************************************************************************/
/*
** Compute ECC for a block (can do either P or Q)
*/
static int ecc_computeblock(
ecc_uint8 *src,
ecc_uint32 major_count,
ecc_uint32 minor_count,
ecc_uint32 major_mult,
ecc_uint32 minor_inc,
ecc_uint8 *dest
) {
ecc_uint32 size = major_count * minor_count;
ecc_uint32 major, minor;
for(major = 0; major < major_count; major++) {
ecc_uint32 index = (major >> 1) * major_mult + (major & 1);
ecc_uint8 ecc_a = 0;
ecc_uint8 ecc_b = 0;
for(minor = 0; minor < minor_count; minor++) {
ecc_uint8 temp = src[index];
index += minor_inc;
if(index >= size) index -= size;
ecc_a ^= temp;
ecc_b ^= temp;
ecc_a = ecc_f_lut[ecc_a];
}
ecc_a = ecc_b_lut[ecc_f_lut[ecc_a] ^ ecc_b];
if(dest[major ] != (ecc_a )) return 0;
if(dest[major + major_count] != (ecc_a ^ ecc_b)) return 0;
}
return 1;
}
/*
** Generate ECC P and Q codes for a block
*/
static int ecc_generate(
ecc_uint8 *sector,
int zeroaddress,
ecc_uint8 *dest
) {
int r;
ecc_uint8 address[4], i;
/* Save the address and zero it out */
if(zeroaddress) for(i = 0; i < 4; i++) {
address[i] = sector[12 + i];
sector[12 + i] = 0;
}
/* Compute ECC P code */
if(!(ecc_computeblock(sector + 0xC, 86, 24, 2, 86, dest + 0x81C - 0x81C))) {
if(zeroaddress) for(i = 0; i < 4; i++) sector[12 + i] = address[i];
return 0;
}
/* Compute ECC Q code */
r = ecc_computeblock(sector + 0xC, 52, 43, 86, 88, dest + 0x8C8 - 0x81C);
/* Restore the address */
if(zeroaddress) for(i = 0; i < 4; i++) sector[12 + i] = address[i];
return r;
}
/***************************************************************************/
/*
** sector types:
** 00 - literal bytes
** 01 - 2352 mode 1 predict sync, mode, reserved, edc, ecc
** 02 - 2336 mode 2 form 1 predict redundant flags, edc, ecc
** 03 - 2336 mode 2 form 2 predict redundant flags, edc
*/
int check_type(unsigned char *sector, int canbetype1) {
int canbetype2 = 1;
int canbetype3 = 1;
ecc_uint32 myedc;
/* Check for mode 1 */
if(canbetype1) {
if(
(sector[0x00] != 0x00) ||
(sector[0x01] != 0xFF) ||
(sector[0x02] != 0xFF) ||
(sector[0x03] != 0xFF) ||
(sector[0x04] != 0xFF) ||
(sector[0x05] != 0xFF) ||
(sector[0x06] != 0xFF) ||
(sector[0x07] != 0xFF) ||
(sector[0x08] != 0xFF) ||
(sector[0x09] != 0xFF) ||
(sector[0x0A] != 0xFF) ||
(sector[0x0B] != 0x00) ||
(sector[0x0F] != 0x01) ||
(sector[0x814] != 0x00) ||
(sector[0x815] != 0x00) ||
(sector[0x816] != 0x00) ||
(sector[0x817] != 0x00) ||
(sector[0x818] != 0x00) ||
(sector[0x819] != 0x00) ||
(sector[0x81A] != 0x00) ||
(sector[0x81B] != 0x00)
) {
canbetype1 = 0;
}
}
/* Check for mode 2 */
if(
(sector[0x0] != sector[0x4]) ||
(sector[0x1] != sector[0x5]) ||
(sector[0x2] != sector[0x6]) ||
(sector[0x3] != sector[0x7])
) {
canbetype2 = 0;
canbetype3 = 0;
if(!canbetype1) return 0;
}
/* Check EDC */
myedc = edc_computeblock(0, sector, 0x808);
if(canbetype2) if(
(sector[0x808] != ((myedc >> 0) & 0xFF)) ||
(sector[0x809] != ((myedc >> 8) & 0xFF)) ||
(sector[0x80A] != ((myedc >> 16) & 0xFF)) ||
(sector[0x80B] != ((myedc >> 24) & 0xFF))
) {
canbetype2 = 0;
}
myedc = edc_computeblock(myedc, sector + 0x808, 8);
if(canbetype1) if(
(sector[0x810] != ((myedc >> 0) & 0xFF)) ||
(sector[0x811] != ((myedc >> 8) & 0xFF)) ||
(sector[0x812] != ((myedc >> 16) & 0xFF)) ||
(sector[0x813] != ((myedc >> 24) & 0xFF))
) {
canbetype1 = 0;
}
myedc = edc_computeblock(myedc, sector + 0x810, 0x10C);
if(canbetype3) if(
(sector[0x91C] != ((myedc >> 0) & 0xFF)) ||
(sector[0x91D] != ((myedc >> 8) & 0xFF)) ||
(sector[0x91E] != ((myedc >> 16) & 0xFF)) ||
(sector[0x91F] != ((myedc >> 24) & 0xFF))
) {
canbetype3 = 0;
}
/* Check ECC */
if(canbetype1) { if(!(ecc_generate(sector , 0, sector + 0x81C))) { canbetype1 = 0; } }
if(canbetype2) { if(!(ecc_generate(sector - 0x10, 1, sector + 0x80C))) { canbetype2 = 0; } }
if(canbetype1) return 1;
if(canbetype2) return 2;
if(canbetype3) return 3;
return 0;
}
/***************************************************************************/
/*
** Encode a type/count combo
*/
void write_type_count(
FILE *out,
unsigned type,
unsigned count
) {
count--;
fputc(((count >= 32) << 7) | ((count & 31) << 2) | type, out);
count >>= 5;
while(count) {
fputc(((count >= 128) << 7) | (count & 127), out);
count >>= 7;
}
}
/***************************************************************************/
unsigned mycounter_analyze;
unsigned mycounter_encode;
unsigned mycounter_total;
void resetcounter(unsigned total) {
mycounter_analyze = 0;
mycounter_encode = 0;
mycounter_total = total;
}
void setcounter_analyze(unsigned n) {
if((n >> 20) != (mycounter_analyze >> 20)) {
unsigned a = (n+64)/128;
unsigned e = (mycounter_encode+64)/128;
unsigned d = (mycounter_total+64)/128;
if(!d) d = 1;
fprintf(stderr, "Analyzing (%02d%%) Encoding (%02d%%)\r",
(100*a) / d, (100*e) / d
);
}
mycounter_analyze = n;
}
void setcounter_encode(unsigned n) {
if((n >> 20) != (mycounter_encode >> 20)) {
unsigned a = (mycounter_analyze+64)/128;
unsigned e = (n+64)/128;
unsigned d = (mycounter_total+64)/128;
if(!d) d = 1;
fprintf(stderr, "Analyzing (%02d%%) Encoding (%02d%%)\r",
(100*a) / d, (100*e) / d
);
}
mycounter_encode = n;
}
/***************************************************************************/
/*
** Encode a run of sectors/literals of the same type
*/
unsigned in_flush(
unsigned edc,
unsigned type,
unsigned count,
FILE *in,
FILE *out
) {
unsigned char buf[2352];
write_type_count(out, type, count);
if(!type) {
while(count) {
unsigned b = count;
if(b > 2352) b = 2352;
fread(buf, 1, b, in);
edc = edc_computeblock(edc, buf, b);
fwrite(buf, 1, b, out);
count -= b;
setcounter_encode(ftell(in));
}
return edc;
}
while(count--) {
switch(type) {
case 1:
fread(buf, 1, 2352, in);
edc = edc_computeblock(edc, buf, 2352);
fwrite(buf + 0x00C, 1, 0x003, out);
fwrite(buf + 0x010, 1, 0x800, out);
setcounter_encode(ftell(in));
break;
case 2:
fread(buf, 1, 2336, in);
edc = edc_computeblock(edc, buf, 2336);
fwrite(buf + 0x004, 1, 0x804, out);
setcounter_encode(ftell(in));
break;
case 3:
fread(buf, 1, 2336, in);
edc = edc_computeblock(edc, buf, 2336);
fwrite(buf + 0x004, 1, 0x918, out);
setcounter_encode(ftell(in));
break;
}
}
return edc;
}
/***************************************************************************/
unsigned char inputqueue[1048576 + 4];
int ecmify(FILE *in, FILE *out) {
unsigned inedc = 0;
int curtype = -1;
int curtypecount = 0;
int curtype_in_start = 0;
int detecttype;
int incheckpos = 0;
int inbufferpos = 0;
int intotallength;
int inqueuestart = 0;
int dataavail = 0;
int typetally[4];
fseek(in, 0, SEEK_END);
intotallength = ftell(in);
resetcounter(intotallength);
typetally[0] = 0;
typetally[1] = 0;
typetally[2] = 0;
typetally[3] = 0;
/* Magic identifier */
fputc('E', out);
fputc('C', out);
fputc('M', out);
fputc(0x00, out);
for(;;) {
if((dataavail < 2352) && (dataavail < (intotallength - inbufferpos))) {
int willread = intotallength - inbufferpos;
if(willread > ((sizeof(inputqueue) - 4) - dataavail)) willread = (sizeof(inputqueue) - 4) - dataavail;
if(inqueuestart) {
memmove(inputqueue + 4, inputqueue + 4 + inqueuestart, dataavail);
inqueuestart = 0;
}
if(willread) {
setcounter_analyze(inbufferpos);
fseek(in, inbufferpos, SEEK_SET);
fread(inputqueue + 4 + dataavail, 1, willread, in);
inbufferpos += willread;
dataavail += willread;
}
}
if(dataavail <= 0) break;
if(dataavail < 2336) {
detecttype = 0;
} else {
detecttype = check_type(inputqueue + 4 + inqueuestart, dataavail >= 2352);
}
if(detecttype != curtype) {
if(curtypecount) {
fseek(in, curtype_in_start, SEEK_SET);
typetally[curtype] += curtypecount;
inedc = in_flush(inedc, curtype, curtypecount, in, out);
}
curtype = detecttype;
curtype_in_start = incheckpos;
curtypecount = 1;
} else {
curtypecount++;
}
switch(curtype) {
case 0: incheckpos += 1; inqueuestart += 1; dataavail -= 1; break;
case 1: incheckpos += 2352; inqueuestart += 2352; dataavail -= 2352; break;
case 2: incheckpos += 2336; inqueuestart += 2336; dataavail -= 2336; break;
case 3: incheckpos += 2336; inqueuestart += 2336; dataavail -= 2336; break;
}
}
if(curtypecount) {
fseek(in, curtype_in_start, SEEK_SET);
typetally[curtype] += curtypecount;
inedc = in_flush(inedc, curtype, curtypecount, in, out);
}
/* End-of-records indicator */
write_type_count(out, 0, 0);
/* Input file EDC */
fputc((inedc >> 0) & 0xFF, out);
fputc((inedc >> 8) & 0xFF, out);
fputc((inedc >> 16) & 0xFF, out);
fputc((inedc >> 24) & 0xFF, out);
/* Show report */
fprintf(stderr, "Literal bytes........... %10d\n", typetally[0]);
fprintf(stderr, "Mode 1 sectors.......... %10d\n", typetally[1]);
fprintf(stderr, "Mode 2 form 1 sectors... %10d\n", typetally[2]);
fprintf(stderr, "Mode 2 form 2 sectors... %10d\n", typetally[3]);
fprintf(stderr, "Encoded %d bytes -> %ld bytes\n", intotallength, ftell(out));
fprintf(stderr, "Done.\n");
return 0;
}
/***************************************************************************/
int main(int argc, char **argv) {
FILE *fin, *fout;
char *infilename;
char *outfilename;
banner();
/*
** Initialize the ECC/EDC tables
*/
eccedc_init();
/*
** Check command line
*/
if((argc != 2) && (argc != 3)) {
fprintf(stderr, "usage: %s cdimagefile [ecmfile]\n", argv[0]);
return 1;
}
infilename = argv[1];
/*
** Figure out what the output filename should be
*/
if(argc == 3) {
outfilename = argv[2];
} else {
outfilename = malloc(strlen(infilename) + 5);
if(!outfilename) abort();
sprintf(outfilename, "%s.ecm", infilename);
}
fprintf(stderr, "Encoding %s to %s.\n", infilename, outfilename);
/*
** Open both files
*/
fin = fopen(infilename, "rb");
if(!fin) {
perror(infilename);
return 1;
}
fout = fopen(outfilename, "wb");
if(!fout) {
perror(outfilename);
fclose(fin);
return 1;
}
/*
** Encode
*/
ecmify(fin, fout);
/*
** Close everything
*/
fclose(fout);
fclose(fin);
return 0;
}