#include "stdio.h"
#include "unistd.h"
#include "string.h"
#include "stdlib.h"

#define MIN(x,y)	( x < y ? x : y )

int main(int argc, char *argv[]) {
	if(argc != 4) {
		fprintf(stderr, "Usage: %s filetopatch output patch.ips\n", argv[0]);
		exit(-1);
	}
	FILE *patch, *input, *output;
	patch = fopen(argv[3], "rb");
	if(!patch) {
		fprintf(stderr, "Unable to open file: %s\n", argv[3]);
		exit(-2);
	}
	input = fopen(argv[1], "rb");
	if(!input) {
		fprintf(stderr, "Unable to open file: %s\n", argv[1]);
		exit(-2);
	}
	output = fopen(argv[2], "wb+");
	if(!output) {
		fprintf(stderr, "Unable to open file: %s\n", argv[2]);
		exit(-2);
	}
	unsigned char buf[7];
	fread(buf, sizeof(buf[0]), 5, patch);
	buf[5] = 0;
	if(strcmp("PATCH", buf)) {
		fprintf(stderr, "Invalid patch file\n");
		exit(-1);
	}

	// Loop until end
	// Assuming that patch addresses are monotonically increasing
	
	int posin = 0;
	unsigned char *bigbuf = malloc(sizeof(char) * 65536);
	unsigned int addr, patchsize;
	int tmp, result;
	unsigned int furthest = 0;

	while(!feof(patch)) {
		fread(buf, sizeof(buf[0]), 5, patch);
		addr = (buf[0] << 16) + (buf[1] << 8)  + buf[2];
		printf("%6x\n", addr );
		if(addr < furthest ) {
			fseek(input, addr, SEEK_SET);
			fclose(output);
			output = fopen(argv[2], "rb+");
			if(!output) {
				fprintf(stderr, "Unable to open file: %s\n", argv[2]);
				exit(-2);
			}
			fseek(output, addr, SEEK_SET);
		} else {
			while(posin < addr) {
				tmp = MIN(addr - posin, 65536);
				result = fread(bigbuf, sizeof(buf[0]), tmp, input);
				if (result < tmp) {
					printf("Expected %i, got %i", tmp, result);
					perror("On noes!");
					exit(-1);
				}
				fwrite(bigbuf, sizeof(buf[0]), tmp, output);
				posin += tmp;
			}
		}
		fprintf(stderr, "%08x vs. %08x\n", addr, ftell(input));
		patchsize = (buf[3] << 8) + buf[4];
		if (patchsize == 0) { // RLE
			printf("Runlength Encoding\n");
			fread(buf, sizeof(buf[0]), 3, patch);
			patchsize = (buf[0] << 8) + buf[1];
			for(tmp = 0; tmp < patchsize; tmp ++) {
				fwrite(&buf[2], sizeof(buf[0]), 1, output);
			}
		} else {
			printf("Encoding %u digits\n", patchsize);
			printf("First few characters: ");
			int j = 0;
			switch(patchsize) {
				default:
					printf("%02x", bigbuf[j++] & 0xff);
				case 3:
					printf("%02x", bigbuf[j++] & 0xff);
				case 2:
					printf("%02x", bigbuf[j++] & 0xff);
				case 1:
					printf("%02x", bigbuf[j++] & 0xff);
			}
			printf("\n");
			fread(bigbuf, sizeof(buf[0]), patchsize, patch);
			fwrite(bigbuf, sizeof(buf[0]), patchsize, output);
		}

		posin += patchsize;
		if(posin > furthest) {
			furthest = posin;
		} else {
			// Move to end
			fseek(output, 0, SEEK_END);
			posin = furthest;
		}
		fseek(input, furthest, SEEK_SET);
		fprintf(stderr, "Furthest: %08x\n", furthest);
	}
	// Finish up rest of patch file
	fseek(output, furthest, SEEK_SET);
	fread(bigbuf, sizeof(char), 1, input);
	while(!feof(input)) {
		fwrite(bigbuf, sizeof(char), 1, output);
		fread(bigbuf, sizeof(char), 1, input);
	}

}

