/* itemcache.wdb parser by dfighter */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#include "cgic.h"
#include "bsd-base64.h"

#include "apspells.h"
#include "arraylist.h"

#define NUMSTATFIELDS 20
#define HDR "id,recordlength,class,subclass,unk1,name1,name2,name3,name4,modelid,quality,flags,buyprice,sellprice,inventoryslot,allowableclass,allowablerace,itemlevel,requiredlevel,requiredskill,requiredskill_level,requiredspell,unknown,unknown,reqfaction,reqfactionstanding,isunique,stackamount,containerslots,itemstatcount,stat1,stat1value,stat2,stat2value,stat3,stat3value,stat4,stat4value,stat5,stat5value,stat6,stat6value,stat7,stat7value,stat8,stat8value,stat9,stat9value,stat10,stat10value,unknown,unknown,dmg1min,dmg1max,dmg1type,dmg2min,dmg2max,dmg2type,dmg3min,dmg3max,dmg3type,dmg4min,dmg4max,dmg4type,dmg5min,dmg5max,dmg5type,armor,holy_resistance,fire_resistance,nature,frost_resistance,shadow_resistance,arcane_resistance,delay,ammotype,range,spellid1,spelltrigger1,spellcharges1,spellcooldown1,spellcategory1,spellcategory1cooldown,spellid2,spelltrigger2,spellcharges2,spellcooldown2,spellcategory2,spellcategory2cooldown,spellid3,spelltrigger3,spellcharges3,spellcooldown3,spellcategory3,spellcategory3cooldown,spellid4,spelltrigger4,spellcharges4,spellcooldown4,spellcategory4,spellcategory4cooldown,spellid5,spelltrigger5,spellcharges5,spellcooldown5,spellcategory5,spellcategory5cooldown,bondid,Description,page_id,page_language,page_material,questid,unknown,lock_material,sheatid,randomprop,randomsuffix,block,itemset,durability,unknown,unknown,bagfamily,totemcategory,socket_color1,socket_content_1,socket_color2,socket_content_2,socket_color3,socket_content_3,socket_bonus,gemproperties,requireddisenchantingskill,armordmgmodifier,unknown,unknown"

typedef unsigned long uint32;
typedef long int32;

typedef struct WDBheader{
	char id[5];
	uint32 build;
	char locale[5];
	uint32 unk1;
	uint32 unk2;
}WDB_HEADER;

#define ITEM_MOD_MANA 0
#define ITEM_MOD_HEALTH 1
#define ITEM_MOD_AGILITY 3
#define ITEM_MOD_STRENGTH 4
#define ITEM_MOD_INTELLECT 5
#define ITEM_MOD_SPIRIT 6
#define ITEM_MOD_STAMINA 7
#define ITEM_MOD_DEFENSE_SKILL_RATING 12
#define ITEM_MOD_DODGE_RATING 13
#define ITEM_MOD_PARRY_RATING 14
#define ITEM_MOD_BLOCK_RATING 15
#define ITEM_MOD_HIT_MELEE_RATING 16
#define ITEM_MOD_HIT_RANGED_RATING 17
#define ITEM_MOD_HIT_SPELL_RATING 18
#define ITEM_MOD_CRIT_MELEE_RATING 19
#define ITEM_MOD_CRIT_RANGED_RATING 20
#define ITEM_MOD_CRIT_SPELL_RATING 21
#define ITEM_MOD_HIT_TAKEN_MELEE_RATING 22
#define ITEM_MOD_HIT_TAKEN_RANGED_RATING 23
#define ITEM_MOD_HIT_TAKEN_SPELL_RATING 24
#define ITEM_MOD_CRIT_TAKEN_MELEE_RATING 25
#define ITEM_MOD_CRIT_TAKEN_RANGED_RATING 26
#define ITEM_MOD_CRIT_TAKEN_SPELL_RATING 27
#define ITEM_MOD_HASTE_MELEE_RATING 28
#define ITEM_MOD_HASTE_RANGED_RATING 29
#define ITEM_MOD_HASTE_SPELL_RATING 30
#define ITEM_MOD_HIT_RATING 31
#define ITEM_MOD_CRIT_RATING 32
#define ITEM_MOD_HIT_TAKEN_RATING 33
#define ITEM_MOD_CRIT_TAKEN_RATING 34
#define ITEM_MOD_RESILIENCE_RATING 35
#define ITEM_MOD_HASTE_RATING 36
#define ITEM_MOD_EXPERTISE_RATING 37
#define ITEM_MOD_ATTACK_POWER 38
#define ITEM_MOD_ARMOR_PENETRATION 44

enum
{
	SLOT_NONEQUIP=0,
	SLOT_HEAD,
	SLOT_NECK,
	SLOT_SHOULDER,
	SLOT_SHIRT,
	SLOT_CHEST,
	SLOT_WAIST,
	SLOT_LEGS,
	SLOT_FEET,
	SLOT_WRIST,
	SLOT_HANDS,
	SLOT_FINGER,
	SLOT_TRINKET,
	SLOT_WEAPON,
	SLOT_SHIELD,
	SLOT_RANGED,
	SLOT_BACK,
	NUM_SLOTS
};

ArrayList *itemlist[NUM_SLOTS];

typedef struct itemsWDBstruct{
	uint32 id;
	uint32 recordlength;
	uint32 class;
	uint32 subclass;
	uint32 unknown;
	char name1[100];
	char name2[100];
	char name3[100];
	char name4[100];
	uint32 modelid;
	uint32 quality;
	uint32 unknown3; /* some item flag */
	uint32 buyprice;
	uint32 sellprice;
	uint32 inventoryslot;
	int32 allowableclass; /* allowableclass? */
	int32 unknown5; /* allowablerace? */
	uint32 itemlevel;
	uint32 requiredlevel;
	uint32 unknown6;
	uint32 unknown7;
	uint32 unknown8;
	uint32 unknown9;
	uint32 unknown10;
	uint32 unknown11;
	uint32 unknown12;
	uint32 unknown13;
	uint32 maxcount;
	uint32 unknown14; /* containerslots?*/
	uint32 unknown15; /* number of stat fields / 2 (because of stat,statval)*/
	int32 stat[NUMSTATFIELDS];
	uint32 unknown18; 
	uint32 unknown19;  
	float unknown20; /* dmgmin */
	float unknown21; /* dmgmax */
	uint32 unknown22;
	float unknown23; /* */
	float unknown24; /*damage*/
	uint32 unknown25;
	float unknown26; /**/
	float unknown27; /**/
	uint32 unknown28;
	float unknown29; /**/
	float unknown30; /**/
	uint32 unknown31;
	float unknown32; /**/
	float unknown33; /**/
	uint32 unknown34;
	uint32 armor;
	uint32 unknown35;
	uint32 unknown36;
	uint32 unknown37;
	uint32 unknown38;
	uint32 unknown39;
	uint32 unknown40;
	uint32 unknown41; /* ammotype */
	uint32 unknown42;
	float unknown43; /*range */
	uint32 spellid1; /* spellid1 */
	uint32 unknown45; /* spelltrigger1 */
	uint32 unknown46; /* spellcharges1 */
	uint32 unknown47; /* spellcooldown1 */
	uint32 unknown48; /* spellcat1 */
	uint32 unknown49; /* spellcatcd1 */
	uint32 spellid2; /* spellid2 */
	uint32 unknown51;
	uint32 unknown52;
	uint32 unknown53;
	uint32 unknown54;
	uint32 unknown55;
	uint32 spellid3; /* spellid3 */
	uint32 unknown57;
	uint32 unknown58;
	uint32 unknown59;
	uint32 unknown60;
	uint32 unknown61;
	uint32 spellid4; /* spellid4 */
	uint32 unknown63;
	uint32 unknown64;
	uint32 unknown65;
	uint32 unknown66;
	uint32 unknown67;
	uint32 spellid5; /* spellid5 */
	uint32 unknown69;
	uint32 unknown70;
	uint32 unknown71;
	uint32 unknown72;
	uint32 unknown73;
	uint32 bondid;
	char   description[500];
	uint32 unknown74; // pageid
	uint32 unknown75; // pagelanguage
	uint32 unknown76; // pagematrial
	uint32 unknown77; // questid
	uint32 unknown78;
	uint32 unknown79; // lock material
	uint32 unknown80; // sheetid
	uint32 unknown81; // randomprop
	uint32 unknown82; // randomsuffix
	uint32 unknown83; // block
	uint32 unknown84; // itemset
	uint32 unknown85; // durability
	uint32 unknown86;
	uint32 unknown87;
	uint32 unknown88; // bagfamily
	uint32 unknown89; // totemcategory
	uint32 socketcolor1; // socket color 1
	uint32 unknown91; // socket content 1
	uint32 socketcolor2; // socket color 2
	uint32 unknown93; // socket content 2
	uint32 socketcolor3; // socket color 3
	uint32 unknown95; // socket content 3
	uint32 unknown96;
	uint32 unknown97;
	uint32 unknown98;
	float unknown99; /* armordmg mod*/
	uint32 unknown100;
	uint32 unknown101;
	float dps_value;
}WDB_ITEM;

typedef struct stat_mods_s {
	float str;
	float agi;
	float crit;
	float hit;
	float exp;
	float arpen;
	float haste;
	float armor;
} stat_mods;

stat_mods modifiers;

void set_default_modifiers()
{
	/*
	STR	3.00
	AGI	0.85
	Hit	1.90 (0.60)
	Crit	1.10
	Haste	0.70
	Exp	1.10
	Arp	0.70
	Armor	0.027
	*/
	modifiers.str = 3.000f;
	modifiers.agi = 0.850f;
	modifiers.crit = 1.100f;
	modifiers.hit = 1.900f;
	modifiers.exp = 1.100f;
	modifiers.arpen = 0.700f;
	modifiers.haste = 0.700f;
	modifiers.armor = 0.027f;
}

FILE* readstr(FILE* fp, char* str){
	int i = 0;
	unsigned char ch = 127;
	
	while(ch != 0){
		fread(&ch,sizeof(char),1,fp);
		if(ch == 255) ch = 0;
		if(ch == 127) ch = 0;
		str[i++] = ch;
	}
	return fp;
}

int deathknight_exclusive(int allowableclass)
{
	/*
	1	Warrior
	2	Paladin
	4	Hunter
	8	Rogue
	16	Priest
	32	Death Knight
	64	Shaman
	128	Mage
	256	Warlock
	1024	Druid 
	*/
	if(allowableclass & 1 &&
		allowableclass & 2 &&
		allowableclass & 4 &&
		allowableclass & 8 &&
		allowableclass & 16 &&
		allowableclass & 64 &&
		allowableclass & 128 &&
		allowableclass & 256 &&
		allowableclass & 1024)
	{
		return 0;
	}
	
	if(allowableclass & 32)
		return 1;
	
	return 0;
}

int usable_by_dk(WDB_ITEM *item)
{
	if(item->allowableclass & 32)
		return 1;
	return 0;
}

int item_equal(void *vlocation1, void *vlocation2)
{
	WDB_ITEM *location1;
	WDB_ITEM *location2;

	location1 = (WDB_ITEM *) vlocation1;
	location2 = (WDB_ITEM *) vlocation2;

	return location1->dps_value == location2->dps_value;
}

int item_compare(void *vlocation1, void *vlocation2)
{
	WDB_ITEM *location1;
	WDB_ITEM *location2;

	location1 = (WDB_ITEM *) vlocation1;
	location2 = (WDB_ITEM *) vlocation2;

	if (location1->dps_value > location2->dps_value) {
		return -1;
	} else if (location1->dps_value < location2->dps_value) {
		return 1;
	} else {
		return 0;
	}
}

float findapspells(WDB_ITEM *item)
{
	int i=0;
	float retval=0;
	while(apsp_array[i].spellid != -1)
	{
		if(item->spellid1 == apsp_array[i].spellid ||
			item->spellid2 == apsp_array[i].spellid ||
			item->spellid3 == apsp_array[i].spellid ||
			item->spellid4 == apsp_array[i].spellid ||
			item->spellid5 == apsp_array[i].spellid)
		{
			retval += apsp_array[i].ap;
		}
		i++;
	}
	return retval;
}

void free_items_from_arraylist(ArrayList *list)
{
	int i;
	WDB_ITEM *temp_item;
	
	for(i=0; i<list->length; i++)
	{
		temp_item = (WDB_ITEM *)list->data[i];
		free(temp_item);
	}
}

float dps_value(WDB_ITEM *item)
{
	int num_stats = item->unknown15;
	int i;
	float retval=0.0;
	
	for(i=0; i<num_stats; i++)
	{
		switch(item->stat[i*2])
		{
		case ITEM_MOD_STRENGTH:
			retval += item->stat[i*2+1] * modifiers.str;
			break;
		case ITEM_MOD_AGILITY:
			retval += item->stat[i*2+1] * modifiers.agi;
			break;
		case ITEM_MOD_CRIT_RATING:
			retval += item->stat[i*2+1] * modifiers.crit;
			break;
		case ITEM_MOD_HIT_RATING:
			retval += item->stat[i*2+1] * modifiers.hit;
			break;
		case ITEM_MOD_EXPERTISE_RATING:
			retval += item->stat[i*2+1] * modifiers.exp;
			break;
		case ITEM_MOD_HASTE_RATING:
			retval += item->stat[i*2+1] * modifiers.haste;
			break;
		case ITEM_MOD_ATTACK_POWER:
			retval += item->stat[i*2+1];
			break;
		case ITEM_MOD_ARMOR_PENETRATION:
			retval += item->stat[i*2+1] * modifiers.arpen;
			break;
		}
	}
	
	// look up attack power spells (will have to do this
	// for arpen spells also)
	retval += findapspells(item);
	
	// hacky sockets, assume a +16 str gem
	if(item->socketcolor1 > 1) retval += 16.0f * modifiers.str;
	if(item->socketcolor2 > 1) retval += 16.0f * modifiers.str;
	if(item->socketcolor3 > 1) retval += 16.0f * modifiers.str;
	// check for meta socket
	if(item->socketcolor1 == 1) retval += 100.0f;
	if(item->socketcolor2 == 1) retval += 100.0f;
	if(item->socketcolor3 == 1) retval += 100.0f;

	// add armor
	retval += item->armor * modifiers.armor;
	
	return retval;
}

int read_items(void){
	WDB_HEADER itemshdr;
	WDB_ITEM *itemswdb;
	FILE* fp = NULL;
	uint32 i = 0;

	fp = fopen("itemcache.wdb","rb");
	if(fp == NULL){
		fprintf(cgiOut,"ERROR: cannot open input file.\n");
		return -1;
	}

	for(i=0; i<NUM_SLOTS; i++)
		itemlist[i] = arraylist_new(32);
	
	/*
	fpout = fopen("itemcache.csv","wt");
	if(fpout == NULL){
		printf("ERROR: cannot open output file.\n");
		return -1;
	}*/

	/* Reading header and checking for correct signature + build */
	fread(&(itemshdr.id),sizeof(char)*4,1,fp);
	itemshdr.id[4] = '\0';
	fread(&(itemshdr.build),sizeof(uint32),1,fp);
	fread(&(itemshdr.locale),sizeof(char)*4,1,fp);
	itemshdr.locale[4] = '\0';
	fread(&(itemshdr.unk1),sizeof(uint32),1,fp);
	fread(&(itemshdr.unk2),sizeof(uint32),1,fp);

	if(strcmp(itemshdr.id,"BDIW")!=0){
		fprintf(cgiOut,"ERROR: input file is not a itemcache file.\n");
		fclose(fp);
		return -1;
	}
	/* if(itemshdr.build != 8714){
		printf("ERROR: input file is not a build 8714 itemcache file.\n");
		fclose(fp);
		return -1;
	}
	*/


	/* Reading the records and dumping them into a csv format */

	while(feof(fp)==0){
		itemswdb = (WDB_ITEM*)malloc(sizeof(WDB_ITEM));
		for(i = 0; i < NUMSTATFIELDS; i++)
			itemswdb->stat[i] = 0;
		fread(&(itemswdb->id),sizeof(uint32),1,fp);
		fread(&(itemswdb->recordlength),sizeof(uint32),1,fp);
		fread(&(itemswdb->class),sizeof(uint32),1,fp);
		fread(&(itemswdb->subclass),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown),sizeof(uint32),1,fp);
		
		fp = readstr(fp,itemswdb->name1);
		fp = readstr(fp,itemswdb->name2);
		fp = readstr(fp,itemswdb->name3);
		fp = readstr(fp,itemswdb->name4);

		fread(&(itemswdb->modelid),sizeof(uint32),1,fp);
		fread(&(itemswdb->quality),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown3),sizeof(uint32),1,fp);
		fread(&(itemswdb->buyprice),sizeof(uint32),1,fp);
		fread(&(itemswdb->sellprice),sizeof(uint32),1,fp);
		fread(&(itemswdb->inventoryslot),sizeof(uint32),1,fp);
		fread(&(itemswdb->allowableclass),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown5),sizeof(uint32),1,fp);
		fread(&(itemswdb->itemlevel),sizeof(uint32),1,fp);
		fread(&(itemswdb->requiredlevel),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown6),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown7),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown8),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown9),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown10),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown11),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown12),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown13),sizeof(uint32),1,fp);
		fread(&(itemswdb->maxcount),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown14),sizeof(uint32),1,fp); /* containerslots */
		fread(&(itemswdb->unknown15),sizeof(uint32),1,fp); /* number of stat fields / 2 */
		for(i = 0; i < itemswdb->unknown15*2; i++)
			fread(&(itemswdb->stat[i]),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown18),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown19),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown20),sizeof(float),1,fp);
		fread(&(itemswdb->unknown21),sizeof(float),1,fp);
		fread(&(itemswdb->unknown22),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown23),sizeof(float),1,fp);
		fread(&(itemswdb->unknown24),sizeof(float),1,fp);
		fread(&(itemswdb->unknown25),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown26),sizeof(float),1,fp);
		fread(&(itemswdb->unknown27),sizeof(float),1,fp);
		fread(&(itemswdb->unknown28),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown29),sizeof(float),1,fp);
		fread(&(itemswdb->unknown30),sizeof(float),1,fp);
		fread(&(itemswdb->unknown31),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown32),sizeof(float),1,fp);
		fread(&(itemswdb->unknown33),sizeof(float),1,fp);
		fread(&(itemswdb->unknown34),sizeof(uint32),1,fp);
		fread(&(itemswdb->armor),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown35),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown36),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown37),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown38),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown39),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown40),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown41),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown42),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown43),sizeof(float),1,fp);
		fread(&(itemswdb->spellid1),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown45),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown46),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown47),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown48),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown49),sizeof(uint32),1,fp);
		fread(&(itemswdb->spellid2),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown51),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown52),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown53),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown54),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown55),sizeof(uint32),1,fp);
		fread(&(itemswdb->spellid3),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown57),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown58),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown59),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown60),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown61),sizeof(uint32),1,fp);
		fread(&(itemswdb->spellid4),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown63),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown64),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown65),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown66),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown67),sizeof(uint32),1,fp);
		fread(&(itemswdb->spellid5),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown69),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown70),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown71),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown72),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown73),sizeof(uint32),1,fp);
		fread(&(itemswdb->bondid),sizeof(uint32),1,fp);
		fp = readstr(fp,itemswdb->description);
		fread(&(itemswdb->unknown74),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown75),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown76),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown77),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown78),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown79),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown80),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown81),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown82),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown83),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown84),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown85),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown86),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown87),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown88),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown89),sizeof(uint32),1,fp);
		fread(&(itemswdb->socketcolor1),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown91),sizeof(uint32),1,fp);
		fread(&(itemswdb->socketcolor2),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown93),sizeof(uint32),1,fp);
		fread(&(itemswdb->socketcolor3),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown95),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown96),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown97),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown98),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown99),sizeof(float),1,fp);
		fread(&(itemswdb->unknown100),sizeof(uint32),1,fp);
		fread(&(itemswdb->unknown101),sizeof(uint32),1,fp);

		itemswdb->dps_value = dps_value(itemswdb);

		if(itemswdb->inventoryslot > 0 &&
			itemswdb->inventoryslot < NUM_SLOTS &&
			usable_by_dk(itemswdb))
		{
			arraylist_append(itemlist[itemswdb->inventoryslot],itemswdb);
		}else{
			free(itemswdb);
		}
		
	}
	
	/* sort all item arraylists */
	for(i=0; i<NUM_SLOTS; i++)
		arraylist_sort(itemlist[i], &item_compare);

	fclose(fp);
	return 0;
}

void free_items()
{
	int i;
	for(i=0; i<NUM_SLOTS; i++)
	{
		free_items_from_arraylist(itemlist[i]);
		arraylist_free(itemlist[i]);
	}
}

void print_page_header()
{
	fprintf(cgiOut, "<HTML><HEAD>\n");
	fprintf(cgiOut, "<TITLE>Death Knight Gear Rankings</TITLE>\n");
	fprintf(cgiOut, "<script src=\"http://www.wowhead.com/widgets/power.js\"></script>\n");
	fprintf(cgiOut, "<link rel=\"stylesheet\" type=\"text/css\" href=\"dkrank.css\" />");
	fprintf(cgiOut, "</HEAD>\n");
	fprintf(cgiOut, "<BODY>\n");
	fprintf(cgiOut, "<div id=\"ev_bg\"><div id=\"everything\">\n");
	
	fprintf(cgiOut, "<div id=\"header\">\n");
	fprintf(cgiOut, "<H1>Death Knight Gear Rankings</H1>\n");
	fprintf(cgiOut, "</div>\n\n");
	
	fprintf(cgiOut, "<div id=\"middle\">\n");
}

void print_page_footer()
{
	fprintf(cgiOut, "</div>\n\n");
	fprintf(cgiOut, "<div id=\"footer\">\n");
	fprintf(cgiOut, "<span class=\"copyright\">World of Warcraft&reg; and Blizzard Entertainment&reg; are all trademarks or registered trademarks of Blizzard Entertainment in the United States and/or other countries. <br> These terms and all related materials, logos, and images are copyright &copy; Blizzard Entertainment. This site is in no way associated with or endorsed by Blizzard Entertainment&reg;.</span>");
	fprintf(cgiOut, "</div>\n\n");

	fprintf(cgiOut, "</div></div> <!-- ev_bg and everything closers -->\n\n");

	fprintf(cgiOut, "</BODY></HTML>\n");
}

void build_gem_string(char *str, WDB_ITEM *item)
{
	int gem1=0, gem2=0, gem3=0;
	if(item->socketcolor1 == 1) gem1 = 41285;
	if(item->socketcolor2 == 1) gem2 = 41285;
	if(item->socketcolor3 == 1) gem3 = 41285;
	if(item->socketcolor1 > 1) gem1 = 39996;
	if(item->socketcolor2 > 1) gem2 = 39996;
	if(item->socketcolor3 > 1) gem3 = 39996;
	
	if(gem1)
		sprintf(str, " rel=\"gems=%i\"", gem1);
	
	if(gem1 && gem2)
		sprintf(str, " rel=\"gems=%i:%i\"", gem1, gem2);
	
	if(gem1 && gem2 && gem3)
		sprintf(str, " rel=\"gems=%i:%i:%i\"", gem1, gem2, gem3);
}

void print_item_link(WDB_ITEM *item)
{
	char gemstr[80];
	gemstr[0] = '\0';
	build_gem_string(gemstr, item);
	fprintf(cgiOut, "<a href=\"http://www.wowhead.com/?item=%lu\"%s class=\"q%lu\">", item->id, gemstr, item->quality);
	fprintf(cgiOut, "%s", item->name1);
	fprintf(cgiOut, "</a>");
}

void print_table(char *title, ArrayList *items, int howmany)
{
	WDB_ITEM *temp_item;
	int i;
	fprintf(cgiOut, "<div class=\"post\">\n");
	
	fprintf(cgiOut,"<H1>%s</H1>\n",title);

	fprintf(cgiOut, "<div class=\"post_body\">\n");
	
	fprintf(cgiOut,"<TABLE class=\"gear\">\n");
	fprintf(cgiOut,"<TR class=\"tableheader\"><TD>Rank</TD><TD>EAP</TD><TD>ilvl</TD><TD>Item</TD></TR>\n");
	for(i=0; i<howmany && i<items->length; i++)
	{
		temp_item = (WDB_ITEM *)items->data[i];
		fprintf(cgiOut, "<TR class=\"");
		if(i%2)
			fprintf(cgiOut, "even");
		else
			fprintf(cgiOut, "odd");
		fprintf(cgiOut, "\"><TD>%i</TD><TD>%.2f</TD><TD>%lu</TD><TD>", i+1, temp_item->dps_value, temp_item->itemlevel);
		print_item_link(temp_item);
		fprintf(cgiOut, "</TD></TR>\n");
	}
	fprintf(cgiOut,"</TABLE>\n");
	fprintf(cgiOut, "</div></div>\n\n");
}

void parse_base64_mods()
{
	stat_mods input_vals;
	char input[80];
	short packed_values[9];
	cgiFormStringNoNewlines("mods", input, 80);
	
	if(strcmp(input, "") != 0)
	{
		b64_pton(input, (unsigned char*)packed_values, 17);
		
		input_vals.str = (float)packed_values[0] / 1000.0f;
		input_vals.agi = (float)packed_values[1] / 1000.0f;
		input_vals.crit = (float)packed_values[2] / 1000.0f;
		input_vals.hit = (float)packed_values[3] / 1000.0f;
		input_vals.exp = (float)packed_values[4] / 1000.0f;
		input_vals.arpen = (float)packed_values[5] / 1000.0f;
		input_vals.haste = (float)packed_values[6] / 1000.0f;
		input_vals.armor = (float)packed_values[7] / 1000.0f;
		
		if(input_vals.str >= 0.0f && input_vals.str <= 10.0f)
			modifiers.str = input_vals.str;
		if(input_vals.agi >= 0.0f && input_vals.agi <= 10.0f)
			modifiers.agi = input_vals.agi;
		if(input_vals.crit >= 0.0f && input_vals.crit <= 10.0f)
			modifiers.crit = input_vals.crit;
		if(input_vals.hit >= 0.0f && input_vals.hit <= 10.0f)
			modifiers.hit = input_vals.hit;
		if(input_vals.exp >= 0.0f && input_vals.exp <= 10.0f)
			modifiers.exp = input_vals.exp;
		if(input_vals.arpen >= 0.0f && input_vals.arpen <= 10.0f)
			modifiers.arpen = input_vals.arpen;
		if(input_vals.haste >= 0.0f && input_vals.haste <= 10.0f)
			modifiers.haste = input_vals.haste;
		if(input_vals.armor >= 0.0f && input_vals.armor <= 10.0f)
			modifiers.armor = input_vals.armor;
	}
}

void print_base64_link()
{
	/*int b64_ntop(u_char const *src, size_t srclength, char *target, 
		size_t targsize);*/
	unsigned char b64_modifiers[80];
	short packed_values[9];
	
	packed_values[0] = (short)(modifiers.str * 1000.0f);
	packed_values[1] = (short)(modifiers.agi * 1000.0f);
	packed_values[2] = (short)(modifiers.crit * 1000.0f);
	packed_values[3] = (short)(modifiers.hit * 1000.0f);
	packed_values[4] = (short)(modifiers.exp * 1000.0f);
	packed_values[5] = (short)(modifiers.arpen * 1000.0f);
	packed_values[6] = (short)(modifiers.haste * 1000.0f);
	packed_values[7] = (short)(modifiers.armor * 1000.0f);
	
	b64_ntop((unsigned char*)packed_values, 17, (char*)b64_modifiers, 80);
	
	fprintf(cgiOut, "<a href=\"");
	cgiValueEscape(cgiScriptName);
	fprintf(cgiOut,"?mods=%s\">Link these values</a>\n\n", b64_modifiers);
}

void print_about()
{
	FILE *about;
	char input;
	
	fprintf(cgiOut, "<div class=\"post\">\n");
	fprintf(cgiOut, "<H1>About This Page</H1>\n");
	fprintf(cgiOut, "<div class=\"post_body\">\n");
	about = fopen("about.txt","r");
	
	if(!about)
	{
		fprintf(cgiOut, "-- to be written --\n");
		fprintf(cgiOut, "</div></div>\n\n");
		return;
	}
	
	input = fgetc(about);
	while(!feof(about))
	{
		fprintf(cgiOut,"%c",input);
		input = fgetc(about);
	}
	
	fclose(about);
	
	fprintf(cgiOut, "</div></div>\n\n");
}

void print_form()
{
	/* str agi crit hit exp arpen haste */

	fprintf(cgiOut, "\n<form method=\"POST\" action=\"");
	cgiValueEscape(cgiScriptName);
	fprintf(cgiOut, "\">\n");
	
	fprintf(cgiOut, "<div class=\"post\">\n");
	
	fprintf(cgiOut, "<H1>Stat Values in Equivalent Attack Power</H1>\n");

	fprintf(cgiOut, "<div class=\"post_body\">\n");
	
	fprintf(cgiOut, "<TABLE class=\"statblock\">\n<TR><TD>");
	fprintf(cgiOut, "Strength:</TD><TD><input type=\"text\" name=\"strength\" value=\"%.3f\">\n", modifiers.str);
	fprintf(cgiOut, "</TD></TR>\n<TR><TD>");
	fprintf(cgiOut, "Agility:</TD><TD><input type=\"text\" name=\"agility\" value=\"%.3f\">\n", modifiers.agi);
	fprintf(cgiOut, "</TD></TR>\n<TR><TD>");
	fprintf(cgiOut, "Crit rating:</TD><TD><input type=\"text\" name=\"crit\" value=\"%.3f\">\n", modifiers.crit);
	fprintf(cgiOut, "</TD></TR>\n<TR><TD>");
	fprintf(cgiOut, "Hit rating:</TD><TD><input type=\"text\" name=\"hit\" value=\"%.3f\">\n", modifiers.hit);
	fprintf(cgiOut, "</TD></TR>\n<TR><TD>");
	fprintf(cgiOut, "Expertise:</TD><TD><input type=\"text\" name=\"expertise\" value=\"%.3f\">\n", modifiers.exp);
	fprintf(cgiOut, "</TD></TR>\n<TR><TD>");
	fprintf(cgiOut, "Armor penetration:</TD><TD><input type=\"text\" name=\"armorpen\" value=\"%.3f\">\n", modifiers.arpen);
	fprintf(cgiOut, "</TD></TR>\n<TR><TD>");
	fprintf(cgiOut, "Haste rating:</TD><TD><input type=\"text\" name=\"haste\" value=\"%.3f\">\n", modifiers.haste);
	fprintf(cgiOut, "</TD></TR>\n<TR><TD>");
	fprintf(cgiOut, "Armor:</TD><TD><input type=\"text\" name=\"armor\" value=\"%.3f\">\n", modifiers.armor);
	fprintf(cgiOut, "</TD></TR>\n</TABLE>\n<br>\n");
	fprintf(cgiOut, "<input type=\"submit\" name=\"modifiers\" value=\"Submit Modifiers\">\n");
	fprintf(cgiOut, "<input type=\"reset\" value=\"Reset Values\">\n");

	print_base64_link();

	fprintf(cgiOut, "</div></div>\n\n");
}

void handle_submit()
{
	double forminput;
	cgiFormDoubleBounded("strength", &forminput, 0.0, 10.0, (double)modifiers.str);
	modifiers.str = (float)forminput;
	cgiFormDoubleBounded("agility", &forminput, 0.0, 10.0, (double)modifiers.agi);
	modifiers.agi = (float)forminput;
	cgiFormDoubleBounded("crit", &forminput, 0.0, 10.0, (double)modifiers.crit);
	modifiers.crit = (float)forminput;
	cgiFormDoubleBounded("hit", &forminput, 0.0, 10.0, (double)modifiers.hit);
	modifiers.hit = (float)forminput;
	cgiFormDoubleBounded("expertise", &forminput, 0.0, 10.0, (double)modifiers.exp);
	modifiers.exp = (float)forminput;
	cgiFormDoubleBounded("armorpen", &forminput, 0.0, 10.0, (double)modifiers.arpen);
	modifiers.arpen = (float)forminput;
	cgiFormDoubleBounded("haste", &forminput, 0.0, 10.0, (double)modifiers.haste);
	modifiers.haste = (float)forminput;
	cgiFormDoubleBounded("armor", &forminput, 0.0, 10.0, (double)modifiers.armor);
	modifiers.armor = (float)forminput;
}

int cgiMain()
{
	int items_per_table=20;
	
	cgiHeaderContentType("text/html");
	
	print_page_header();
	
	set_default_modifiers();
	
	parse_base64_mods();

	if ((cgiFormSubmitClicked("modifiers") == cgiFormSuccess))
	{
		handle_submit();
	}

	if(read_items() != 0)
		return -1;
	
	print_about();
	
	print_form();
	
	print_table("Helms", itemlist[SLOT_HEAD], items_per_table);
	print_table("Amulets", itemlist[SLOT_NECK], items_per_table);
	print_table("Shoulder", itemlist[SLOT_SHOULDER], items_per_table);
	print_table("Cloaks", itemlist[SLOT_BACK], items_per_table);
	print_table("Chestpieces", itemlist[SLOT_CHEST], items_per_table);
	print_table("Belts", itemlist[SLOT_WAIST], items_per_table);
	print_table("Legpieces", itemlist[SLOT_LEGS], items_per_table);
	print_table("Boots", itemlist[SLOT_FEET], items_per_table);
	print_table("Bracers", itemlist[SLOT_WRIST], items_per_table);
	print_table("Gloves", itemlist[SLOT_HANDS], items_per_table);
	print_table("Rings", itemlist[SLOT_FINGER], items_per_table);
	print_table("Trinkets", itemlist[SLOT_TRINKET], items_per_table);
	
	print_page_footer();
	
	free_items();
	
	return 0;
}
