Task 168: .ECC File Format

Task 168: .ECC File Format

File Format Specifications for .ECC

The .ECC file format is primarily associated with dvdisaster, a software tool for creating error correction data for optical media (CDs, DVDs, BD) using Reed-Solomon codes. It allows recovery from data loss due to scratches or aging. The format supports different codecs (RS01, RS02, RS03), but separate .ecc files are typically used with RS01 and RS03. The file starts with a 4096-byte ECC header, followed by CRC data and parity bytes. The structure varies by codec, but the header is common. RS02 is generally for augmented ISO images, not separate .ecc files.

  1. List of all the properties of this file format intrinsic to its file system:
  • Cookie: A 12-byte string "dvdisaster" used as a magic identifier.
  • Method: 4-byte string describing the codec (e.g., "RS01", "RS02", "RS03").
  • Method Flags: 4 bytes for format specifications (e.g., bit flags for validity of sums or file type).
  • Medium FP: 16-byte MD5 sum of a specific sector (usually sector 16, the ISO root).
  • Medium Sum: 16-byte MD5 sum of the ISO image.
  • ECC Sum: 16-byte MD5 sum of ECC data (calculation varies by codec).
  • Sectors: 8-byte unsigned integer for the number of sectors in the protected medium.
  • Data Bytes: 4-byte integer for the number of data layers (including CRC layer).
  • ECC Bytes: 4-byte integer for the number of ECC layers (roots in Reed-Solomon).
  • Creator Version: 4-byte integer encoding the dvdisaster version that created the file (e.g., 102345 for v10.23.45).
  • Needed Version: 4-byte integer for the minimum dvdisaster version required to process the file.
  • FP Sector: 4-byte integer specifying the sector used for Medium FP.
  • Self CRC: 4-byte CRC32 checksum of the ECC header itself.
  • CRC Sum: 16-byte MD5 sum of the CRC layer (used in some codecs).
  • In Last: 4-byte integer for the number of bytes in the last image sector.
  • Sectors Per Layer: 8-byte unsigned integer for sectors per layer (key in RS03).
  • Sectors Added By ECC: 8-byte unsigned integer for sectors added by ECC (used in RS02/RS03).
  • Padding: 3960 bytes of zero-padding in the header for future expansion.
  1. Two direct download links for files of format .ECC:

After extensive searching, no direct download links for sample .ECC files could be found. These files are typically generated specifically for individual ISO images using dvdisaster and are not commonly shared publicly.

  1. Ghost blog embedded HTML JavaScript for drag-and-drop .ECC file dump:
ECC File Dumper
Drag and drop .ECC file here
  1. Python class for .ECC file handling:
import struct
import hashlib

class ECCFile:
    def __init__(self, filename):
        self.filename = filename
        self.properties = {}
        self.data = None

    def read(self):
        with open(self.filename, 'rb') as f:
            self.data = f.read()
        self.decode()

    def decode(self):
        if len(self.data) < 4096:
            raise ValueError("Invalid .ECC file: too short")
        header = self.data[:4096]
        self.properties['Cookie'] = header[0:12].decode('ascii')
        self.properties['Method'] = header[12:16].decode('ascii')
        self.properties['Method Flags'] = struct.unpack('<I', header[16:20])[0]
        self.properties['Medium FP'] = header[20:36].hex()
        self.properties['Medium Sum'] = header[36:52].hex()
        self.properties['ECC Sum'] = header[52:68].hex()
        self.properties['Sectors'] = struct.unpack('<Q', header[68:76])[0]
        self.properties['Data Bytes'] = struct.unpack('<i', header[76:80])[0]
        self.properties['ECC Bytes'] = struct.unpack('<i', header[80:84])[0]
        self.properties['Creator Version'] = struct.unpack('<i', header[84:88])[0]
        self.properties['Needed Version'] = struct.unpack('<i', header[88:92])[0]
        self.properties['FP Sector'] = struct.unpack('<i', header[92:96])[0]
        self.properties['Self CRC'] = struct.unpack('<I', header[96:100])[0]
        self.properties['CRC Sum'] = header[100:116].hex()
        self.properties['In Last'] = struct.unpack('<i', header[116:120])[0]
        self.properties['Sectors Per Layer'] = struct.unpack('<Q', header[120:128])[0]
        self.properties['Sectors Added By ECC'] = struct.unpack('<Q', header[128:136])[0]
        # Note: Further CRC and parity decoding would require RS implementation, omitted for brevity

    def print_properties(self):
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write(self, new_filename=None):
        if not self.data:
            # Create a sample minimal .ECC file if no data (for demo; in practice, generate full RS data)
            header = bytearray(4096)
            header[0:12] = b'*dvdisaster*'
            header[12:16] = b'RS01'
            # Set defaults for other fields (omitted full RS generation)
            self.data = header + b'\x00' * (4 * self.properties.get('Sectors', 0))  # Placeholder CRC + parity
        filename = new_filename or self.filename
        with open(filename, 'wb') as f:
            f.write(self.data)

# Example usage:
# ecc = ECCFile('example.ecc')
# ecc.read()
# ecc.print_properties()
# ecc.write('new.ecc')
  1. Java class for .ECC file handling:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class ECCFile {
    private String filename;
    private Map<String, Object> properties = new HashMap<>();
    private byte[] data;

    public ECCFile(String filename) {
        this.filename = filename;
    }

    public void read() throws IOException {
        data = Files.readAllBytes(Paths.get(filename));
        decode();
    }

    private void decode() {
        if (data.length < 4096) {
            throw new IllegalArgumentException("Invalid .ECC file: too short");
        }
        ByteBuffer buffer = ByteBuffer.wrap(data, 0, 4096).order(ByteOrder.LITTLE_ENDIAN);
        properties.put("Cookie", new String(data, 0, 12));
        properties.put("Method", new String(data, 12, 4));
        properties.put("Method Flags", buffer.getInt(16));
        properties.put("Medium FP", bytesToHex(data, 20, 16));
        properties.put("Medium Sum", bytesToHex(data, 36, 16));
        properties.put("ECC Sum", bytesToHex(data, 52, 16));
        properties.put("Sectors", buffer.getLong(68));
        properties.put("Data Bytes", buffer.getInt(76));
        properties.put("ECC Bytes", buffer.getInt(80));
        properties.put("Creator Version", buffer.getInt(84));
        properties.put("Needed Version", buffer.getInt(88));
        properties.put("FP Sector", buffer.getInt(92));
        properties.put("Self CRC", Integer.toUnsignedLong(buffer.getInt(96)));
        properties.put("CRC Sum", bytesToHex(data, 100, 16));
        properties.put("In Last", buffer.getInt(116));
        properties.put("Sectors Per Layer", buffer.getLong(120));
        properties.put("Sectors Added By ECC", buffer.getLong(128));
        // Note: Full CRC/parity decoding omitted

    }

    public void printProperties() {
        properties.forEach((key, value) -> System.out.println(key + ": " + value));
    }

    public void write(String newFilename) throws IOException {
        if (data == null) {
            // Create sample
            data = new byte[4096];
            System.arraycopy("*dvdisaster*".getBytes(), 0, data, 0, 12);
            System.arraycopy("RS01".getBytes(), 0, data, 12, 4);
            // Defaults omitted
        }
        Files.write(Paths.get(newFilename != null ? newFilename : filename), data);
    }

    private String bytesToHex(byte[] bytes, int offset, int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = offset; i < offset + length; i++) {
            sb.append(String.format("%02x", bytes[i]));
        }
        return sb.toString();
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     ECCFile ecc = new ECCFile("example.ecc");
    //     ecc.read();
    //     ecc.printProperties();
    //     ecc.write("new.ecc");
    // }
}
  1. JavaScript class for .ECC file handling:
class ECCFile {
    constructor(filename) {
        this.filename = filename;
        this.properties = {};
        this.data = null;
    }

    async read() {
        // Assuming Node.js with fs module
        const fs = require('fs');
        this.data = fs.readFileSync(this.filename);
        this.decode();
    }

    decode() {
        if (this.data.length < 4096) {
            throw new Error('Invalid .ECC file: too short');
        }
        const view = new DataView(this.data.buffer);
        this.properties.Cookie = new TextDecoder().decode(this.data.slice(0, 12));
        this.properties.Method = new TextDecoder().decode(this.data.slice(12, 16));
        this.properties['Method Flags'] = view.getUint32(16, true);
        this.properties['Medium FP'] = [...this.data.slice(20, 36)].map(b => b.toString(16).padStart(2, '0')).join('');
        this.properties['Medium Sum'] = [...this.data.slice(36, 52)].map(b => b.toString(16).padStart(2, '0')).join('');
        this.properties['ECC Sum'] = [...this.data.slice(52, 68)].map(b => b.toString(16).padStart(2, '0')).join('');
        this.properties.Sectors = Number(view.getBigUint64(68, true));
        this.properties['Data Bytes'] = view.getInt32(76, true);
        this.properties['ECC Bytes'] = view.getInt32(80, true);
        this.properties['Creator Version'] = view.getInt32(84, true);
        this.properties['Needed Version'] = view.getInt32(88, true);
        this.properties['FP Sector'] = view.getInt32(92, true);
        this.properties['Self CRC'] = view.getUint32(96, true);
        this.properties['CRC Sum'] = [...this.data.slice(100, 116)].map(b => b.toString(16).padStart(2, '0')).join('');
        this.properties['In Last'] = view.getInt32(116, true);
        this.properties['Sectors Per Layer'] = Number(view.getBigUint64(120, true));
        this.properties['Sectors Added By ECC'] = Number(view.getBigUint64(128, true));
        // Note: Full decoding omitted

    }

    printProperties() {
        for (const [key, value] of Object.entries(this.properties)) {
            console.log(`${key}: ${value}`);
        }
    }

    write(newFilename) {
        const fs = require('fs');
        if (!this.data) {
            this.data = new Uint8Array(4096);
            const encoder = new TextEncoder();
            this.data.set(encoder.encode('*dvdisaster*'), 0);
            this.data.set(encoder.encode('RS01'), 12);
            // Defaults omitted
        }
        fs.writeFileSync(newFilename || this.filename, this.data);
    }
}

// Example usage:
// const ecc = new ECCFile('example.ecc');
// await ecc.read();
// ecc.printProperties();
// ecc.write('new.ecc');
  1. C "class" (using struct and functions) for .ECC file handling:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

typedef struct {
    char cookie[12];
    char method[4];
    uint32_t methodFlags;
    uint8_t mediumFP[16];
    uint8_t mediumSum[16];
    uint8_t eccSum[16];
    uint64_t sectors;
    int32_t dataBytes;
    int32_t eccBytes;
    int32_t creatorVersion;
    int32_t neededVersion;
    int32_t fpSector;
    uint32_t selfCRC;
    uint8_t crcSum[16];
    int32_t inLast;
    uint64_t sectorsPerLayer;
    uint64_t sectorsAddedByECC;
} ECCProperties;

typedef struct {
    char* filename;
    ECCProperties props;
    uint8_t* data;
    size_t size;
} ECCFile;

ECCFile* eccfile_create(const char* filename) {
    ECCFile* ecc = malloc(sizeof(ECCFile));
    ecc->filename = strdup(filename);
    ecc->data = NULL;
    ecc->size = 0;
    return ecc;
}

void eccfile_read(ECCFile* ecc) {
    FILE* f = fopen(ecc->filename, "rb");
    if (!f) return;
    fseek(f, 0, SEEK_END);
    ecc->size = ftell(f);
    fseek(f, 0, SEEK_SET);
    ecc->data = malloc(ecc->size);
    fread(ecc->data, 1, ecc->size, f);
    fclose(f);
    eccfile_decode(ecc);
}

void eccfile_decode(ECCFile* ecc) {
    if (ecc->size < 4096) return;
    memcpy(ecc->props.cookie, ecc->data + 0, 12);
    memcpy(ecc->props.method, ecc->data + 12, 4);
    memcpy(&ecc->props.methodFlags, ecc->data + 16, 4);
    memcpy(ecc->props.mediumFP, ecc->data + 20, 16);
    memcpy(ecc->props.mediumSum, ecc->data + 36, 16);
    memcpy(ecc->props.eccSum, ecc->data + 52, 16);
    memcpy(&ecc->props.sectors, ecc->data + 68, 8);
    memcpy(&ecc->props.dataBytes, ecc->data + 76, 4);
    memcpy(&ecc->props.eccBytes, ecc->data + 80, 4);
    memcpy(&ecc->props.creatorVersion, ecc->data + 84, 4);
    memcpy(&ecc->props.neededVersion, ecc->data + 88, 4);
    memcpy(&ecc->props.fpSector, ecc->data + 92, 4);
    memcpy(&ecc->props.selfCRC, ecc->data + 96, 4);
    memcpy(ecc->props.crcSum, ecc->data + 100, 16);
    memcpy(&ecc->props.inLast, ecc->data + 116, 4);
    memcpy(&ecc->props.sectorsPerLayer, ecc->data + 120, 8);
    memcpy(&ecc->props.sectorsAddedByECC, ecc->data + 128, 8);
    // Note: Endian assumption little-endian; full decoding omitted
}

void eccfile_print_properties(ECCFile* ecc) {
    printf("Cookie: %.12s\n", ecc->props.cookie);
    printf("Method: %.4s\n", ecc->props.method);
    printf("Method Flags: %u\n", ecc->props.methodFlags);
    printf("Medium FP: ");
    for (int i = 0; i < 16; i++) printf("%02x", ecc->props.mediumFP[i]);
    printf("\n");
    // Similar for other hex fields...
    printf("Sectors: %lu\n", ecc->props.sectors);
    printf("Data Bytes: %d\n", ecc->props.dataBytes);
    printf("ECC Bytes: %d\n", ecc->props.eccBytes);
    printf("Creator Version: %d\n", ecc->props.creatorVersion);
    printf("Needed Version: %d\n", ecc->props.neededVersion);
    printf("FP Sector: %d\n", ecc->props.fpSector);
    printf("Self CRC: %u\n", ecc->props.selfCRC);
    // CRC Sum hex...
    printf("In Last: %d\n", ecc->props.inLast);
    printf("Sectors Per Layer: %lu\n", ecc->props.sectorsPerLayer);
    printf("Sectors Added By ECC: %lu\n", ecc->props.sectorsAddedByECC);
}

void eccfile_write(ECCFile* ecc, const char* newFilename) {
    if (!ecc->data) {
        ecc->size = 4096;
        ecc->data = calloc(1, ecc->size);
        memcpy(ecc->data, "*dvdisaster*", 12);
        memcpy(ecc->data + 12, "RS01", 4);
        // Defaults omitted
    }
    FILE* f = fopen(newFilename ? newFilename : ecc->filename, "wb");
    if (f) {
        fwrite(ecc->data, 1, ecc->size, f);
        fclose(f);
    }
}

void eccfile_destroy(ECCFile* ecc) {
    free(ecc->data);
    free(ecc->filename);
    free(ecc);
}

// Example usage:
// int main() {
//     ECCFile* ecc = eccfile_create("example.ecc");
//     eccfile_read(ecc);
//     eccfile_print_properties(ecc);
//     eccfile_write(ecc, "new.ecc");
//     eccfile_destroy(ecc);
//     return 0;
// }