Aquí están las imágenes "restauradas", gracias a la investigación adicional de tillberg:
Como se esperaba, hay un marcador de bloque de 5 bytes cada 0x4020 bytes. El formato parece ser el siguiente:
struct marker {
uint8_t tag; /* 1 if this is the last marker in the file, 0 otherwise */
uint16_t len; /* size of the following block (little-endian) */
uint16_t notlen; /* 0xffff - len */
};
Una vez que se ha leído el marcador, los siguientes marker.len
bytes forman un bloque que forma parte del archivo. marker.notlen
es una variable de control de tal manera que marker.len + marker.notlen == 0xffff
. El último bloque es tal que marker.tag == 1
.
La estructura es probablemente la siguiente. Todavía hay valores desconocidos.
struct file {
uint8_t name_len; /* number of bytes in the filename */
/* (not sure whether it's uint8_t or uint16_t) */
char name[name_len]; /* filename */
uint32_t file_len; /* size of the file (little endian) */
/* eg. "40 25 01 00" is 0x12540 bytes */
uint16_t unknown; /* maybe a checksum? */
marker marker1; /* first block marker (tag == 0) */
uint8_t data1[marker1.len]; /* data of the first block */
marker marker2; /* second block marker (tag == 0) */
uint8_t data2[marker2.len]; /* data of the second block */
/* ... */
marker lastmarker; /* last block marker (tag == 1) */
uint8_t lastdata[lastmarker.len]; /* data of the last block */
uint32_t unknown2; /* end data? another checksum? */
};
No he descubierto qué hay al final, pero como los PNG aceptan el relleno, no es demasiado dramático. Sin embargo, el tamaño del archivo codificado indica claramente que los últimos 4 bytes deben ignorarse ...
Como no tenía acceso a todos los marcadores de bloque justo antes del comienzo del archivo, escribí este decodificador que comienza al final e intenta encontrar los marcadores de bloque. No es robusto en absoluto, pero bueno, funcionó para sus imágenes de prueba:
#include <stdio.h>
#include <string.h>
#define MAX_SIZE (1024 * 1024)
unsigned char buf[MAX_SIZE];
/* Usage: program infile.png outfile.png */
int main(int argc, char *argv[])
{
size_t i, len, lastcheck;
FILE *f = fopen(argv[1], "rb");
len = fread(buf, 1, MAX_SIZE, f);
fclose(f);
/* Start from the end and check validity */
lastcheck = len;
for (i = len - 5; i-- > 0; )
{
size_t off = buf[i + 2] * 256 + buf[i + 1];
size_t notoff = buf[i + 4] * 256 + buf[i + 3];
if (buf[i] >= 2 || off + notoff != 0xffff)
continue;
else if (buf[i] == 1 && lastcheck != len)
continue;
else if (buf[i] == 0 && i + off + 5 != lastcheck)
continue;
lastcheck = i;
memmove(buf + i, buf + i + 5, len - i - 5);
len -= 5;
i -= 5;
}
f = fopen(argv[2], "wb+");
fwrite(buf, 1, len, f);
fclose(f);
return 0;
}
Investigación más antigua
Esto es lo que obtienes al eliminar el byte 0x4022
de la segunda imagen, luego al eliminar el byte 0x8092
:
Realmente no "repara" las imágenes; Lo hice por prueba y error. Sin embargo, lo que dice es que hay datos inesperados cada 16384 bytes. Supongo que las imágenes están empaquetadas en algún tipo de estructura de sistema de archivos y los datos inesperados son simplemente marcadores de bloque que debe eliminar al leer los datos.
No sé exactamente dónde están los marcadores de bloque y su tamaño, pero el tamaño del bloque en sí es ciertamente 2 ^ 14 bytes.
Sería útil si también pudiera proporcionar un volcado hexadecimal (unas pocas docenas de bytes) de lo que aparece justo antes de la imagen y justo después. Esto daría pistas sobre qué tipo de información se almacena al principio o al final de los bloques.
Por supuesto, también existe la posibilidad de que haya un error en su código de extracción. Si está utilizando un búfer de 16384 bytes para sus operaciones de archivo, entonces primero comprobaría allí.