/*
 * buildpatch
 *
 * Copyright (C) 2011 Hermes
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

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

#include "zlib.h"
#include <openssl/sha.h>

#define FWRITE(a,b,c) {v = fwrite(a, 1, b, c); if(v != b) {fprintf(stderr, "I/O error\n"); retval=1; goto out;}}

char filetype[4]= "PFWT";

int main(int argc, const char **argv)
{
	const char *infile, *infile2, *outfile;

	unsigned char *buffer = NULL;
    unsigned char *buffer2 = NULL;
    unsigned char *buffer3 = NULL;
    
    FILE *fp= NULL, *fp2= NULL, *fp3= NULL;

    int retval = 0;

    int len, len2, v;
  
	
    
	/* Check arguments */
	if (argc < 3) {
		fprintf(stderr, "USAGE:\n\nbuildpatch <old file> <new file> [file.pfw]\n");
		return 1;
	}

	/* Read arguments */
	infile  = argv[1];
    infile2 = argv[2];

    if(!strcmp(infile, infile2)) {
        fprintf(stderr, "ERROR: <old file> and <new file> must be different name\n");
		return 1;
	}

    if(argc>3)
        outfile = argv[3];
    else 
        outfile ="patch.pfw";

	buffer = malloc(1024*1024);
    buffer2 = malloc(1024*1024);
    buffer3 = malloc(1024*1024);

    fp = fopen(infile,"rb");

    if(!fp) {fprintf(stderr, "ERROR opening %s file\n", infile);retval=1;goto out;}

    fp2 = fopen(infile2,"rb");

    if(!fp2) {fprintf(stderr, "ERROR opening %s file\n", infile2);retval=1;goto out;}

    fp3 = fopen(outfile, "wb");

    if(!fp3) {fprintf(stderr, "ERROR creating %s file\n", outfile);retval=1;goto out;}

    fseek(fp, 0, SEEK_END);
    fseek(fp2, 0, SEEK_END);

    len = ftell(fp);
    len2 = ftell(fp2);

    fseek(fp, 0, SEEK_SET);
    fseek(fp2, 0, SEEK_SET);

    if(len != len2) {
    
        fprintf(stderr, "different file sizes\n");
        retval=1;goto out;
    }

    FWRITE(filetype, 4, fp3);
    
    FWRITE(&len2, 4, fp3);

    int pos =0, r, s;

    unsigned char hash[20];
    unsigned char hash2[20];

    while(pos<len) {
    
        s= len - pos; if(s > 1024*1024) s = 1024 * 1024;

        r = fread(buffer, 1, s, fp);

        if(r!=s) {
            fprintf(stderr, "fail reading file 0\n");
            retval=1;goto out;
        }

        r = fread(buffer2, 1, s, fp2);

        if(r!=s) {
            fprintf(stderr, "fail reading file 1\n");
            retval=1;goto out;
        }

        pos += r;
        
        

        r = 0;
        while(r < s) {
            buffer3[r] = buffer[r] ^ buffer2[r] ^ 170;
            r++;
        }
        SHA1(buffer, s, hash);
        SHA1(buffer2, s, hash2);
        
        u32 o;

        if(Zlib_Compress(buffer3, s, buffer, &o)<0) {
            fprintf(stderr, "ZLIB Error\n");
            retval=1;goto out;
        }
        
        FWRITE(&s, 4, fp3);
        FWRITE(&o, 4, fp3);
        
        FWRITE(hash, 20, fp3);
        FWRITE(hash2, 20, fp3);
        FWRITE(buffer, o, fp3);
    }

    if(fp3) {fclose(fp3); fp3 = NULL;}
 
out:
	if (buffer)  free(buffer);
    if (buffer2)  free(buffer2);
    if (buffer3)  free(buffer3);

    if(fp) fclose(fp);
    if(fp2) fclose(fp2);
    if(fp3) {fclose(fp3); remove(outfile);}

	return retval;
}
