roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * files-xml-save.cpp
 *	 Used to serialize structures (object, creature, room, etc) to xml files
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
/*			Web Editor
 * 			 _   _  ____ _______ ______
 *			| \ | |/ __ \__   __|  ____|
 *			|  \| | |  | | | |  | |__
 *			| . ` | |  | | | |  |  __|
 *			| |\  | |__| | | |  | |____
 *			|_| \_|\____/  |_|  |______|
 *
 * 		If you change anything here, make sure the changes are reflected in the web
 * 		editor! Either edit the PHP yourself or tell Dominus to make the changes.
 */
#include "mud.h"
#include "version.h"
#include "effects.h"
#include "bans.h"
#include "guilds.h"
#include "factions.h"
#include "specials.h"
#include "calendar.h"
#include "quests.h"
#include "unique.h"
#include "magic.h"
#include "alchemy.h"

xmlNodePtr saveObjRefFlags(xmlNodePtr parentNode, const char* name, int maxBit, const char *bits);
// Object flags to be saved for object refs
int objRefSaveFlags[] =
{
	O_PERM_ITEM,
	O_HIDDEN,
	O_CURSED,
	O_WORN,
	O_TEMP_ENCHANT,
	O_WAS_SHOPLIFTED,
	O_ENVENOMED,
	O_JUST_BOUGHT,
	O_NO_DROP,
	O_BROKEN_BY_CMD,
	O_BEING_PREPARED,
	O_UNIQUE,
	O_KEEP,
	O_DARKNESS,
	-1
};

//*********************************************************************
//						saveToFile
//*********************************************************************
// This function will write the supplied player to PLAYERPATH/Player.xml
// NOTE: For now, it will ignore equiped equipment, so be sure to
// remove the equiped equipment and put it in the inventory before
// calling this function otherwise it will be lost

int Player::saveToFile(LoadType saveType) {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char		filename[256];

	ASSERTLOG( this != NULL );
	ASSERTLOG( name[0] != '\0' );
	ASSERTLOG( isPlayer() );

	if(name[0] == '\0') {
		printf("Invalid player passed to save\n");
		return(-1);
	}

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Player", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	escapeText();
	saveToXml(rootNode, ALLITEMS, LS_FULL);

	if(saveType == LS_BACKUP) {
		checkDirExists(BACKUPPATH, true);
		sprintf(filename, "%s/%s.bak.xml", BACKUPPATH, name);
	} else {
		checkDirExists(PLAYERPATH, true);
		sprintf(filename, "%s/%s.xml", PLAYERPATH, name);
	}

	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
	return(0);
}

//*********************************************************************
//						saveToFile
//*********************************************************************
// This function will write the supplied room to ROOMPATH/r#####.xml
// It will most likely only be called from *save r

int Room::saveToFile(int permOnly, LoadType saveType) {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char		filename[256];

	ASSERTLOG( this != NULL );
	ASSERTLOG( info.id >= 0 );
	if(saveType == LS_BACKUP)
		checkDirExists(info.area, roomBackupPath);
	else
		checkDirExists(info.area, roomPath);

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Room", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	escapeText();
	saveToXml(rootNode, permOnly);

	if(saveType == LS_BACKUP)
		strcpy(filename, roomBackupPath(info));
	else
		strcpy(filename, roomPath(info));
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
	return(0);
}

//*********************************************************************
//						saveToFile
//*********************************************************************
// This function will write the supplied creature to the proper
// m##.xml file - To accomplish this it parse the proper m##.xml file if
// available and walk down the tree to the proper place in numerical order
// if the file does not exist it will create it with only that monster in it.
// It will most likely only be called from *save c

int Monster::saveToFile() {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char		filename[256];

	// If we can't clean the monster properly, don't save anything
	if(cleanMobForSaving() != 1)
		return(-1);

	// Invalid Number
	if(info.id < 0)
		return(-1);
	checkDirExists(info.area, monsterPath);

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Creature", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	escapeText();
	saveToXml(rootNode, ALLITEMS, LS_FULL);

	strcpy(filename, monsterPath(info));
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
	return(0);
}

//*********************************************************************
//						saveObject
//*********************************************************************
// This function will write the supplied object to the proper
// o##.xml file - To accomplish this it parse the proper o##.xml file if
// available and walk down the tree to the proper place in numerical order
// if the file does not exist it will create it with only that object in it.
// It will most likely only be called from *save o

int Object::saveToFile() {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char		filename[256];

	ASSERTLOG( this );

	// Invalid Number
	if(info.id < 0)
		return(-1);
	checkDirExists(info.area, objectPath);

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Object", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	// make sure uniqueness stays intact
	setFlag(O_UNIQUE);
	if(!gConfig->getUnique(this))
		clearFlag(O_UNIQUE);

	escapeText();
	saveToXml(rootNode, ALLITEMS, LS_FULL);

	strcpy(filename, objectPath(info));
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
	return(0);
}

//*********************************************************************
//						saveGuilds
//*********************************************************************
// Causes the guilds structure to be generated into an xml tree and saved

bool Config::saveGuilds() const {
	GuildCreation * gcp;
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	//xmlNodePtr		curNode, bankNode, membersNode;
	char		filename[80];
	//int i;
	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Guilds", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);
	xml::newNumProp(rootNode, "NextGuildId", nextGuildId);

	std::map<int, Guild*>::const_iterator it;
	Guild *guild;
	for(it = guilds.begin() ; it != guilds.end() ; it++) {
		guild = (*it).second;
		if(guild->num == 0)
			continue;
		guild->saveToXml(rootNode);
	}

	std::list<GuildCreation*>::const_iterator gcIt;
	for(gcIt = guildCreations.begin() ; gcIt != guildCreations.end() ; gcIt++) {
		gcp = (*gcIt);
		gcp->saveToXml(rootNode);
	}

	sprintf(filename, "%s/guilds.xml", CONFPATH);
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
	return(true);
}

//*********************************************************************
//						saveBans
//*********************************************************************

bool Config::saveBans() const {
	int found=0;
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	xmlNodePtr	curNode;
	char		filename[80];

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL,BAD_CAST "Bans", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	std::list<Ban*>::const_iterator it;
	Ban* ban;

	for(it = bans.begin() ; it != bans.end() ; it++) {
		found++;
		ban = (*it);

		curNode = xmlNewChild(rootNode, NULL, BAD_CAST "Ban", NULL);
		// Site
		xmlNewChild(curNode, NULL, BAD_CAST "Site", BAD_CAST ban->site.c_str());
		// Duration
		//sprintf(buf, "%d", ban->duration);
		xml::newNumChild(curNode, "Duration", ban->duration);
		// Unban Time
		//sprintf(buf, "%ld", ban->unbanTime);
		xml::newNumChild(curNode, "UnbanTime", ban->unbanTime);
		// Banned By
		xmlNewChild(curNode, NULL, BAD_CAST "BannedBy", BAD_CAST ban->by.c_str());
		// Ban Time
		xmlNewChild(curNode, NULL, BAD_CAST "BanTime", BAD_CAST ban->time.c_str());
		// Reason
		xmlNewChild(curNode, NULL, BAD_CAST "Reason", BAD_CAST ban->reason.c_str());
		// Password
		xmlNewChild(curNode, NULL, BAD_CAST "Password", BAD_CAST ban->password.c_str());
		// Suffix
		xmlNewChild(curNode, NULL, BAD_CAST "Suffix", BAD_CAST iToYesNo(ban->isSuffix));
		// Prefix
		xmlNewChild(curNode, NULL, BAD_CAST "Prefix", BAD_CAST iToYesNo(ban->isPrefix));
	}
	sprintf(filename, "%s/bans.xml", CONFPATH);
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
	return(true);
}

//*********************************************************************
//						saveToXml
//*********************************************************************
// This will save the entire creature with anything non zero to the given
// node which should be a creature or player node

int Creature::saveToXml(xmlNodePtr rootNode, int permOnly, LoadType saveType) const {
//	xmlNodePtr	rootNode;
	xmlNodePtr		curNode;
	xmlNodePtr		childNode;
	int i;

	if(name[0] == '\0' || rootNode == NULL)
		return(-1);

	const Player	*pPlayer = getConstPlayer();
	const Monster	*mMonster = getConstMonster();

	if(pPlayer) {
		xml::newProp(rootNode, "Name", pPlayer->name);
		xml::newProp(rootNode, "Password", pPlayer->getPassword());
		xml::newNumProp(rootNode, "LastLogin", pPlayer->getLastLogin());
	} else if(mMonster) {
		// Saved for LS_REF and LS_FULL
		xml::newNumProp(rootNode, "Num", mMonster->info.id);
		xml::saveNonNullString(rootNode, "Name", mMonster->name);
		xml::newProp(rootNode, "Area", mMonster->info.area);
	}

	// For the future, when we change things, can read them in based on the version they
	// were saved in before or do modifications based on the new version
	xml::newProp(rootNode, "Version", VERSION);

	if(pPlayer) {
		if(pPlayer->area_room) {
			curNode = xml::newStringChild(rootNode, "AreaRoom");
			pPlayer->area_room->mapmarker.save(curNode);
		} else
			pPlayer->room.save(rootNode, "Room", true);
	}

	// Saved for LS_REF and LS_FULL
	xml::saveNonZeroNum(rootNode, "Race", race);
	xml::saveNonZeroNum(rootNode, "Class", cClass);
	if(pPlayer) {
		xml::saveNonZeroNum(rootNode, "Class2", pPlayer->getSecondClass());
	} else if(mMonster) {
		// TODO: Dom: for compatability, remove when possible
		xml::saveNonZeroNum(rootNode, "Class2", mMonster->getMobTrade());
		xml::saveNonZeroNum(rootNode, "MobTrade", mMonster->getMobTrade());
	}

	xml::saveNonZeroNum(rootNode, "Level", level);
	xml::saveNonZeroNum(rootNode, "Type", (int)type);
	xml::saveNonZeroNum(rootNode, "Experience", experience);
	coins.save("Coins", rootNode);
	xml::saveNonZeroNum(rootNode, "Alignment", alignment);

	curNode = xml::newStringChild(rootNode, "Stats");
	strength.save(curNode, "Strength");
	dexterity.save(curNode, "Dexterity");
	constitution.save(curNode, "Constitution");
	intelligence.save(curNode, "Intelligence");
	piety.save(curNode, "Piety");
	hp.save(curNode, "Hp");
	mp.save(curNode, "Mp");
	hooks.save(curNode, "Hooks");
	if(pPlayer)
		pPlayer->focus.save(curNode, "Focus");

	// Only saved for LS_FULL saves, or player saves
	if(saveType == LS_FULL || pPlayer) {
		xml::saveNonNullString(rootNode, "Description", description);

		// Keys
		curNode = xml::newStringChild(rootNode, "Keys");
		for(i=0; i<3; i++) {
			if(key[i][0] == 0)
				continue;
			childNode = xml::newStringChild(curNode, "Key", key[i]);
			xml::newNumProp(childNode, "Num", i);
		}

		// MoveTypes
		curNode = xml::newStringChild(rootNode, "MoveTypes");
		for(i=0; i<3; i++) {
			if(movetype[i][0] == 0)
				continue;
			childNode = xml::newStringChild(curNode, "MoveType", movetype[i]);
			xml::newNumProp(childNode, "Num", i);
		}


		xml::saveNonZeroNum(rootNode, "Armor", armor);
		xml::saveNonZeroNum(rootNode, "Deity", deity);


		saveULongArray(rootNode, "Realms", "Realm", realm, MAX_REALM-1);
		saveLongArray(rootNode, "Proficiencies", "Proficiency", proficiency, 6);

		damage.save(rootNode, "Dice");

		xml::saveNonZeroNum(rootNode, "Clan", clan);
		xml::saveNonZeroNum(rootNode, "PoisonDuration", poison_dur);
		xml::saveNonZeroNum(rootNode, "PoisonDamage", poison_dmg);
		xml::saveNonZeroNum(rootNode, "Size", size);

		if(pPlayer) pPlayer->saveXml(rootNode);
		else if(mMonster) mMonster->saveXml(rootNode);

		saveFactions(rootNode);
		saveSkills(rootNode);
		effects.save(rootNode, "Effects");
		saveAttacks(rootNode);

		if(!minions.empty()) {
			curNode = xml::newStringChild(rootNode, "Minions");
			std::list<bstring>::const_iterator mIt;
			for(mIt = minions.begin() ; mIt != minions.end() ; mIt++) {
				childNode = xml::newStringChild(curNode, "Minion", (*mIt));
			}
		}

		//		saveShortIntArray(rootNode, "Factions", "Faction", faction, MAX_FACTION);

		// Save dailys
		curNode = xml::newStringChild(rootNode, "DailyTimers");
		for(i=0; i<DAILYLAST+1; i++)
			saveDaily(curNode, i, daily[i]);

		// Save saving throws
		curNode = xml::newStringChild(rootNode, "SavingThrows");
		for(i=0; i<MAX_SAVE; i++)
			saveSavingThrow(curNode, i, saves[i]);

		// Perhaps change this into saveInt/CharArray
		saveBits(rootNode, "Spells", MAXSPELL, spells);
		saveBits(rootNode, "Quests", MAXSPELL, quests);
		xml::saveNonZeroNum(rootNode, "CurrentLanguage", current_language);
		saveBits(rootNode, "Languages", LANGUAGE_COUNT, languages);
	}

	// Saved for LS_FULL and LS_REF
	saveBits(rootNode, "Flags", pPlayer ? MAX_PLAYER_FLAGS : MAX_MONSTER_FLAGS, flags);

	// Save lasttimes
	for(i=0; i<TOTAL_LTS; i++) {
		// this nested loop means we won't create an xml node if we don't have to
		if(lasttime[i].interval || lasttime[i].ltime || lasttime[i].misc) {
			curNode = xml::newStringChild(rootNode, "LastTimes");
			for(; i<TOTAL_LTS; i++)
				saveLastTime(curNode, i, lasttime[i]);
		}
	}

	curNode = xml::newStringChild(rootNode, "Inventory");
	saveObjectsXml(curNode, first_obj, permOnly);

	// We want quests saved after inventory so when they are loaded we can calculate
	// if the quest is complete or not and store it in the appropriate variables
	if(pPlayer)
		pPlayer->saveQuests(rootNode);

	return(0);
}

//*********************************************************************
//						saveXml
//*********************************************************************

void Monster::saveXml(xmlNodePtr curNode) const {
	xmlNodePtr childNode, subNode;

	xml::saveNonZeroNum(curNode, "SkillLevel", skillLevel);
	xml::saveNonZeroNum(curNode, "UpdateAggro", updateAggro);
	xml::saveNonZeroNum(curNode, "LoadAggro", loadAggro);
	// Attacks
	childNode = xml::newStringChild(curNode, "Attacks");
	for(int i=0; i<3; i++) {
		if(attack[i][0] == 0)
			continue;
		subNode = xml::newStringChild(childNode, "Attack", attack[i]);
		xml::newNumProp(subNode, "Num", i);
	}
	xml::saveNonNullString(curNode, "LastMod", last_mod);
	xml::saveNonNullString(curNode, "Talk", talk);
	xmlNodePtr talkNode = xml::newStringChild(curNode, "TalkResponses");
	foreach(TalkResponse * talkResponse, responses)
		talkResponse->saveToXml(talkNode);

	xml::saveNonNullString(curNode, "TradeTalk", ttalk);
	xml::saveNonZeroNum(curNode, "NumWander", numwander);
	xml::saveNonZeroNum(curNode, "MagicResistance", magicResistance);
	xml::saveNonZeroNum(curNode, "DefenseSkill", defenseSkill);
	xml::saveNonZeroNum(curNode, "AttackPower", attackPower);
	xml::saveNonZeroNum(curNode, "WeaponSkill", weaponSkill);
	saveCarryArray(curNode, "CarriedItems", "Carry", carry, 10);
	saveCatRefArray(curNode, "AssistMobs", "Mob", assist_mob, NUM_ASSIST_MOB);
	saveCatRefArray(curNode, "EnemyMobs", "Mob", enemy_mob, NUM_ENEMY_MOB);

	xml::saveNonNullString(curNode, "PrimeFaction", primeFaction);
	xml::saveNonNullString(curNode, "AggroString", aggroString);

	jail.save(curNode, "Jail", false);
	xml::saveNonZeroNum(curNode, "WeaponSkill", maxLevel);
	xml::saveNonZeroNum(curNode, "Cast", cast);
	saveCatRefArray(curNode, "Rescue", "Mob", rescue, NUM_RESCUE);

	saveBits(curNode, "ClassAggro", 32, cClassAggro);
	saveBits(curNode, "RaceAggro", 32, raceAggro);
	saveBits(curNode, "DeityAggro", 32, deityAggro);
}

//*********************************************************************
//						saveXml
//*********************************************************************

void Player::saveXml(xmlNodePtr curNode) const {
	xmlNodePtr childNode;
	int i;

	// record people logging off during roomMove
	if(gConfig->isMovingRoom())
		gConfig->moveRoomLog((bstring)"p" + name, false);

	bank.save("Bank", curNode);
	xml::saveNonZeroNum(curNode, "WeaponTrains", weaponTrains);
	xml::saveNonNullString(curNode, "Surname", surname);
	xml::saveNonNullString(curNode, "Forum", forum);
	bound.save(curNode, "BoundRoom");
	previousRoom.save(curNode, "PreviousRoom");
	statistics.save(curNode, "Statistics");
	xml::saveNonZeroNum(curNode, "ActualLevel", actual_level);
	xml::saveNonZeroNum(curNode, "Created", created);
	xml::saveNonNullString(curNode, "OldCreated", oldCreated);
	xml::saveNonZeroNum(curNode, "Guild", guild);
	if(title != " ")
		xml::saveNonNullString(curNode, "Title", title);
	xml::saveNonZeroNum(curNode, "GuildRank", guildRank);
	if(isStaff()) {
		childNode = xml::newStringChild(curNode, "Ranges");
		for(i=0; i<MAX_BUILDER_RANGE; i++) {
			// empty range, skip it
			if(!bRange[i].low.id && !bRange[i].high)
				continue;
			bRange[i].save(childNode, "Range", i);
		}
	}

	xml::saveNonZeroNum(curNode, "NegativeLevels", negativeLevels);
	xml::saveNonZeroNum(curNode, "LostExperience", lostExperience);
	xml::saveNonZeroNum(curNode, "LastInterest", lastInterest);
	xml::saveNonZeroNum(curNode, "TickDmg", tickDmg);

	xml::saveNonNullString(curNode, "CustomColors", customColors);
	xml::saveNonZeroNum(curNode, "Thirst", thirst);
	saveBits(curNode, "Songs", gConfig->getMaxSong(), songs);

	std::list<CatRef>::const_iterator it;
	if(!roomExp.empty()) {
		childNode = xml::newStringChild(curNode, "RoomExp");
		for(it = roomExp.begin() ; it != roomExp.end() ; it++) {
			(*it).save(childNode, "Room", true);
		}
	}
	if(!lore.empty()) {
		childNode = xml::newStringChild(curNode, "Lore");
		for(it = lore.begin() ; it != lore.end() ; it++) {
			(*it).save(childNode, "Info", true);
		}
	}
	std::list<int>::const_iterator rt;
	if(!recipes.empty()) {
		childNode = xml::newStringChild(curNode, "Recipes");
		for(rt = recipes.begin() ; rt != recipes.end() ; rt++) {
			xml::newNumChild(childNode, "Recipe", (*rt));
		}
	}

	// Save any Anchors
	for(i=0; i<MAX_DIMEN_ANCHORS; i++) {
		if(anchor[i]) {
			childNode = xml::newStringChild(curNode, "Anchors");
			for(; i<MAX_DIMEN_ANCHORS; i++)
				if(anchor[i]) {
					xmlNodePtr subNode = xml::newStringChild(childNode, "Anchor");
					xml::newNumProp(subNode, "Num", i);
					anchor[i]->save(subNode);
				}
			break;
		}
	}

	xml::saveNonZeroNum(curNode, "Wimpy", wimpy);

	childNode = xml::newStringChild(curNode, "Pets");
	savePetsXml(childNode, first_fol);

	if(birthday) {
		childNode = xml::newStringChild(curNode, "Birthday");
		birthday->save(childNode);
	}

//			childNode = xml::newStringChild(curNode, "Deaths", NULL);
//			for(i=0; i<LUCKY_DEATHS; i++) {
//				if(lastDeath[i]) {
//					subNode = xml::newStringChild(childNode, "Death", NULL);
//					xml::saveNonZeroInt(subNode, "Time", lastDeath[i]);
//					xml::saveNonNullString(subNode, "Text", killedBy[i]);
//				}
//			}
	xml::saveNonNullString(curNode, "LastPassword", lastPassword);
	xml::saveNonNullString(curNode, "PoisonedBy", poisonedBy);
	xml::saveNonNullString(curNode, "AfflictedBy", afflictedBy);
}

//*********************************************************************
//						saveQuests
//*********************************************************************

void Player::saveQuests(xmlNodePtr rootNode) const {
	std::pair<int, QuestCompletion*> p;
	xmlNodePtr questNode;

	questNode = xml::newStringChild(rootNode, "QuestsInProgress");
	foreach(p, questsInProgress) {
		p.second->save(questNode);
	}

	questNode = xml::newStringChild(rootNode, "QuestsCompleted");
	std::list<int>::const_iterator it;
	for(it = questsCompleted.begin() ; it != questsCompleted.end() ; it++) {
		xml::newNumChild(questNode, "Quest", *it);
	}
}

//*********************************************************************
//						saveFactions
//*********************************************************************

void Creature::saveFactions(xmlNodePtr rootNode) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, "Factions");
	xmlNodePtr factionNode;
	std::map<bstring, long>::const_iterator fIt;
	for(fIt = factions.begin() ; fIt != factions.end() ; fIt++) {
		factionNode = xml::newStringChild(curNode, "Faction");
		xml::newStringChild(factionNode, "Name", (*fIt).first);
		xml::newNumChild(factionNode, "Regard", (*fIt).second);
	}
}

//*********************************************************************
//						saveSkills
//*********************************************************************

void Creature::saveSkills(xmlNodePtr rootNode) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, "Skills");
	std::map<bstring, CrtSkill*>::const_iterator sIt;
	for(sIt = skills.begin() ; sIt != skills.end() ; sIt++) {
		(*sIt).second->save(curNode);
	}
}

//*********************************************************************
//						save
//*********************************************************************

void Effects::save(xmlNodePtr rootNode, const char* name) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, name);
	std::list<EffectInfo*>::const_iterator eIt;
	for(eIt = list.begin() ; eIt != list.end() ; eIt++) {
		(*eIt)->save(curNode);
	}
}

//*********************************************************************
//						save
//*********************************************************************

void EffectInfo::save(xmlNodePtr rootNode) const {
	xmlNodePtr effectNode = xml::newStringChild(rootNode, "Effect");
	xml::newStringChild(effectNode, "Name", name);
	xml::newNumChild(effectNode, "Duration", duration);
	xml::newNumChild(effectNode, "Strength", strength);
	xml::newNumChild(effectNode, "Extra", extra);
}

//*********************************************************************
//						saveAttacks
//*********************************************************************

void Creature::saveAttacks(xmlNodePtr rootNode) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, "SpecialAttacks");
	std::list<SpecialAttack*>::const_iterator eIt;
	for(eIt = specials.begin() ; eIt != specials.end() ; eIt++) {
		(*eIt)->save(curNode);
	}
}

//*********************************************************************
//						save
//*********************************************************************

void CrtSkill::save(xmlNodePtr rootNode) const {
	xmlNodePtr skillNode = xml::newStringChild(rootNode, "Skill");
	xml::newStringChild(skillNode, "Name", getName());
	xml::newNumChild(skillNode, "Gained", getGained());
	xml::newNumChild(skillNode, "GainBonus", getGainBonus());
}


int AlchemyEffect::saveToXml(xmlNodePtr rootNode) {
	if(rootNode == NULL)
		return(-1);
	xml::newStringChild(rootNode, "Effect", effect);
	xml::newNumChild(rootNode, "Duration", duration);
	xml::newNumChild(rootNode, "Strength", strength);
	xml::newNumChild(rootNode, "Quality", quality);

	return(0);
}

//*********************************************************************
//						saveToXml
//*********************************************************************
// This function will save the entire object except 0 values to the
// given node which should be an object node unless saveType is REF
// in which case it will only save fields that are changed in the
// ordinary course of the game

int Object::saveToXml(xmlNodePtr rootNode, int permOnly, LoadType saveType, int quantity) const {
//	xmlNodePtr	rootNode;
	xmlNodePtr		curNode;
	xmlNodePtr		childNode;

	int i;


	if(rootNode == NULL)
		return(-1);

	ASSERTLOG( info.id >= 0 );
	ASSERTLOG( info.id < OMAX );

	// If the object's index is 0, then we have to do a full save
	if(!info.id && saveType != LS_FULL) {
		// We should never get here...if it's a 0 index, should have O_SAVE_FULL set
		printf("ERROR: Forcing full save.\n");
		saveType = LS_FULL;
	}

	//rootNode = xml::newStringChild(parentNode, "Object", NULL);
	xml::newNumProp(rootNode, "Num", info.id);
	xml::newProp(rootNode, "Area", info.area);
	xml::newProp(rootNode, "Version", VERSION);
	if(quantity > 1)
		xml::newNumProp(rootNode, "Quantity", quantity);

	// These are saved for full and reference
	xml::saveNonNullString(rootNode, "Name", name);
	xml::saveNonZeroNum(rootNode, "ShopValue", shopValue);
	xml::saveNonZeroNum(rootNode, "Adjustment", adjustment);
	xml::saveNonZeroNum(rootNode, "ShotsMax", shotsmax);
	// NOTE: This is saved even if zero so that it loads broken items
	// properly.
	xml::newNumChild(rootNode, "ShotsCur", shotscur);
	xml::saveNonZeroNum(rootNode, "Armor", armor);
	xml::saveNonZeroNum(rootNode, "NumAttacks", numAttacks);
	xml::saveNonZeroNum(rootNode, "Delay", delay);
	xml::saveNonZeroNum(rootNode, "Extra", extra);
	xml::saveNonZeroNum(rootNode, "LotteryCycle", lotteryCycle);

	if(type == LOTTERYTICKET) {
		// Lottery tickets have a custom description so be sure to save it
		xml::saveNonNullString(rootNode, "Description", description);
		saveShortIntArray(rootNode, "LotteryNumbers", "LotteryNum", lotteryNumbers, 6);
	}

	damage.save(rootNode, "Dice");
	value.save("Value", rootNode);
	hooks.save(rootNode, "Hooks");

	// Save the keys in case they were modified
	// Keys
	curNode = xml::newStringChild(rootNode, "Keys");
	for(i=0; i<3; i++) {
		if(key[i][0] == 0)
			continue;
		childNode = xml::newStringChild(curNode, "Key", key[i]);
		xml::newNumProp(childNode, "Num", i);
	}

	for(i=0; i<4; i++) {
		// this nested loop means we won't create an xml node if we don't have to
		if(lasttime[i].interval || lasttime[i].ltime || lasttime[i].misc) {
			curNode = xml::newStringChild(rootNode, "LastTimes");
			for(; i<4; i++)
				saveLastTime(curNode, i, lasttime[i]);
		}
	}

	xml::saveNonNullString(rootNode, "Effect", effect);
	xml::saveNonZeroNum(rootNode, "EffectDuration", effectDuration);
	xml::saveNonZeroNum(rootNode, "EffectStrength", effectStrength);

	xml::saveNonZeroNum(rootNode, "Recipe", recipe);
	xml::saveNonZeroNum(rootNode, "Made", made);
	xml::saveNonNullString(rootNode, "Owner", questOwner);

	if(saveType != LS_FULL) {
		// Save only a subset of flags for obj references
		saveObjRefFlags(rootNode, "Flags", MAX_OBJECT_FLAGS, flags);
	}

	if(saveType == LS_FULL) {
		saveBits(rootNode, "Flags", MAX_OBJECT_FLAGS, flags);
		// These are only saved for full objects
		if(type != LOTTERYTICKET)
			xml::saveNonNullString(rootNode, "Description", description);


        if(!alchemyEffects.empty()) {
            curNode = xml::newStringChild(rootNode, "AlchemyEffects");

            std::pair<int, AlchemyEffect> p;
            foreach(p, alchemyEffects) {
                childNode = xml::newStringChild(curNode, "AlchemyEffect");
				p.second.saveToXml(childNode);
                xml::newNumProp(childNode, "Num", p.first);
            }
        }
		xml::saveNonNullString(rootNode, "UseOutput", use_output);
		xml::saveNonNullString(rootNode, "UseAttack", use_attack);
		xml::saveNonNullString(rootNode, "LastMod", lastMod);

		xml::saveNonZeroNum(rootNode, "Weight", weight);
		xml::saveNonZeroNum(rootNode, "Type", type);
		xml::saveNonNullString(rootNode, "SubType", subType);

		xml::saveNonZeroNum(rootNode, "WearFlag", wearflag);
		xml::saveNonZeroNum(rootNode, "MagicPower", magicpower);

		xml::saveNonZeroNum(rootNode, "Level", level);
		xml::saveNonZeroNum(rootNode, "Quality", quality);
		xml::saveNonZeroNum(rootNode, "RequiredSkill", requiredSkill);
		xml::saveNonZeroNum(rootNode, "Clan", clan);
		xml::saveNonZeroNum(rootNode, "Special", special);
		xml::saveNonZeroNum(rootNode, "QuestNum", questnum);

		xml::saveNonZeroNum(rootNode, "Bulk", bulk);
		xml::saveNonZeroNum(rootNode, "Size", size);
		xml::saveNonZeroNum(rootNode, "MaxBulk", maxbulk);

		xml::saveNonZeroNum(rootNode, "MaxUnique", max_unique);
		xml::saveNonZeroNum(rootNode, "CoinCost", coinCost);

		deed.save(rootNode, "Deed", false);

		xml::saveNonZeroNum(rootNode, "KeyVal", keyVal);
		xml::saveNonZeroNum(rootNode, "Material", (int)material);
		xml::saveNonZeroNum(rootNode, "MinStrength", minStrength);

		saveCatRefArray(rootNode, "InBag", "Obj", in_bag, 3);

		std::list<CatRef>::const_iterator it;
		if(!randomObjects.empty()) {
			curNode = xml::newStringChild(rootNode, "RandomObjects");
			for(it = randomObjects.begin() ; it != randomObjects.end() ; it++) {
				(*it).save(curNode, "RandomObject", false);
			}
		}

	}

	if(compass) {
		curNode = xml::newStringChild(rootNode, "Compass");
		compass->save(curNode);
	}

	// Save contained items for both full and reference saves
	// Also check for container flag
	if(first_obj) {
		curNode = xml::newStringChild(rootNode, "SubItems");
		// If we're a permenant container, always save the items inside of it
		if(type == CONTAINER && flagIsSet(O_PERM_ITEM))
			permOnly = ALLITEMS;
		saveObjectsXml(curNode, first_obj, permOnly);
	}
	return(0);
}

//*********************************************************************
//						saveToXml
//*********************************************************************

int Room::saveToXml(xmlNodePtr rootNode, int permOnly) const {
	std::map<int, crlasttime>::const_iterator it;
	xmlNodePtr		curNode;
	int i;

	if(!this || name[0] == '\0' || rootNode == NULL)
		return(-1);

	// record rooms saved during roomMove
	if(gConfig->isMovingRoom())
		gConfig->moveRoomLog((bstring)"r" + info.rstr(), false);

	xml::newNumProp(rootNode, "Num", info.id);
	xml::newProp(rootNode, "Version", VERSION);
	xml::newProp(rootNode, "Area", info.area);

	xml::saveNonNullString(rootNode, "Name", name);
	xml::saveNonNullString(rootNode, "ShortDescription", short_desc);
	xml::saveNonNullString(rootNode, "LongDescription", long_desc);
	xml::saveNonNullString(rootNode, "Fishing", fishing);
	xml::saveNonNullString(rootNode, "Faction", faction);
	xml::saveNonNullString(rootNode, "LastModBy", last_mod);
	// TODO: Change this into a TimeStamp
	xml::saveNonNullString(rootNode, "LastModTime", lastModTime);
	xml::saveNonNullString(rootNode, "LastPlayer", lastPly);
	// TODO: Change this into a TimeStamp
	xml::saveNonNullString(rootNode, "LastPlayerTime", lastPlyTime);


	xml::saveNonZeroNum(rootNode, "LowLevel", lowLevel);
	xml::saveNonZeroNum(rootNode, "HighLevel", highLevel);
	xml::saveNonZeroNum(rootNode, "MaxMobs", maxmobs);

	xml::saveNonZeroNum(rootNode, "Trap", trap);
	trapexit.save(rootNode, "TrapExit", false);
	xml::saveNonZeroNum(rootNode, "TrapWeight", trapweight);
	xml::saveNonZeroNum(rootNode, "TrapStrength", trapstrength);

	xml::saveNonZeroNum(rootNode, "BeenHere", beenhere);
	xml::saveNonZeroNum(rootNode, "RoomExp", roomExp);

	saveBits(rootNode, "Flags", MAX_ROOM_FLAGS, flags);

	curNode = xml::newStringChild(rootNode, "Wander");
	wander.save(curNode);

	if(track.getDirection() != "") {
		curNode = xml::newStringChild(rootNode, "Track");
		track.save(curNode);
	}

	xml::saveNonZeroNum(rootNode, "Size", size);
	effects.save(rootNode, "Effects");
	hooks.save(curNode, "Hooks");



	// Save Perm Mobs
	curNode = xml::newStringChild(rootNode, "PermMobs");
	for(it = permMonsters.begin(); it != permMonsters.end() ; it++) {
		saveCrLastTime(curNode, (*it).first, (*it).second);
	}

	// Save Perm Objs
	curNode = xml::newStringChild(rootNode, "PermObjs");
	for(it = permObjects.begin(); it != permObjects.end() ; it++) {
		saveCrLastTime(curNode, (*it).first, (*it).second);
	}

	// Save LastTimes -- Dunno if we need this?
	for(i=0; i<16; i++) {
		// this nested loop means we won't create an xml node if we don't have to
		if(lasttime[i].interval || lasttime[i].ltime || lasttime[i].misc) {
			curNode = xml::newStringChild(rootNode, "LastTimes");
			for(; i<16; i++)
				saveLastTime(curNode, i, lasttime[i]);
		}
	}

	// Save Objects
	curNode = xml::newStringChild(rootNode, "Objects");
	saveObjectsXml(curNode, first_obj, permOnly);

	// Save Creatures
	curNode = xml::newStringChild(rootNode, "Creatures");
	saveCreaturesXml(curNode, first_mon, permOnly);

	// Save Exits
	curNode = xml::newStringChild(rootNode, "Exits");
	saveExitsXml(curNode, first_ext);
	return(0);
}

//*********************************************************************
//						saveToXml
//*********************************************************************

int Exit::saveToXml(xmlNodePtr parentNode) const {
	xmlNodePtr	rootNode;
	xmlNodePtr		curNode;
	xmlNodePtr		childNode;

	int i;

	if(this == NULL || parentNode == NULL || flagIsSet(X_PORTAL))
		return(-1);

	rootNode = xml::newStringChild(parentNode, "Exit");
	xml::newProp(rootNode, "Name", name);

	// Exit Keys
	curNode = xml::newStringChild(rootNode, "Keys");
	for(i=0; i<3; i++) {
		if(desc_key[i][0] == 0)
			continue;
		childNode = xml::newStringChild(curNode, "Key", desc_key[i]);
		xml::newNumProp(childNode, "Num",i);
	}

	target.save(rootNode, "Target");
	xml::saveNonZeroNum(rootNode, "Toll", toll);
	xml::saveNonZeroNum(rootNode, "Level", level);
	xml::saveNonZeroNum(rootNode, "Trap", trap);
	xml::saveNonZeroNum(rootNode, "Key", key);
	xml::saveNonNullString(rootNode, "KeyArea", keyArea);
	xml::saveNonZeroNum(rootNode, "Size", size);
	xml::saveNonNullString(rootNode, "PassPhrase", passphrase);
	xml::saveNonZeroNum(rootNode, "PassLang", passlang);
	xml::saveNonNullString(rootNode, "Enter", enter);
	xml::saveNonNullString(rootNode, "Open", open);
	effects.save(rootNode, "Effects");

	saveBits(rootNode, "Flags", MAX_EXIT_FLAGS, flags);
	saveLastTime(curNode, 0, ltime);
	hooks.save(curNode, "Hooks");

	return(0);
}

//*********************************************************************
//						saveExitsXml
//*********************************************************************

int saveExitsXml(xmlNodePtr curNode, xtag *xp) {
	while(xp) {
		if(xp->ext)
			xp->ext->saveToXml(curNode);
		xp = xp->next_tag;
	}
	return(0);
}

//*********************************************************************
//						saveObjectsXml
//*********************************************************************

int saveObjectsXml(xmlNodePtr parentNode, otag* op, int permOnly) {
	xmlNodePtr curNode;
	int quantity=0;
	LoadType lt;
	while(op) {
		//if(ot->obj && ot->obj->index != 0 && (permOnly == ALLITEMS || (permOnly == PERMONLY && ot->obj->flagIsSet(O_PERM_ITEM)))) {
		if(	op->obj &&
			!op->obj->flagIsSet(O_PORTAL) &&
			(	permOnly == ALLITEMS ||
				(permOnly == PERMONLY && op->obj->flagIsSet(O_PERM_ITEM))
			)
		) {
			if(op->obj->flagIsSet(O_CUSTOM_OBJ) || op->obj->flagIsSet(O_SAVE_FULL) || !op->obj->info.id) {
				// If it's a custom or has the save flag set, save the entire object
				curNode = xml::newStringChild(parentNode, "Object");
				lt = LS_FULL;
			} else {
				// Just save a reference and any changed fields
				curNode = xml::newStringChild(parentNode, "ObjRef");
				lt = LS_REF;
			}

			// quantity code reduces filesize for player shops, storage rooms, and
			// inventories (which tend of have a lot of identical items in them)
			quantity = 1;
			while(op->next_tag && *op->next_tag->obj == *op->obj) {
				quantity++;
				op = op->next_tag;
			}
			op->obj->saveToXml(curNode, permOnly, lt, quantity);
		}
		op = op->next_tag;
	}
	return(0);
}

//*********************************************************************
//						saveCreaturesXml
//*********************************************************************

int saveCreaturesXml(xmlNodePtr parentNode, ctag* cp, int permOnly) {
	xmlNodePtr curNode;
	while(cp) {
		if(	cp->crt && cp->crt->isMonster() &&
			(	(permOnly == ALLITEMS && !cp->crt->isPet()) ||
				(permOnly == PERMONLY && cp->crt->flagIsSet(M_PERMENANT_MONSTER))
			)
		) {
			if(cp->crt->flagIsSet(M_SAVE_FULL)) {
				// Save a fully copy of the mob to the node
				curNode = xml::newStringChild(parentNode, "Creature");
				cp->crt->saveToXml(curNode, permOnly, LS_FULL);
			} else {
				// Just save a reference
				curNode = xml::newStringChild(parentNode, "CrtRef");
				cp->crt->saveToXml(curNode, permOnly, LS_REF);
			}
		}
		cp = cp->next_tag;
	}
	return(0);
}

//*********************************************************************
//						savePetsXml
//*********************************************************************

int savePetsXml(xmlNodePtr parentNode, ctag* cp) {
	xmlNodePtr curNode;
	while(cp) {
		if(cp->crt && cp->crt->isPet()) {
			curNode = xml::newStringChild(parentNode, "Creature");
			cp->crt->saveToXml(curNode, ALLITEMS, LS_FULL);
		}
		cp = cp->next_tag;
	}
	return(0);
}

//*********************************************************************
//						saveToXml
//*********************************************************************

bool Guild::saveToXml(xmlNodePtr rootNode) const {
	xmlNodePtr curNode, membersNode;

	curNode = xml::newStringChild(rootNode, "Guild");
//		xml::newStringChild(curNode, "", );
//		xml::newIntChild(curNode, "", guild->);
	xml::newNumProp(curNode, "ID", num);
	xml::newStringChild(curNode, "Name", name);
	xml::newStringChild(curNode, "Leader", leader);

	xml::newNumChild(curNode, "Level", level);
	xml::newNumChild(curNode, "NumMembers", numMembers);

	membersNode = xml::newStringChild(curNode, "Members");
	std::list<bstring>::const_iterator mIt;
	for(mIt = members.begin() ; mIt != members.end() ; mIt++) {
		xml::newStringChild(membersNode, "Member", *mIt);
	}

	xml::newNumChild(curNode, "PkillsWon", pkillsWon);
	xml::newNumChild(curNode, "PkillsIn", pkillsIn);
	xml::newNumChild(curNode,"Points", points);
	bank.save("Bank", curNode);

	return(true);
}

//*********************************************************************
//						saveToXml
//*********************************************************************

bool GuildCreation::saveToXml(xmlNodePtr rootNode) const {
	xmlNodePtr childNode, curNode = xml::newStringChild(rootNode, "GuildCreation");

	xml::newStringChild(curNode, "Name", name);
	xml::newStringChild(curNode, "Leader", leader);
	xml::newStringChild(curNode, "LeaderIp", leaderIp);
	xml::newNumChild(curNode, "Status", status);

	// Supporters
	std::map<bstring, bstring>::const_iterator sIt;
	for(sIt = supporters.begin() ; sIt != supporters.end() ; sIt++) {
		childNode = xml::newStringChild(curNode, "Supporter", (*sIt).first);
		xml::newProp(childNode, "Ip", (*sIt).second);
	}
	return(true);
}




#define BIT_ISSET(p,f)	((p)[(f)/8] & 1<<((f)%8))
#define BIT_SET(p,f)	((p)[(f)/8] |= 1<<((f)%8))
#define BIT_CLEAR(p,f)	((p)[(f)/8] &= ~(1<<((f)%8)))

//*********************************************************************
//						save
//*********************************************************************

void Stat::save(xmlNodePtr parentNode, const char* statName) const {
	xmlNodePtr curNode = xml::newStringChild(parentNode, "Stat");
	xml::newProp(curNode, "Name", statName);

	xml::newNumChild(curNode, "Current", cur);
	xml::newNumChild(curNode, "Max", max);
	xml::newNumChild(curNode, "Initial", initial);
	xml::newNumChild(curNode, "TempMax", tmpMax);
}

//*********************************************************************
//						saveDaily
//*********************************************************************

xmlNodePtr saveDaily(xmlNodePtr parentNode, int i, struct daily pDaily) {
	// Avoid writing un-used daily timers
	if(pDaily.max == 0 && pDaily.cur == 0 && pDaily.ltime == 0)
		return(NULL);

	xmlNodePtr curNode = xml::newStringChild(parentNode, "Daily");
	xml::newNumProp(curNode, "Num", i);

	xml::newNumChild(curNode, "Max", pDaily.max);
	xml::newNumChild(curNode, "Current", pDaily.cur);
	xml::newNumChild(curNode, "LastTime", pDaily.ltime);
	return(curNode);
}

//*********************************************************************
//						saveCrLastTime
//*********************************************************************

xmlNodePtr saveCrLastTime(xmlNodePtr parentNode, int i, struct crlasttime pCrLastTime) {
	// Avoid writing un-used last times
	if(!pCrLastTime.interval && !pCrLastTime.ltime && !pCrLastTime.cr.id)
		return(NULL);

	if(i < 0 || i >= NUM_PERM_SLOTS)
		return(NULL);

	xmlNodePtr curNode = xml::newStringChild(parentNode, "LastTime");
	xml::newNumProp(curNode, "Num", i);

	xml::newNumChild(curNode, "Interval", pCrLastTime.interval);
	xml::newNumChild(curNode, "LastTime", pCrLastTime.ltime);
	pCrLastTime.cr.save(curNode, "Misc", false);
	return(curNode);
}

//*********************************************************************
//						saveLastTime
//*********************************************************************

xmlNodePtr saveLastTime(xmlNodePtr parentNode, int i, struct lasttime pLastTime) {
	// Avoid writing un-used last times
	if(!pLastTime.interval && !pLastTime.ltime && !pLastTime.misc)
		return(NULL);

	xmlNodePtr curNode = xml::newStringChild(parentNode, "LastTime");
	xml::newNumProp(curNode, "Num", i);

	xml::newNumChild(curNode, "Interval", pLastTime.interval);
	xml::newNumChild(curNode, "LastTime", pLastTime.ltime);
	xml::newNumChild(curNode, "Misc", pLastTime.misc);
	return(curNode);
}

//*********************************************************************
//						saveSavingThrow
//*********************************************************************

xmlNodePtr saveSavingThrow(xmlNodePtr parentNode, int i, struct saves pSavingThrow) {
	xmlNodePtr curNode = xml::newStringChild(parentNode, "SavingThrow");
	xml::newNumProp(curNode, "Num", i);

	xml::newNumChild(curNode, "Chance", pSavingThrow.chance);
	xml::newNumChild(curNode, "Gained", pSavingThrow.gained);
	xml::newNumChild(curNode, "Misc", pSavingThrow.misc);
	return(curNode);
}

//*********************************************************************
//						saveObjRefFlags
//*********************************************************************

xmlNodePtr saveObjRefFlags(xmlNodePtr parentNode, const char* name, int maxBit, const char *bits) {
	xmlNodePtr curNode=NULL;
	// this nested loop means we won't create an xml node if we don't have to
	for(int i=0; objRefSaveFlags[i] != -1; i++) {
		if(BIT_ISSET(bits, objRefSaveFlags[i])) {
			curNode = xml::newStringChild(parentNode, name);
			for(; objRefSaveFlags[i] != -1; i++) {
				if(BIT_ISSET(bits, objRefSaveFlags[i]))
					saveBit(curNode, objRefSaveFlags[i]);
			}
			return(curNode);
		}
	}
	return(curNode);
}

//*********************************************************************
//						saveBits
//*********************************************************************

xmlNodePtr saveBits(xmlNodePtr parentNode, const char* name, int maxBit, const char *bits) {
	xmlNodePtr curNode=NULL;
	// this nested loop means we won't create an xml node if we don't have to
	for(int i=0; i<maxBit; i++) {
		if(BIT_ISSET(bits, i)) {
			curNode = xml::newStringChild(parentNode, name);
			for(; i<maxBit; i++) {
				if(BIT_ISSET(bits, i))
					saveBit(curNode, i);
			}
			return(curNode);
		}
	}
	return(curNode);
}

//*********************************************************************
//						saveBit
//*********************************************************************

xmlNodePtr saveBit(xmlNodePtr parentNode, int bit) {
	xmlNodePtr curNode;

	curNode = xml::newStringChild(parentNode, "Bit");
	xml::newNumProp(curNode, "Num", bit);
	return(curNode);
}

//*********************************************************************
//						saveLongArray
//*********************************************************************

xmlNodePtr saveLongArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const long array[], int arraySize) {
	xmlNodePtr childNode, curNode=NULL;
	// this nested loop means we won't create an xml node if we don't have to
	for(int i=0; i<arraySize; i++) {
		if(!array[i]) {
			curNode = xml::newStringChild(parentNode, rootName);
			for(; i<arraySize; i++) {
				if(array[i]) {
					childNode = xml::newNumChild(curNode, childName, array[i]);
					xml::newNumProp(childNode, "Num", i);
				}
			}
			return(curNode);
		}
	}
	return(curNode);
}

//*********************************************************************
//						saveULongArray
//*********************************************************************

xmlNodePtr saveULongArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const unsigned long array[], int arraySize) {
	xmlNodePtr childNode, curNode=NULL;
	// this nested loop means we won't create an xml node if we don't have to
	for(int i=0; i<arraySize; i++) {
		if(array[i]) {
			curNode = xml::newStringChild(parentNode, rootName);
			for(; i<arraySize; i++) {
				if(array[i]) {
					childNode = xml::newNumChild(curNode, childName, array[i]);
					xml::newNumProp(childNode, "Num", i);
				}
			}
			return(curNode);
		}
	}
	return(curNode);
}

//*********************************************************************
//						saveCatRefArray
//*********************************************************************

xmlNodePtr saveCatRefArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const std::map<int, CatRef>& array, int arraySize) {
	xmlNodePtr curNode=NULL;
	std::map<int, CatRef>::const_iterator it;

	// this nested loop means we won't create an xml node if we don't have to
	for(it = array.begin(); it != array.end() ; it++) {
		if((*it).second.id) {
			curNode = xml::newStringChild(parentNode, rootName);
			for(; it != array.end() ; it++) {
				if((*it).second.id)
					(*it).second.save(curNode, childName, false, (*it).first);
			}
			return(curNode);
		}
	}
	return(curNode);
}

//*********************************************************************
//						saveCatRefArray
//*********************************************************************

xmlNodePtr saveCatRefArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const CatRef array[], int arraySize) {
	xmlNodePtr curNode=NULL;
	// this nested loop means we won't create an xml node if we don't have to
	for(int i=0; i<arraySize; i++) {
		if(array[i].id) {
			curNode = xml::newStringChild(parentNode, rootName);
			for(; i<arraySize; i++) {
				if(array[i].id)
					array[i].save(curNode, childName, false, i);
			}
			return(curNode);
		}
	}
	return(curNode);
}

//*********************************************************************
//						saveCarryArray
//*********************************************************************

xmlNodePtr saveCarryArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const Carry array[], int arraySize) {
	xmlNodePtr curNode=NULL;
	// this nested loop means we won't create an xml node if we don't have to
	for(int i=0; i<arraySize; i++) {
		if(array[i].info.id) {
			curNode = xml::newStringChild(parentNode, rootName);
			for(; i<arraySize; i++) {
				if(array[i].info.id)
					array[i].save(curNode, childName, false, i);
			}
			return(curNode);
		}
	}
	return(curNode);
}
//*********************************************************************
//						saveShortIntArray
//*********************************************************************

xmlNodePtr saveShortIntArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const short array[], int arraySize) {
	xmlNodePtr childNode, curNode=NULL;
	// this nested loop means we won't create an xml node if we don't have to
	for(int i=0; i<arraySize; i++) {
		if(array[i]) {
			curNode = xml::newStringChild(parentNode, rootName);
			for(; i<arraySize; i++) {
				if(array[i]) {
					childNode = xml::newNumChild(curNode, childName, array[i]);
					xml::newNumProp(childNode, "Num", i);
				}
			}
			return(curNode);
		}
	}
	return(curNode);
}

#undef BIT_ISSET
#undef BIT_SET
#undef BIT_CLEAR