void ShipTurretManager::install() // static
{
    InstallTimer const installTimer("ShipTurretManager::install");

    Iff iff;
    if (iff.open("datatables/space/ship_turret.iff", true))
    {
        DataTable dataTable;
        dataTable.load(iff);
        int numberOfRows = dataTable.getNumRows();
        for (int row = 0; row < numberOfRows; ++row)
        {
            std::string const chassisName(dataTable.getStringValue("chassis", row));
            ShipChassis const * const chassis = ShipChassis::findShipChassisByName(TemporaryCrcString(chassisName.c_str(), false));
            FATAL(!chassis, ("ShipTurretManager::install: no such chassis '%s'", chassisName.c_str()));
            int const weaponIndex = dataTable.getIntValue("weaponIndex", row);
            ShipTurretData &shipTurretData = s_shipTurretData[chassis->getCrc()][weaponIndex];
            shipTurretData.minYaw = dataTable.getFloatValue("minYaw", row) * PI_OVER_180;
            shipTurretData.maxYaw = dataTable.getFloatValue("maxYaw", row) * PI_OVER_180;
            shipTurretData.minPitch = dataTable.getFloatValue("minPitch", row) * PI_OVER_180;
            shipTurretData.maxPitch = dataTable.getFloatValue("maxPitch", row) * PI_OVER_180;
        }
    }
}
void LevelManager::install()
{
	if(m_installed)
		return;
	m_installed = true;

	DataTable *playerLevelDatatable = DataTableManager::getTable("datatables/player/player_level.iff", true);

	if (playerLevelDatatable)
	{
		ms_numLevels = playerLevelDatatable->getNumRows();

		const int levelColumn          = playerLevelDatatable->findColumnNumber("level");
		const int xp_requiredColumn    = playerLevelDatatable->findColumnNumber("xp_required");
		const int health_grantedColumn = playerLevelDatatable->findColumnNumber("health_granted");
		const int xp_typeColumn        = playerLevelDatatable->findColumnNumber("xp_type");
		const int xp_multiplierColumn  = playerLevelDatatable->findColumnNumber("xp_multiplier");


		// @NOTE: there are basically two independent tables in this data file
		//   there is level information and xp type information
		//   these are independent and have diffent lengths, so I'm putting the info
		//   into two different data structures
		int i;
		for(i = 0; i < ms_numLevels; ++i)
		{
			LevelRecord levelRecord;

			levelRecord.level       = (int16) playerLevelDatatable->getIntValue(levelColumn, i);
			levelRecord.xp_required = playerLevelDatatable->getIntValue(xp_requiredColumn, i);
			levelRecord.health      = playerLevelDatatable->getIntValue(health_grantedColumn, i);

			ms_levelRecords.insert(std::make_pair(levelRecord.level, levelRecord));

			std::string xpTypeName  = playerLevelDatatable->getStringValue(xp_typeColumn, i);
			if (!xpTypeName.empty())
			{
				const uint32 xpTypeNameCrc = Crc::calculate(xpTypeName.c_str());
				const int xpMultiplier = playerLevelDatatable->getIntValue(xp_multiplierColumn, i);
				ms_xpTypeRecords.insert(std::make_pair(xpTypeNameCrc, xpMultiplier));
			}
			if(levelRecord.level > ms_maxLevel)
				ms_maxLevel = levelRecord.level;
		}
		DataTableManager::close("datatables/player/player_level.iff");
	}

	DataTable *skillDatatable = DataTableManager::getTable("datatables/skill/skills.iff", true);

	if (skillDatatable)
	{
		const int numSkillRows = skillDatatable->getNumRows();
		
		const int skill_nameColumn = skillDatatable->findColumnNumber("NAME");
		const int type_nameColumn  = skillDatatable->findColumnNumber("XP_TYPE");
		const int xp_costColumn    = skillDatatable->findColumnNumber("XP_COST");

		int i;
		for(i = 0; i < numSkillRows; i++)
		{
			SkillRecord skillRecord;
			
			std::string skillName = skillDatatable->getStringValue(skill_nameColumn, i);
			uint32 skillNameCrc = Crc::calculate(skillName.c_str());

			std::string xpTypeName = skillDatatable->getStringValue(type_nameColumn, i);
			skillRecord.xp_type_name_Crc = Crc::calculate(xpTypeName.c_str());

			skillRecord.xp_cost = skillDatatable->getIntValue(xp_costColumn, i);

			ms_skillRecords.insert(std::make_pair(skillNameCrc, skillRecord));
		}
		DataTableManager::close("datatables/skill/skills.iff");
	}

	ExitChain::add(LevelManager::remove, "LevelManager::remove", 0, false);
}
DataTableColumnType::DataTableColumnType(std::string const &desc) :
	m_typeSpecString(desc),
	m_type(DT_Unknown),
	m_basicType(DT_Unknown),
	m_defaultValue(),
	m_enumMap(0),
	m_defaultCell(0)
{
	// desc may only look like:
	// {ifshbep}[def] or e(x=0,y=1,z=2,...)[def]

	// first, split into type and default value
	int type = tolower(desc[0]);
	m_defaultValue = getDelimStr(desc, '[', ']');

	if (type == 'i')
	{
		m_type = m_basicType = DT_Int;
		if (m_defaultValue.length() == 0)
			m_defaultValue = "0";
	}
	else if (type == 'f')
	{
		m_type = m_basicType = DT_Float;
		if (m_defaultValue.length() == 0)
			m_defaultValue = "0";
	}
	else if (type == 's')
	{
		m_type = m_basicType = DT_String;
	}
	else if (type == 'c')
	{
		m_type = m_basicType = DT_Comment;
	}
	else if (type == 'h')
	{
		m_type = DT_HashString;
		m_basicType = DT_Int;
	}
	else if (type == 'p')
	{
		m_type = DT_PackedObjVars;
		m_basicType = DT_String;
	}
	else if (type == 'b')
	{
		m_type = DT_Bool;
		m_basicType = DT_Int;
		if (m_defaultValue != "1")
			m_defaultValue = "0";
	}
	else if (type == 'e')
	{
		m_enumMap = new StringIntMap;
		m_type = DT_Enum;
		m_basicType = DT_Int;
		// build the enumeration map
		std::string enumList = getDelimStr(desc, '(', ')');
		enumList += ",";
		// enumList looks like "foo=0,bar=1,life=42,"
		std::string::size_type eqPos;
		while ((eqPos = enumList.find('=')) != std::string::npos)
		{
			std::string::size_type endPos = enumList.find(',');
			std::string label = enumList.substr(0, eqPos);
			std::string val = enumList.substr(eqPos+1, endPos-eqPos-1);
			(*m_enumMap)[label] = static_cast<int>(strtol(val.c_str(), NULL, 0));
			enumList.erase(0, endPos+1);
		}
		// assure the default is a member of the enumeration
		if (m_enumMap->find(m_defaultValue) == m_enumMap->end())
		{
			WARNING(true, ("Default value [%s] is not a member of enumeration", m_defaultValue.c_str()));
			m_basicType = DT_Unknown;
		}
	}
	else if (type == 'v')
	{
		m_enumMap = new StringIntMap;
		m_type = DT_BitVector;
		m_basicType = DT_Int;
		// build the enumeration map
		std::string enumList = getDelimStr(desc, '(', ')');
		enumList += ",";
		// enumList looks like "foo=0,bar=1,life=42,"
		std::string::size_type eqPos;
		while ((eqPos = enumList.find('=')) != std::string::npos)
		{
			std::string::size_type endPos = enumList.find(',');
			std::string label = enumList.substr(0, eqPos);
			std::string val = enumList.substr(eqPos+1, endPos-eqPos-1);
			int bit = static_cast<int>(strtol(val.c_str(), NULL, 0));
			if((bit < 1) || (bit > 32))
			{
				WARNING(true, ("Flags value [%s] is not a whole number from 1 to 32", label.c_str()));
				m_basicType = DT_Unknown;
			}
			(*m_enumMap)[label] = 1 << (bit - 1);
			enumList.erase(0, endPos+1);
		}
		// assure the default is a member of the enumeration
		if(strcmp(m_defaultValue.c_str(), "NONE") != 0)
		{		
			if (m_enumMap->find(m_defaultValue) == m_enumMap->end())
			{
				WARNING(true, ("Default value [%s] is not a member of enumeration", m_defaultValue.c_str()));
				m_basicType = DT_Unknown;
			}
		}
	}
	else if (type == 'z')
	{
		m_enumMap = new StringIntMap;
		m_type = DT_Enum;
		m_basicType = DT_Int;
		// get the filename
		std::string fileName = getDelimStr(desc, '(', ')');

		DataTable * enumTable = DataTableManager::getTable(fileName, true);
		if (!enumTable)
		{
			m_basicType = DT_Unknown;
			return;
		}
		int enumCount = enumTable->getNumRows();
		int x;
		std::string firstKey;
		for (x=0; x<enumCount; ++x)
		{
			std::string key = enumTable->getStringValue(0, x);
			Unicode::trim(key);
			int value = enumTable->getIntValue(1,x);
			if (x==0)
				firstKey = key;
			(*m_enumMap)[key] = value;
		}

		// assure the default is a member of the enumeration
		if (m_enumMap->find(m_defaultValue) == m_enumMap->end())
		{
			m_defaultValue = firstKey;
		}
	}
	else
		m_basicType = DT_Unknown;

	createDefaultCell();
}
void CitizenRankDataTable::install()
{
	DataTable * table = DataTableManager::getTable(cs_citizenRankDataTableName, true);
	if (table)
	{
		int const columnRankName = table->findColumnNumber("rankName");
		int const columnRankSlotId = table->findColumnNumber("rankSlotId");
		int const columnTitle = table->findColumnNumber("title");

		// the can be a variable number of "alternate title" columns, as long as the columns
		// are named alternateTitle1, alternateTitle2, alternateTitle3, alternateTitle4,
		// alternateTitle5, and so on
		std::vector<int> columnAlternateTitle;
		char buffer[128];
		int columnNumber;
		for (int i = 1; i <= 1000000000; ++i)
		{
			snprintf(buffer, sizeof(buffer)-1, "alternateTitle%d", i);
			buffer[sizeof(buffer)-1] = '\0';

			columnNumber = table->findColumnNumber(buffer);
			if (columnNumber < 0)
				break;

			columnAlternateTitle.push_back(columnNumber);
		}

		FATAL((columnRankName < 0), ("column \"rankName\" not found in %s", cs_citizenRankDataTableName));
		FATAL((columnRankSlotId < 0), ("column \"rankSlotId\" not found in %s", cs_citizenRankDataTableName));
		FATAL((columnTitle < 0), ("column \"title\" not found in %s", cs_citizenRankDataTableName));

		CitizenRankDataTable::CitizenRank const * currentRank = NULL;

		int const numRows = table->getNumRows();
		std::string rankName, alternateTitle;
		int slotId;
		bool title;
		std::vector<std::string> titles;
		std::map<std::string, int> names;
		std::map<int, CitizenRankDataTable::CitizenRank const *> allRanksById;
		for (int i = 0; i < numRows; ++i)
		{
			rankName = table->getStringValue(columnRankName, i);

			if (rankName.empty())
				continue;

			FATAL((names.count(rankName) >= 1), ("%s, row %d: rank name %s already used at row %d (either as a rank name or rank alternate title)", cs_citizenRankDataTableName, (i+3), rankName.c_str(), names[rankName]));
			names[rankName] = (i+3);

			slotId = table->getIntValue(columnRankSlotId, i);
			title = (table->getIntValue(columnTitle, i) != 0);

			// read all alternate titles
			titles.clear();
			if (title)
				titles.push_back(rankName);

			for (std::vector<int>::const_iterator iterColumnAlternateTitle = columnAlternateTitle.begin(); iterColumnAlternateTitle != columnAlternateTitle.end(); ++iterColumnAlternateTitle)
			{
				alternateTitle = table->getStringValue(*iterColumnAlternateTitle, i);

				if (!alternateTitle.empty())
				{
					FATAL((names.count(alternateTitle) >= 1), ("%s, row %d: alternate title %s already used at row %d (either as a rank name or rank alternate title)", cs_citizenRankDataTableName, (i+3), alternateTitle.c_str(), names[alternateTitle]));
					names[alternateTitle] = (i+3);

					titles.push_back(alternateTitle);
				}
			}

			FATAL((!titles.empty() && !title), ("%s: rank %s cannot have any alternate titles unless it is defined as \"titleable\")", cs_citizenRankDataTableName, rankName.c_str()));

			// check for valid slot id
			FATAL((slotId < 0), ("%s, row %d: rank slot id %d must be >= 0", cs_citizenRankDataTableName, (i+3), slotId));

			// create citizen rank info
			currentRank = new CitizenRank(rankName, slotId, titles);

			// check for duplicate slot id
			FATAL((allRanksById.count(slotId) >= 1), ("%s, row %d: rank slot id %d already used by rank %s", cs_citizenRankDataTableName, (i+3), slotId, allRanksById[slotId]->name.c_str()));
			allRanksById[slotId] = currentRank;

			s_allCitizenRankNames.push_back(currentRank->name);
			s_allCitizenRanks.push_back(currentRank);

			if (!currentRank->titles.empty())
				s_allTitleableCitizenRanks.push_back(currentRank);

			s_allCitizenRanksByName[currentRank->name] = currentRank;

			for (std::vector<std::string>::const_iterator iterTitle = currentRank->titles.begin(); iterTitle != currentRank->titles.end(); ++iterTitle)
				s_allCitizenRanksByTitle[*iterTitle] = currentRank;
		}

		DataTableManager::close(cs_citizenRankDataTableName);

		// do additional verification
		slotId = -1;
		for (std::map<int, CitizenRankDataTable::CitizenRank const *>::const_iterator iterSlotId = allRanksById.begin(); iterSlotId != allRanksById.end(); ++iterSlotId)
		{
			FATAL((iterSlotId->first != iterSlotId->second->slotId), ("%s: slot id mismatch for slot %s (%d, %d)", cs_citizenRankDataTableName, iterSlotId->second->name.c_str(), iterSlotId->first, iterSlotId->second->slotId));

			// make sure that slot ids start at 0 and there are no "holes" in the slot ids
			if (iterSlotId == allRanksById.begin())
			{
				FATAL((iterSlotId->first != 0), ("%s: slot id must start at 0", cs_citizenRankDataTableName));
			}
			else
			{
				FATAL(((slotId + 1) != iterSlotId->first), ("%s: slot id must be contiguous (there is a \"hole\" between %d and %d)", cs_citizenRankDataTableName, slotId, iterSlotId->first));
			}

			slotId = iterSlotId->first;
		}
	}
	else
	{
		FATAL(true, ("citizen rank datatable %s not found", cs_citizenRankDataTableName));
	}

	ExitChain::add(remove, "CitizenRankDataTable::remove");
}