/*
 * jpg_rec.c
 * 2005-09-04
 * Fabian Bornhofen (fbornhofen *at* googlemail *dot* com)
 * http://bfabian.de
 *
 * I wrote this in order to recover some JPEG images from a FAT formatted
 * floppy disk. The programme is not intended for "professional" use.
 * 
 * How does it work?
 * jpg_rec searches a disk image for the (possible) header of a jpg file.
 * If it finds one it writes all the following data up to the EOI (end of image)
 * marker to a separate file. This might also work for other file formats,
 * as described below.
 * Maybe the programme can be improved by a table containing different "magic
 * lines"... it would be interesting ;) 
 * You're invited to go ahead:
 *
 *   This programme is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License (version 2) as
 *   published by the Free Software Foundation.
 *
 * USAGE: jpg_rec FILE_LIKELY_TO_CONTAIN_JPG_IMAGES
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* the following lines may be adapted for other file types */

#define EXTENSION "jpg"

char MAGIC_LINE[] = { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F' };
int MAGIC_LINE_LEN = 10;

int HEADER_LEN = 20;

char EOI[] = { 0xFF, 0xD9 }; // end of image
int EOI_LEN = 2;

/* ------ */

unsigned long filesize(char* f_name);
void copy_region(char* src, int n_bytes, char* dest);
int recover_files(char* diskimage_name);
char* buffer_find(char* buffer, int buf_size, char* str, int strl);
int bin_strncmp(char* s1, char* s2, int n);

int main(int argc, char** argv)
{
	if (argc < 2) {
		fprintf(stderr, "missing argument\n");
		exit(-1);
	}
	printf("magic line: %d bytes\n", MAGIC_LINE_LEN);
	printf("files: %d\n", recover_files(argv[1]));
	return 0;
}

unsigned long filesize(char* f_name)
{
	FILE* f = fopen(f_name, "r");
	fseek(f, 0, SEEK_END);
	printf("file size: %d\n", ftell(f));
	return ftell(f);
}

void copy_region(char* src, int n_bytes, char* dest)
{
	while(n_bytes--)
		*(dest++) = *(src++);
}

int recover_files(char* diskimage_name)
{
	int firstloop = 1, done = 0;
	int n_found = -1;
	int cur_pos = 0;	

	char *img_buf = 0, *jpg_buf = 0;
	char* next_pos, *nnext_pos = 0;

	char ofname[255];	

	int fsize = filesize(diskimage_name);
	FILE *fin = fopen(diskimage_name, "r");
	FILE *fout;
	if (!fin) goto quit;
	n_found = -2;
	if(!(img_buf=malloc(fsize*sizeof(char))))
		goto quit;
	n_found = -3;
	if(!(jpg_buf=malloc(fsize*sizeof(char))))
		goto quit;
	n_found = 0;

	fread(img_buf, fsize, 1, fin);
	
	next_pos = img_buf;
	do {
		int block_size;
		if (firstloop) {
			next_pos = buffer_find(next_pos, fsize,
					MAGIC_LINE, MAGIC_LINE_LEN);
			firstloop = 0;
		} else
			next_pos = buffer_find(next_pos, fsize-cur_pos,
					MAGIC_LINE, MAGIC_LINE_LEN );
	
		if (!next_pos) 
			break;

		n_found++;
		cur_pos = next_pos - img_buf;
		printf("%d.\tpos: %d\n", n_found, cur_pos);		

		nnext_pos = buffer_find(next_pos + HEADER_LEN,  
				fsize - cur_pos - HEADER_LEN,
				EOI, EOI_LEN);
		if (!nnext_pos) {
			nnext_pos = img_buf + fsize;
			done = 1;
		} else 				//fixed 2005-09-26
			nnext_pos += EOI_LEN;	//include end of image marker
		
		if (!(block_size = nnext_pos - next_pos))
			break;
		
		printf("\tblock size: %d\n", block_size);
		copy_region(next_pos, block_size, jpg_buf);
		
		snprintf(ofname, 255, "recovered-%d.%s", n_found, EXTENSION);
		if(!(fout = fopen(ofname, "w"))) {
			fprintf(stderr, "could not write %s\n", ofname);
			n_found = -4;
			goto quit;
		}
		fwrite(next_pos, block_size, 1, fout);

		next_pos += MAGIC_LINE_LEN;
		fclose(fout);				
	} while (next_pos && !done);
	
quit:
	free(img_buf);
	free(jpg_buf);
	return n_found;
}

char* buffer_find(char* buffer, int buf_size, char* str, int strl)
{
	while((buf_size--)-strl > 0) {
		if (!bin_strncmp(buffer, str, strl))
			return buffer;
		buffer++;
	}
	return NULL;
}

int bin_strncmp(char* s1, char* s2, int n)
{
	while(n--) {
		if (*(s1++) != *(s2++))
			return 1;
	}
	return 0;
}


