/*
	BinMaker program
	Copyright 2007 Jonathan Wilson
	Portions lifted from BinOpener copyright 2007 booto (temptemp91 at hotmail dot com)

	This file is part of binmaker.

	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 2 of the License, or
	(at your option) any later version.

	binmaker 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, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/
#include "manifestitem.h"
#include <algorithm>
#include <string.h>

void ManifestItem::calcsize(ManifestFile *m)
{
	Header.extraentryoffset = 0;
	Header.extraentrycount = 0;
	Header.streamnameoffset = m->Header.streamnamessize;
	Header.sourcenameoffset = m->Header.sourcenamessize;
	Header.extraentryoffset = m->Header.extraentriessize;
	Header.extraentrycount = ExtraEntries.size();
	Header.streamsize = StreamSize;
	if (Header.streamsize)
	{
		Header.reloentrysize = RelocationEntries.size() * 4;
		Header.impentrysize = ImpEntries.size() * 4;
		if (Header.reloentrysize)
		{
			Header.reloentrysize += 4;
		}
		if (Header.impentrysize)
		{
			Header.impentrysize += 4;
		}
		m->Header.binsize += StreamSize;
	}
	else
	{
		Header.reloentrysize = 0;
		Header.impentrysize = 0;
	}
	m->Header.streamnamessize += (strlen(StreamName) + 1);
	m->Header.sourcenamessize += (strlen(SourceName) + 1);
	m->Header.extraentriessize += ExtraEntries.size() * sizeof(ExtraEntry);
	if (Header.streamsize)
	{
		if (StreamSize > m->Header.maxstreamsize)
		{
			m->Header.maxstreamsize = StreamSize;
		}
		if ((RelocationEntries.size()) && (((RelocationEntries.size() + 1) * 4) > m->Header.maxrelosize))
		{
			m->Header.maxrelosize = ((RelocationEntries.size() + 1) * 4);
		}
		if ((ImpEntries.size()) && (((ImpEntries.size() + 1) * 4) > m->Header.maximpsize))
		{
			m->Header.maximpsize = ((ImpEntries.size() + 1) * 4);
		}
	}
}

void ManifestItem::save(ManifestFile *m)
{
	strcpy(m->Streams + m->StreamPos,StreamName);
	m->StreamPos += strlen(StreamName);
	m->StreamPos++;
	strcpy(m->Sources + m->SourcePos,SourceName);
	m->SourcePos += strlen(SourceName);
	m->SourcePos++;
	fwrite(&Header,sizeof(Header),1,m->Manifest);
	fwrite(StreamData,StreamSize,1,m->Bin);
	unsigned int x = 0xFFFFFFFF;
	if (Header.reloentrysize)
	{
		std::sort(RelocationEntries.begin(),RelocationEntries.end());
		for (unsigned int i = 0;i < RelocationEntries.size();i++)
		{
			fwrite(&RelocationEntries[i],4,1,m->Relo);
		}
		fwrite(&x,4,1,m->Relo);
	}
	if (Header.impentrysize)
	{
		for (unsigned int i = 0;i < ImpEntries.size();i++)
		{
			fwrite(&ImpEntries[i],4,1,m->Imp);
		}
		fwrite(&x,4,1,m->Imp);
	}
	for (unsigned int i = 0;i < ExtraEntries.size();i++)
	{
		m->ExtraEntries[m->ExtraEntryPos].filehash1 = ExtraEntries[i]->filehash1;
		m->ExtraEntries[m->ExtraEntryPos].filehash2 = ExtraEntries[i]->filehash2;
		m->ExtraEntryPos++;
	}
}

void ManifestItem::AddImport(unsigned int filehash1,unsigned int filehash2,unsigned int import)
{
	ExtraEntry *entry = new ExtraEntry;
	entry->filehash1 = filehash1;
	entry->filehash2 = filehash2;
	ExtraEntries.push_back(entry);
	ImpEntries.push_back(import);
}

unsigned long hash_function(const char *input, int length)
{
	unsigned long num_blocks, extra, current_val;
	if(length == 0)
	{
		return 0x1337c0de;
	}
	num_blocks = length / 4;
	extra = length % 4;
	current_val = length;
	while(num_blocks--)
	{
		current_val += input[1]<<8 | input[0];
		input += 4;
		current_val ^= ((input[-1]<<8 | input[-2]) ^ (current_val<<5))<<11;
		current_val += current_val>>11;
	}
	if(extra)
	{
		switch(extra)
		{
			case 1:
				current_val += input[0];
				current_val = (current_val<<10)^current_val;
				current_val += current_val>>1;
				break;
			case 2:
				current_val += input[1]<<8 | input[0];
				current_val ^= (current_val<<11);
				current_val += current_val>>17;
				break;
			case 3:
				current_val += input[1]<<8 | input[0];
				current_val ^= (current_val ^ (input[2]<<2))<<16;
				current_val += current_val>>11;
				break;
		}
	}
	current_val ^= (current_val<<3);
	current_val += (current_val>>5);
	current_val ^= (current_val<<2);
	current_val += (current_val>>15);
	current_val ^= (current_val<<10);
	return current_val;
}
