Aqui estão as imagens "restauradas", graças às pesquisas adicionais de tillberg:
Como esperado, há um marcador de bloco de 5 bytes a cada 0x4020 bytes. O formato parece ser o seguinte:
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 */
};
Após a leitura do marcador, os próximos marker.len
bytes formam um bloco que faz parte do arquivo. marker.notlen
é uma variável de controle tal que marker.len + marker.notlen == 0xffff
. O último bloco é tal que marker.tag == 1
.
A estrutura é provavelmente a seguinte. Ainda existem valores desconhecidos.
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? */
};
Ainda não descobri o que há no final, mas como os PNGs aceitam o preenchimento, não é muito dramático. No entanto, o tamanho do arquivo codificado indica claramente que os últimos 4 bytes devem ser ignorados ...
Como não tinha acesso a todos os marcadores de bloco imediatamente antes do início do arquivo, escrevi esse decodificador que inicia no final e tenta encontrar os marcadores de bloco. Não é robusto, mas funciona bem para as imagens de teste:
#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;
}
Pesquisa mais antiga
É isso que você obtém ao remover o byte 0x4022
da segunda imagem e, em seguida, ao remover o byte 0x8092
:
Realmente não "repara" as imagens; Eu fiz isso por tentativa e erro. No entanto, o que diz é que há dados inesperados a cada 16384 bytes. Meu palpite é que as imagens são compactadas em algum tipo de estrutura do sistema de arquivos e os dados inesperados são simplesmente marcadores de blocos que você deve remover ao ler os dados.
Não sei exatamente onde estão os marcadores de bloco e seu tamanho, mas o tamanho do bloco em si é certamente 2 ^ 14 bytes.
Ajudaria se você também pudesse fornecer um dump hexadecimal (algumas dezenas de bytes) do que aparece logo antes da imagem e logo depois. Isso daria dicas sobre que tipo de informação é armazenada no início ou no final dos blocos.
Claro que também existe a possibilidade de que haja um erro no seu código de extração. Se você estiver usando um buffer de 16384 bytes para suas operações de arquivo, eu primeiro verificaria lá.