///////////////////////////////////////////////////////////
// BDP-S390 Firmware Modification Code:
//    Binary File Insertion
//--------------------------------------------------------
// Copyright (C) 2013 Malcolm Stagg
///////////////////////////////////////////////////////////

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string.h>

using namespace std;

int main(int argc, char * argv[]) {
	ifstream infile, infile_ins;
	ofstream outfile;
	
	if (argc != 5) {
		cout << "Usage: insertfile [decoded bin input] [file number] [file to insert] [decoded bin output]" << endl;
		exit(1);
	}
	char * infilename = argv[1];
	int filenum = atoi(argv[2]);
	char * infilename_ins = argv[3];
	char * outfilename = argv[4];
	
	infile.open(infilename, ifstream::binary);
	infile_ins.open(infilename_ins, ifstream::binary);
	
	if (!infile || !infile_ins) {
		cout << "cannot open input file" << endl;
		exit(1);
	}
	
	//get size of insert file
	infile_ins.seekg(0, ios::end);
	int filelen = infile_ins.tellg();
	infile_ins.seekg(0, ios::beg);
	
	//read header
	char binheader[0xf0];
	
	infile.read(binheader, 0xf0);
	
	int binfilesize = *((int *)(binheader+0x5C));
	
	//subfile locations:
	int fileloc[10], filesize[10];
	int maxend = 0xf0;
	int minstart = 0x00ffffff;
	for (int i=0;i<10;i++) {
		fileloc[i] = *((int *)(binheader+0xa0+8*i));
		filesize[i] = *((int *)(binheader+0xa4+8*i));
		if (fileloc[i]+filesize[i]>maxend) maxend=fileloc[i]+filesize[i];
		if (fileloc[i]>=0xf0 && fileloc[i]<minstart) minstart=fileloc[i];
	}
	
	if (minstart != 0xf0)  {
		cout << "ERROR: first file should start at 0xf0 but actually starts at " << hex << minstart << endl;
		exit(1);
	} else if (maxend != binfilesize) {
		cout << "ERROR: last file should go to the end of the file (" << binfilesize << ") not " << hex << maxend << endl;
		exit(1);
	}
	
	int curloc = 0;
	
	if (fileloc[filenum] <= 0xf0) {
		fileloc[filenum] = maxend;
		filesize[filenum] = 0;
	}
		
	int sizediff = filelen - filesize[filenum];
	
	//modify table
	int oldbinfilesize = binfilesize;
	filesize[filenum] = filelen;
	binfilesize += sizediff;
	for (int i=0;i<10;i++)
		if (fileloc[i] > fileloc[filenum]) fileloc[i]+=sizediff;
	
	for (int i=0;i<10;i++) {
		*((int *)(binheader+0xa0+8*i)) = fileloc[i];
		*((int *)(binheader+0xa4+8*i)) = filesize[i];
	}
	
	*((int *)(binheader+0x5C)) = binfilesize;
	
	outfile.open(outfilename, fstream::binary);
	
	if (!outfile) {
		cout << "cannot open output file" << endl;
		exit(1);
	}
	
	//write the header
	outfile.write(binheader, 0xf0);
	
	//write up to offset
	infile.seekg(0xf0, ios::beg);
	char * filedata = (char *)malloc(4096);
	if (!filedata) {
		cout << "ERROR: Cannot allocate array!!!\n";
		exit(1);
	}
	for (int i=0xf0;i<fileloc[filenum];i+=4096) {
		infile.read(filedata, (fileloc[filenum]-i<4096?fileloc[filenum]-i:4096));
		outfile.write(filedata, (fileloc[filenum]-i<4096?fileloc[filenum]-i:4096));
	}
	//copy new file
	infile_ins.seekg(0, ios::beg);
	for (int i=0;i<filelen;i+=4096) {
		infile_ins.read(filedata, (filelen-i<4096?filelen-i:4096));
		outfile.write(filedata, (filelen-i<4096?filelen-i:4096));
	}
	//write rest of file (fileloc[filenum]+old size to end)
	infile.seekg(fileloc[filenum]+filelen-sizediff, ios::beg);
	for (int i=fileloc[filenum]+filelen-sizediff;i<oldbinfilesize;i+=4096) {
		infile.read(filedata, (oldbinfilesize-i<4096?oldbinfilesize-i:4096));
		outfile.write(filedata, (oldbinfilesize-i<4096?oldbinfilesize-i:4096));
	}
	free(filedata);
	
	outfile.close();
	infile.close();
	infile_ins.close();
}

