ecmlib 1.0.0

A simple CD-ROM error code modeler (ECM), used to save some space storing backups.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
//!
//! This file is to store the common enums, structs... used in both the encoder and the decoder.
//!

use bitflags::bitflags;
use thiserror::Error;

///
/// Enum to identify the different sectors types. This data is important for the decoding, so it must be stored in the output stream in any way via Index, header...
///
/// The Unknown type must not be used unless you explicity wants to indicate that the sector is not yet recognized or something similar. The decoder will not understand this type and will be ignored.
///
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SectorType {
    /// Unknown sector.
    Unknown = 0,
    /// Normal CDDA sector.
    Cdda, //
    /// A fully zeroed sector. It doesn't have any data mark, so it is detected as CDDA.
    CddaGap, //
    /// Normal MODE1 sector.
    Mode1,
    /// MODE1 sector with no data (fully zeroed).
    Mode1Gap,
    /// A damaged MODE1 sector. The EDC and ECC cannot be validated, so it is not a correct MODE1 sector.
    /// It is used in some games protections, so all the data will be kept as is.
    Mode1Raw, //
    /// Normal MODE2 sector. This mode is not widely used.
    Mode2, //
    /// Normal MODE2 sector but fully zeroed. Data can be ignored and regenerated easily.
    Mode2Gap,
    /// Non compliant MODE2 XA sector used in some PSX games for example. It contains the XA flags, but it is fully zeroed including the EDC/ECC data.
    /// Because the XA flags and the lack of EDC and ECC, this sector will be detected as a MODE2 sector and then fully copied.
    /// This mode was created to avoid this problem and optimize a bit more the space.
    Mode2XaGap,
    /// Normal MODE2 XA1 sector.
    Mode2Xa1,
    /// A normal MODE2 XA1 sector but with no data (zeroed).
    Mode2Xa1Gap,
    /// Normal MODE2 XA2 sector.
    Mode2Xa2,
    /// A normal MODE2 XA2 sector but with no data (zeroed).
    Mode2Xa2Gap,
}

///
/// Implementation to convert u8 to SectorType matching unknown modes to SectorType::Unknown
///
impl From<u8> for SectorType {
    fn from(value: u8) -> Self {
        match value {
            0 => SectorType::Unknown,
            1 => SectorType::Cdda,
            2 => SectorType::CddaGap,
            3 => SectorType::Mode1,
            4 => SectorType::Mode1Gap,
            5 => SectorType::Mode1Raw,
            6 => SectorType::Mode2,
            7 => SectorType::Mode2Gap,
            8 => SectorType::Mode2XaGap,
            9 => SectorType::Mode2Xa1,
            10 => SectorType::Mode2Xa1Gap,
            11 => SectorType::Mode2Xa2,
            12 => SectorType::Mode2Xa2Gap,
            _ => SectorType::Unknown, // fallback automático
        }
    }
}

bitflags! {
    ///
    /// This enum must be used to pass to the decoder the optimizations used into the sector. Like the SectorType, it is important to store this data together with the sector to be able to decode it later.
    ///
    /// The way that this enum works is by bitwise and the result must be a u8 value. For example if you want to use the RemoveSync and RemoveECC optimization you should pass it as:
    ///
    /// `let optimizations: u8 = Optimizations::RemoveSync & Optimizations::RemoveECC;`
    ///
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    pub struct Optimizations: u8 {
        /// Don't remove anything (just copy the entire sector).
        const None = 0b0000_0000;
        /// Remove the sync data. This will save 12 bytes on non CDDA sectors.
        const RemoveSync = 0b0000_0001;
        /// Remove the address data (3 bytes). Present in all non CDDA sectors.
        const RemoveMSF = 0b0000_0010;
        /// Remove the mode data (1 byte). Present in all non CDDA sectors, but must be provided to the decoder.
        ///
        /// Can be used to save some space by putting several sectors of the same mode together storing just one byte in an index or similar.
        const RemoveMode = 0b0000_0100;
        /// Remove the zeroes in the MODE1 sectors saving 8 bytes.
        const RemoveBlanks = 0b0000_1000;
        /// Remove the redundant copy of the flags in the MODE2 XA sectors saving 4 bytes.
        const RemoveRedundantFlag = 0b0001_0000;
        /// Remove the ECC present in MODE1 and MODE2 XA1 sectors. Saves 276 bytes and can be regenerated without problem.
        const RemoveECC = 0b0010_0000;
        /// Remove the EDC data present in MODE1 and MODE2 XA1 & XA2 sectors. Saves 4 bytes and like the ECC, can be regenerated.
        const RemoveEDC = 0b0100_0000;
        /// Remove the GAP on sectors with zeroed data. This data is just a bunch of zeroes, so it can be regenerated easily. The saved size depends of the sector type.
        const RemoveGap = 0b1000_0000;
    }
}

#[derive(Debug, Error)]
pub enum StatusError {
    #[error("Not enough sector data to be able to process it.")]
    NotEnoughSectorData,

    #[error("Unknown format detected: {0}")]
    UnknownFormat(String),
}

///
/// Struct to convert the MSF data to a Sector Number and viceversa.
///
pub struct MSF;

impl MSF {
    ///
    /// Convert a decimal value [0..=99] to the BCD equivalent.
    /// Example: 28 → 0x28
    ///
    fn to_bcd(value: u8) -> u8 {
        assert!(value < 100, "BCD sólo soporta valores 0..=99");
        ((value / 10) << 4) | (value % 10)
    }

    ///
    /// Converts a sector number into a MSF slice
    ///
    pub fn sectors_to_msf(sector_number: u32) -> [u8; 3] {
        let frames: u8 = (sector_number % 75) as u8;
        let seconds: u8 = ((sector_number / 75) % 60) as u8;
        let minutes: u8 = (sector_number / 75 / 60) as u8;

        [
            MSF::to_bcd(minutes),
            MSF::to_bcd(seconds),
            MSF::to_bcd(frames),
        ]
    }
}

///
/// This struct contains the tools required to generate the Reed-Solomon data (a.k.a ECC & EDC), into the sectors.
///
pub struct EccEdc {
    ecc_f_lut: [u8; 256],
    ecc_b_lut: [u8; 256],
    edc_lut: [u32; 256],
}

impl EccEdc {
    ///
    /// Initialization which generates the required Reed-Solomon data
    ///
    /// # Arguments
    ///
    /// There are no arguments
    ///
    /// # Return
    ///
    /// The EccEdc initialized.
    ///
    /// # Example
    ///
    /// ```text
    /// let edc_ecc_calculator = EccEdc::new();
    ///
    /// ```
    ///
    pub fn new() -> Self {
        let mut ecc_f_lut = [0u8; 256];
        let mut ecc_b_lut = [0u8; 256];
        let mut edc_lut = [0u32; 256];

        for i in 0..256 {
            let mut edc = i as u32;
            let j = ((i << 1) ^ if (i & 0x80) != 0 { 0x11D } else { 0 }) & 0xFF;

            ecc_f_lut[i] = j as u8;
            ecc_b_lut[i ^ j] = i as u8;

            for _ in 0..8 {
                edc = (edc >> 1) ^ if (edc & 1) != 0 { 0xD8018001 } else { 0 };
            }
            edc_lut[i] = edc;
        }

        Self {
            ecc_f_lut,
            ecc_b_lut,
            edc_lut,
        }
    }

    ///
    /// Generates the sector EDC using the provided data
    ///
    /// # Arguments
    ///
    /// * data: A slice with the data to be used to calculate the EDC
    ///
    /// # Return
    ///
    /// The generated EDC
    ///
    /// # Example
    ///
    /// ```text
    /// let edc_ecc_calculator = EccEdc::new();
    /// let edc = edc_ecc_calculator.generate_edc(&sector[0x10..0x818]);
    ///
    /// // Generate the two kind of ECC
    /// self.generate_ecc_pq(address, data, &mut generated_ecc[..0xAD], 86, 24, 2, 86);
    /// self.generate_ecc_pq(address, data, &mut generated_ecc[0xAC..], 52, 43, 86, 88);
    ///
    /// ```
    ///
    pub fn generate_edc(&self, data: &[u8]) -> u32 {
        let mut edc = 0;
        for &byte in data {
            edc = (edc >> 8) ^ self.edc_lut[((edc ^ byte as u32) & 0xFF) as usize];
        }
        edc
    }

    ///
    /// Generates the sector ECC using the provided data
    ///
    /// # Arguments
    ///
    /// * address: The address and mode (4 bytes) for a MODE1 sector or zeroes for a MODE2 sector.
    /// * data: The data used to generate the ECC
    /// * ecc (output): A preassigned slice to store the ECC
    /// * major_count: The major count to use
    /// * minot_count: The minor count to use
    /// * major_mult: The major multiplier
    /// * minor_mult: The minor multiplier
    ///
    /// # Return
    ///
    /// None
    ///
    /// # Example
    ///
    /// ```text
    /// let edc_ecc_calculator = EccEdc::new();
    /// let mut generated_ecc: [u8; 276] = [0; 276];
    ///
    /// ```
    ///
    pub fn generate_ecc_pq(
        &self,
        address: &[u8],
        data: &[u8],
        ecc: &mut [u8],
        major_count: usize,
        minor_count: usize,
        major_mult: usize,
        minor_inc: usize,
    ) {
        let size = major_count * minor_count;

        for major in 0..major_count {
            let mut index = (major >> 1) * major_mult + (major & 1);
            let mut ecc_a = 0u8;
            let mut ecc_b = 0u8;

            for _ in 0..minor_count {
                let temp = if index < 4 {
                    address[index]
                } else {
                    data[index - 4]
                };

                index += minor_inc;
                if index >= size {
                    index -= size;
                }

                ecc_b ^= temp;
                ecc_a = self.ecc_f_lut[(ecc_a ^ temp) as usize];
            }

            ecc_a = self.ecc_b_lut[(self.ecc_f_lut[ecc_a as usize] ^ ecc_b) as usize];
            ecc[major] = ecc_a;
            ecc[major + major_count] = ecc_a ^ ecc_b;
        }
    }

    ///
    /// Generates the sector ECC using the provided data and verifies that matches the original ECC data.
    ///
    /// # Arguments
    ///
    /// * address: The address and mode (4 bytes) for a MODE1 sector or zeroes for a MODE2 sector.
    /// * data: A slice with the data to be used to generate the ECC
    /// * ecc: The original ECC data for comparison
    ///
    /// # Return
    ///
    /// Boolean: true if matches otherwise false.
    ///
    /// # Example
    ///
    /// ```text
    /// let edc_ecc_calculator = EccEdc::new();
    /// let address: [u8; 4] = [0, 0, 0, 0];
    ///
    /// let is_correct_ecc =
    ///     edc_ecc_calculator.check_ecc(&address, &sector[0x10..], &sector[0x81C..0x930]);
    ///
    /// ```
    ///
    pub fn check_ecc(&self, address: &[u8], data: &[u8], ecc: &[u8]) -> bool {
        let mut generated_ecc = [0u8; 276];

        self.generate_ecc_pq(address, data, &mut generated_ecc[..0xAC], 86, 24, 2, 86);
        self.generate_ecc_pq(address, data, &mut generated_ecc[0xAC..], 52, 43, 86, 88);

        generated_ecc == ecc
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use std::path::Path;

    #[test]
    fn generate_edc() {
        let path = Path::new("tests/data/mode2_xa1.bin");
        let sector = fs::read(path).expect("Cannot open test sector.");
        assert_eq!(sector.len(), 2352, "The sector must be 2352 bytes length.");

        let edc_ecc_calculator = EccEdc::new();
        let original_edc = &sector[0x818..0x81C];

        let generated_edc: u32 = edc_ecc_calculator.generate_edc(&sector[0x10..0x818]);

        assert_eq!(
            generated_edc.to_le_bytes(),
            original_edc,
            "Testing matching generated EDC."
        );
    }

    #[test]
    fn generate_ecc() {
        let path = Path::new("tests/data/mode2_xa1.bin");
        let sector = fs::read(path).expect("Cannot open test sector.");
        assert_eq!(sector.len(), 2352, "The sector must be 2352 bytes length.");

        let edc_ecc_calculator = EccEdc::new();
        let address: [u8; 4] = [0, 0, 0, 0];
        let mut generated_ecc_p: [u8; 172] = [0; 172];
        let mut generated_ecc_q: [u8; 104] = [0; 104];

        // Testing the function generate_ecc_pq with ECC P type
        edc_ecc_calculator.generate_ecc_pq(
            &address,
            &sector[0x10..],
            &mut generated_ecc_p[..],
            86,
            24,
            2,
            86,
        );
        assert_eq!(
            generated_ecc_p,
            sector[0x81C..0x8C8],
            "Testing matching ECC P generated data."
        );

        // Testing the function generate_ecc_pq with ECC Q type
        edc_ecc_calculator.generate_ecc_pq(
            &address,
            &sector[0x10..],
            &mut generated_ecc_q[..],
            52,
            43,
            86,
            88,
        );
        assert_eq!(
            generated_ecc_q,
            sector[0x8C8..0x930],
            "Testing matching ECC Q generated data."
        );
    }

    #[test]
    fn check_ecc() {
        let path = Path::new("tests/data/mode2_xa1.bin");
        let sector = fs::read(path).expect("Cannot open test sector.");
        assert_eq!(sector.len(), 2352, "The sector must be 2352 bytes length.");

        let edc_ecc_calculator = EccEdc::new();
        let address: [u8; 4] = [0, 0, 0, 0];

        let is_correct_ecc =
            edc_ecc_calculator.check_ecc(&address, &sector[0x10..], &sector[0x81C..0x930]);

        assert_eq!(
            is_correct_ecc, true,
            "The check_ecc function must return Ok(true)."
        );
    }

    #[test]
    fn check_msf() {
        let sector_number: u32 = 254874;
        let msf: [u8; 3] = [0x56, 0x38, 0x24];

        let generated_msf = MSF::sectors_to_msf(sector_number);

        assert_eq!(
            msf, generated_msf,
            "The msf conversion is not properly working."
        );
    }
}