// checksum.c — cuatro checksums sobre un archivo y qué errores detecta cada uno.
//
// Uso: ./checksum ARCHIVO [OFFSET]
//
// Calcula XOR (8 bits), Fletcher-16, Adler-32 y CRC-32 de ARCHIVO y luego
// prueba tres daños en una copia en memoria, a partir de OFFSET (por defecto 0):
//   1. un bit volteado          2. el mismo bit volteado en dos bytes
//   3. dos bytes intercambiados
// Para cada daño indica qué checksum lo detecta (cambia) y cuál no.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static uint32_t xor8(const uint8_t *d, size_t n) {
    uint8_t x = 0;
    for (size_t i = 0; i < n; i++)
        x ^= d[i];
    return x;
}

// Fletcher-16: dos sumas módulo 255; s2 acumula s1 y así depende de la posición.
static uint32_t fletcher16(const uint8_t *d, size_t n) {
    uint32_t s1 = 0, s2 = 0;
    for (size_t i = 0; i < n; i++) {
        s1 = (s1 + d[i]) % 255;
        s2 = (s2 + s1) % 255;
    }
    return (s2 << 8) | s1;
}

// Adler-32: la misma idea, módulo 65521 (primo) y s1 empieza en 1.
static uint32_t adler32(const uint8_t *d, size_t n) {
    uint32_t a = 1, b = 0;
    for (size_t i = 0; i < n; i++) {
        a = (a + d[i]) % 65521;
        b = (b + a) % 65521;
    }
    return (b << 16) | a;
}

// CRC-32 (el de Ethernet, zip y gzip), bit a bit: lento pero corto.
static uint32_t crc32(const uint8_t *d, size_t n) {
    uint32_t c = 0xFFFFFFFFu;
    for (size_t i = 0; i < n; i++) {
        c ^= d[i];
        for (int k = 0; k < 8; k++)
            c = (c >> 1) ^ (0xEDB88320u & -(c & 1));
    }
    return ~c;
}

struct algo {
    const char *name;
    uint32_t (*fn)(const uint8_t *, size_t);
};

static const struct algo algos[] = {
    {"XOR-8", xor8}, {"Fletcher-16", fletcher16},
    {"Adler-32", adler32}, {"CRC-32", crc32},
};
#define NALGO (sizeof(algos) / sizeof(algos[0]))

static void check(const char *what, const uint8_t *orig, const uint8_t *bad, size_t n) {
    printf("\n%s\n", what);
    for (size_t a = 0; a < NALGO; a++) {
        uint32_t before = algos[a].fn(orig, n), after = algos[a].fn(bad, n);
        printf("  %-12s %08x -> %08x  %s\n", algos[a].name, before, after,
               before != after ? "detectado" : "NO DETECTADO");
    }
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "uso: %s ARCHIVO [OFFSET]\n", argv[0]);
        return 1;
    }
    FILE *f = fopen(argv[1], "rb");
    if (!f) {
        perror(argv[1]);
        return 1;
    }
    fseek(f, 0, SEEK_END);
    long n = ftell(f);
    rewind(f);
    size_t off = argc > 2 ? strtoul(argv[2], NULL, 0) : 0;
    if (n < 2 || off + 1 >= (size_t)n) {
        fprintf(stderr, "el archivo debe tener más de OFFSET+1 bytes\n");
        return 1;
    }
    uint8_t *orig = malloc(n), *bad = malloc(n);
    if (!orig || !bad || fread(orig, 1, n, f) != (size_t)n) {
        fprintf(stderr, "no se pudo leer %s\n", argv[1]);
        return 1;
    }
    fclose(f);
    printf("%s: %ld bytes, daños en el offset %zu\n", argv[1], n, off);
    if (orig[off] == orig[off + 1])
        printf("(aviso: los bytes %zu y %zu son iguales; el daño 3 no cambia nada)\n",
               off, off + 1);

    memcpy(bad, orig, n);
    bad[off] ^= 0x04;
    check("1. Un bit volteado (bit 2 del byte OFFSET)", orig, bad, n);

    memcpy(bad, orig, n);
    bad[off] ^= 0x04;
    bad[off + 1] ^= 0x04;
    check("2. El mismo bit volteado en dos bytes seguidos", orig, bad, n);

    memcpy(bad, orig, n);
    bad[off] = orig[off + 1];
    bad[off + 1] = orig[off];
    check("3. Dos bytes seguidos intercambiados", orig, bad, n);

    free(orig);
    free(bad);
    return 0;
}
