/*
	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 <stdio.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <d3d9.h>
#include "manifestitem.h"
#include "texture.h"
#include "emptyfile.h"
#include "dumpfile.h"
#include "aptaptdata.h"
#include "aptconstdata.h"
#include "aptdatdata.h"
#include "aptgeometrydata.h"
#include "road.h"
#include "logiccommandset.h"
#include "tinyxml.h"
#include "terraintextureatlas.h"
#include "gamescriptlist.h"
#include "gamemap.h"
#include "missionobjectivelist.h"
LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;
HWND hWnd = NULL;
const char *HiddenWindowClass = "HiddenWindowClass";
HWND CreateHiddenWindow()
{
	WNDCLASS wnd;
	ZeroMemory(&wnd, sizeof(WNDCLASS));
	wnd.style = CS_HREDRAW | CS_VREDRAW;
	wnd.lpfnWndProc = &DefWindowProc;
	wnd.hInstance = GetModuleHandle(NULL);
	wnd.hbrBackground = (HBRUSH)COLOR_WINDOW;
	wnd.lpszClassName = HiddenWindowClass;
	if (RegisterClass(&wnd)==0)
	{
		return NULL;
	}
	return CreateWindow(HiddenWindowClass, "ResourceCompiler", WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
}

void AddImports(TiXmlNode* entry, ManifestItem *t)
{
	TiXmlNode* import;
	for (import = entry->FirstChild(); import != 0; import = import->NextSibling())
	{
		if (import->Type() == TiXmlNode::ELEMENT)
		{
			if (!_stricmp(import->Value(),"Import"))
			{
				unsigned int hash1,hash2,imp;
				import->ToElement()->Attribute("Hash1",&hash1);
				import->ToElement()->Attribute("Hash2",&hash2);
				import->ToElement()->Attribute("Import",&imp);
				t->AddImport(hash1,hash2,imp);
			}
		}
	}
}

int main(int argc, char* argv[])
{
	d3d = Direct3DCreate9(D3D_SDK_VERSION);
	hWnd = CreateHiddenWindow();
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
	d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);
	printf("C&C3 compiled fileset builder tool\n");
	char *c = _strdup(argv[0]);
	strrchr(c,'\\')[0] = 0;
	SetCurrentDirectory(c);
	if (argc < 2)
	{
		printf("Usage: binmaker <xml input file>\n");
		printf("XML input file is the name of the xml file to use as input\n");
		printf("see readme.txt for the format of this xml file\n");
		return 1;
	}
	ManifestFile *mf = new ManifestFile();
	TiXmlDocument doc(argv[1]);
	doc.LoadFile();
	TiXmlNode* node = doc.FirstChild();
	while ((node->Type() != TiXmlNode::ELEMENT) && (_stricmp(node->Value(),"ManifestFile")))
	{
		node = node->NextSibling();
	}
	const char *filename = node->ToElement()->Attribute("Filename");
	node->ToElement()->Attribute("BinSize",&(mf->binsize));
	node->ToElement()->Attribute("MaxStreamSize",&(mf->maxstreamsize));
	node->ToElement()->Attribute("MaxReloSize",&(mf->maxrelosize));
	node->ToElement()->Attribute("MaxImpSize",&(mf->maximpsize));
	TiXmlNode* entry;
	for (entry = node->FirstChild(); entry != 0; entry = entry->NextSibling())
	{
		if (entry->Type() == TiXmlNode::ELEMENT)
		{
			if (!_stricmp(entry->Value(),"Include"))
			{
				unsigned char type = 0;
				unsigned int x;
				const char *importname = entry->ToElement()->Attribute("Filename");
				entry->ToElement()->Attribute("Type",&x);
				type = (unsigned char)x;
				mf->AddInclude(importname,type);
			}
			if (!_stricmp(entry->Value(),"Texture"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				const char *texturename = entry->ToElement()->Attribute("Filename");
				unsigned int hash2,hash4;
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash4",&hash4);
				Texture *t = new Texture(streamname,sourcename,texturename,hash2,hash4);
				mf->Items.push_back(t);
				AddImports(entry,t);
			}
			if (!_stricmp(entry->Value(),"EmptyFile"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				unsigned int hash1,hash2,hash3,hash4;
				entry->ToElement()->Attribute("Hash1",&hash1);
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash3",&hash3);
				entry->ToElement()->Attribute("Hash4",&hash4);
				EmptyFile *t = new EmptyFile(streamname,sourcename,hash1,hash2,hash3,hash4);
				mf->Items.push_back(t);
				AddImports(entry,t);
			}
			if (!_stricmp(entry->Value(),"DumpFile"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				char *df = (char *)entry->ToElement()->Attribute("Dump");
				strchr(df,':')[0] = '_';
				FILE *fd = fopen(df,"rb");
				fseek(fd,0,SEEK_END);
				unsigned int size = ftell(fd);
				fseek(fd,0,SEEK_SET);
				char *dc = new char[size];
				fread(dc,size,1,fd);
				fclose(fd);
				unsigned int hash1,hash2,hash3,hash4;
				entry->ToElement()->Attribute("Hash1",&hash1);
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash3",&hash3);
				entry->ToElement()->Attribute("Hash4",&hash4);
				DumpFile *t = new DumpFile(streamname,sourcename,hash1,hash2,hash3,hash4,dc,size);
				mf->Items.push_back(t);
				AddImports(entry,t);
				TiXmlNode* relo;
				for (relo = entry->FirstChild(); relo != 0; relo = relo->NextSibling())
				{
					if (relo->Type() == TiXmlNode::ELEMENT)
					{
						if (!_stricmp(relo->Value(),"Relocation"))
						{
							unsigned int loc;
							relo->ToElement()->Attribute("Loc",&loc);
							t->RelocationEntries.push_back(loc);
						}
					}
				}
			}
			if (!_stricmp(entry->Value(),"AptAptData"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				const char *texturename = entry->ToElement()->Attribute("Filename");
				unsigned int hash2,hash4;
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash4",&hash4);
				AptAptData *t = new AptAptData(streamname,sourcename,texturename,hash2,hash4);
				mf->Items.push_back(t);
				AddImports(entry,t);
			}
			if (!_stricmp(entry->Value(),"AptConstData"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				const char *texturename = entry->ToElement()->Attribute("Filename");
				unsigned int hash2,hash4;
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash4",&hash4);
				AptConstData *t = new AptConstData(streamname,sourcename,texturename,hash2,hash4);
				mf->Items.push_back(t);
				AddImports(entry,t);
			}
			if (!_stricmp(entry->Value(),"AptDatData"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				const char *texturename = entry->ToElement()->Attribute("Filename");
				unsigned int hash2,hash4;
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash4",&hash4);
				AptDatData *t = new AptDatData(streamname,sourcename,texturename,hash2,hash4);
				mf->Items.push_back(t);
				AddImports(entry,t);
			}
			if (!_stricmp(entry->Value(),"AptGeometryData"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				const char *texturename = entry->ToElement()->Attribute("Filename");
				unsigned int hash2,hash4,geometrynumber;
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash4",&hash4);
				entry->ToElement()->Attribute("GeometryNumber",&geometrynumber);
				AptGeometryData *t = new AptGeometryData(streamname,sourcename,texturename,hash2,hash4,geometrynumber);
				mf->Items.push_back(t);
				AddImports(entry,t);
			}
			if (!_stricmp(entry->Value(),"Road"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				unsigned int hash2,hash4,roadhasnormal;
				float roadwidth,roadwidthintexture,roadpriority;
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash4",&hash4);
				entry->ToElement()->Attribute("RoadWidth",&roadwidth);
				entry->ToElement()->Attribute("RoadWidthInTexture",&roadwidthintexture);
				entry->ToElement()->Attribute("RoadPriority",&roadpriority);
				entry->ToElement()->Attribute("RoadHasNormal",&roadhasnormal);
				Road *t = new Road(streamname,sourcename,hash2,hash4,roadwidth,roadwidthintexture,roadpriority,roadhasnormal);
				mf->Items.push_back(t);
				AddImports(entry,t);
			}
			if (!_stricmp(entry->Value(),"LogicCommandSet"))
			{
				const char *streamname = entry->ToElement()->Attribute("StreamName");
				const char *sourcename = entry->ToElement()->Attribute("SourceName");
				unsigned int hash2,hash4,count;
				entry->ToElement()->Attribute("Hash2",&hash2);
				entry->ToElement()->Attribute("Hash4",&hash4);
				entry->ToElement()->Attribute("CommandCount",&count);
				LogicCommandSet *t = new LogicCommandSet(streamname,sourcename,hash2,hash4,count);
				mf->Items.push_back(t);
				AddImports(entry,t);
			}
			if (!_stricmp(entry->Value(),"TerrainTextureAtlas"))
			{
				TerrainTextureAtlas *t = new TerrainTextureAtlas(entry);
				mf->Items.push_back(t);
			}
			if (!_stricmp(entry->Value(),"GameScriptList"))
			{
				GameScriptList *t = new GameScriptList(entry);
				mf->Items.push_back(t);
			}
			if (!_stricmp(entry->Value(),"GameMap"))
			{
				GameMap *t = new GameMap(entry);
				mf->Items.push_back(t);
			}
			if (!_stricmp(entry->Value(),"MissionObjectiveList"))
			{
				MissionObjectiveList *t = new MissionObjectiveList(entry);
				mf->Items.push_back(t);
			}
		}
	}
	mf->Save(filename);
	d3ddev->Release();
	d3d->Release();
}
