Legacy:LadderQuery

From Unreal Wiki, The Unreal Engine Documentation Site
UT2003 :: Object >> XAdminBase >> xWebQueryHandler >> LadderQuery (Ladder1.46)
//-----------------------------------------------------------
//  Ladder.LadderQuery
//  QueryHandler for webadmin interface
//-----------------------------------------------------------
class LadderQuery extends xWebQueryHandler
	DependsOn(ProfileConfigSet)
	config(System);

const LOGNAME		= 'LadderQuery';
const EPICNUMBER    = 85;

var LadderGameRules LadderRules;

// Profile Tracking Variables
var SessionMaster 			SMaster;
var ConfigMaster			ConfigM;
var string 					EpicPackages[EPICNUMBER];

// Active Profile Variables
var StringArray 		ProfileMaps;
var ProfileExporter		ExportMaster;
var ProfileConfigSet 	CurrentProfile;
var StringArray 		ProfileMutators;

var string				DefaultProfileName;

// Physical page that will be served
var config string		MasterPage;	// This is main html page for Ladder
var config string		SubFrame;	// Manage sub-frame
var config string		SubFrameMenu;	// Sub-menu (GameType, Mutators, Maps, PlayInfo, Review)
var config string		SubFrameContent;


// Include Files
var config string		IncludeTable;
var config string		IncludeSection;
var config string		IncludeRows;
var config string		IncludeCaptionButton;
var config string		IncludeMapSelect;
var config string		IncludeProfileName;
var config string		IncludeGameType;
var config string		IncludeCheckBox;
var config string		IncludeSwitch;

// Virtual paths that will be requested from clients
var config string 		ProfileViewURI;
var config string 		ProfileEditURI;
var config string 		ProfileMenuURI;
var config string 		ProfileSwitchURI;
var config string		ProfileManageURI;

var config string		SubFrameURI;	// accessed through manage link - linked to SubFrame page
var config string		SubFrameMenuURI;
var config string 		SubMapsURI;
var config string 		SubSavedURI;
var config string 		SubRulesURI;
var config string 		SubReviewURI;
var config string 		SubGametypeURI;
var config string 		SubMutatorsURI;
var config string		SubImportURI;
var config string		SubExportURI;

// Localized Page Help texts
var localized string 	HelpViewProfile;
var localized string 	HelpEditProfile;
var localized string 	HelpSwitchProfile;
var localized string 	HelpManageProfile;
var localized string 	HelpImportProfile;
var localized string 	HelpExportProfile;
// Subpage help text
var localized string	HelpGameType;
var localized string	HelpMutators;
var localized string	HelpMaps;
var localized string	HelpPlayInfo;
var localized string	HelpReview;

// Localized Menu Link Text
var localized string 	LinkEditProfile;
var localized string 	LinkViewProfile;
var localized string 	LinkSwitchProfile;
var localized string 	LinkManageProfile;
var localized string 	LinkImportProfile;
var localized string 	LinkExportProfile;

// for AddProfilePage/ManageProfilePage
var localized string 	TableTitle[9];
var localized string 	PageCaption[9];
var localized string 	ProfileButtonName[9];
var localized string 	ProfileButtonValue[9];

// Section Headers & Text
var localized string	MapText;
var localized string	ErrorText;
var localized string	MutatorText;
var localized string 	RequiredText;
var localized string	AvailableText;
var localized string	UnlimitedText;
var localized string	FollowingMaps;
var localized string	AreAssignedText;
var localized string	AreAvailableText;
var localized string	FollowingMutators;
var localized string	CommandLineOptionsTitle;

// Button Values
var localized string 	ButtonLoadProfile;			// Label for "switch immediately" button
var localized string	ButtonDeleteProfile;
var localized string	ButtonPreviewProfile;		// Label for previewing remote profile button
var localized string	ButtonApplyImportProfile;
var localized string	ButtonApplyExportProfile;
var localized string	ButtonApplyProfileChanges;

// Input form labels
var localized string	LabelCopyProfile;
var localized string	LabelDeleteProfile;
var localized string	LabelImportProfile;
var localized string	LabelExportProfile;
var localized string	LabelAddProfile;
var localized string	LabelSelectActions;
var localized string	LabelTextboxActions;

var localized string	LabelPlayInfo;
var localized string	LabelGeneralProfile;
var localized string	LabelGameType;
var localized string	LabelProfileName;
var localized string	LabelAccessControl;
var localized string	LabelChooseAction;
var localized string	LabelManagement;
var localized string	LabelInputDescription;

var localized string 	LabelMaxMaps;
var localized string 	LabelNumberGames;
var localized string	LabelCheckAllMap;
var localized string	LabelRequiredMaps;
var localized string	LabelUncheckAllMap;
var localized string 	LabelChangeMaplist;
var localized string	LabelCheckAllMutator;
var localized string	LabelRequiredMutators;
var localized string	LabelNumberMatchesLeft;
var localized string	LabelUncheckAllMutator;
var localized string	LabelDelayProfileChange;
var localized string	LabelApplyImportProfile;
var localized string	LabelApplyExportProfile;
var localized string	LabelModifyProfile;

// Success Messages
var localized string	SuccessText;
var localized string	SuccessDeleteProfile;
var localized string	SuccessExportProfile;
var localized string	SuccessImportProfile;
var localized string	SuccessImportProfileTitle;
var localized string	SuccessCopyProfile;
var localized string	SuccessSwitchProfile;

// Profile Errors
var localized string	ErrorLoadingPlayInfoText[5];
var localized string	ErrorGameRulesNotFound;
var localized string	ErrorGameClassText;
var localized string	ErrorGameClassTitle;
var localized string	ErrorAccessControlText;
var localized string	ErrorAccessControlTitle;
var localized string 	ErrorSameProfileName;
var localized string	ErrorRemoteMutator;
var localized string	ErrorRemoteServerActor;
var localized string	ErrorSavingProfile;
var localized string	ErrorDeletingProfile;
var localized string	ErrorExportingProfile;
var localized string	ErrorNoProfilesExist;
var localized string	ErrorCopyFrom;
var localized string	ErrorCopyNoName;
var localized string	ErrorExcludeMapNotFound;
var localized string	ErrorIncludeMapNotFound;
var localized string	ErrorReplacingWebConnection;
var localized string	ImportClassSpawnError;
var localized string	ImportProfileFailText;
var localized string	ImportProfileFailTitle;

// Import errors
var localized string	ErrorBind;
var localized string	ErrorBadURL;
var localized string	ErrorServer;
var localized string	ErrorResolve;
var localized string	ErrorProfile;
var localized string	ErrorTimeout;
var localized string	ErrorNotFound;
var localized string	ErrorForbidden;
var localized string	ErrorBadRequest;
var localized string	ErrorUnauthorized;

function bool Init()
{
	local GameRules GR;

	for (GR=Level.Game.GameRulesModifiers;GR!=None;GR=GR.NextGameRules)
		if (LadderGameRules(GR) != None)
			LadderRules = LadderGameRules(GR);

	if (LadderRules == None)
	{
		Cleanup();
		return false;
	}

	if (ConfigM == None && ConfigMaster(Level.Game.BaseMutator.NextMutator) != None)
		ConfigM = ConfigMaster(Level.Game.BaseMutator.NextMutator);

	GetCurrentConfigSet();
	ExportMaster 			= new class'ProfileExporter';
	DefaultProfileName 		= LadderRules.DefaultProfileName;

	SMaster 				= new(None) class'SessionMaster';
	ProfileMaps 			= new(None) class'SortedStringArray';
	ProfileMutators 		= new(None) class'SortedStringArray';

	Assert(Level.Game.AccessControl != None);
	return true;
}

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// Session Manipulation Functions
//

// Load profile information into session object
function CreateSessionInfo(coerce int ProfileIndex, string SID, optional bool bIncludeNonActive, optional bool bSave, optional bool bQuiet)
{
    local int 										i;
    local Session 									CurS;
    local string 									TempStr;
    local array<string> 							TempArr;
    local ProfileConfigSet 							TempPCS;
    local array<ProfileConfigSet.ProfileMutator> 	AllPCSMutators;
    local array<ProfileConfigSet.ProfileMap> 		AllPCSMaps;
    local array<ProfileConfigSet.ProfileSA> AllPCSServerActors;

    if (SID == "")
        return;

    CurS = SMaster.getSession(SID,true);
	TempPCS = LadderRules.LadderProfiles[ProfileIndex];
	TempPCS.StartEdit();

// General profile information
    CurS.setValue("ProfileIndex", string(ProfileIndex), true);
	CurS.setValue("ProfileName",LadderRules.Profiles[ProfileIndex].ProfileName,true);
	CurS.setValue("GameType", string(TempPCS.GetGameClass()), True);
	CurS.setValue("ACClass", TempPCS.GetAccessControl(), True);
	CurS.setValue("MaxMaps", string(TempPCS.GetMaxMaps()), True);

	AllPCSMutators = TempPCS.GetProfileMutators();
	AllPCSMaps = TempPCS.GetProfileMaps();
	AllPCSServerActors = TempPCS.GetProfileServerActors();

    TempArr = LadderRules.GetProfileMutators(string(ProfileIndex));
    TempStr = class'wUtils103.wArray'.static.Join(TempArr,",");

// Load profile mutator information
    CurS.ClearMutators();
	for (i=0;i<AllPCSMutators.Length;i++)
	   if (AllPCSMutators[i].bRequired || (InStr(TempStr,AllPCSMutators[i].MutatorName)!=-1) || bIncludeNonActive)
	       CurS.setMutator(AllPCSMutators[i].MutatorName,True,AllPCSMutators[i].bRequired);

    TempArr = LadderRules.GetProfileMaps(string(ProfileIndex));
    TempStr = class'wUtils103.wArray'.static.Join(TempArr,",");

// Load profile map information
    CurS.ClearMaps();
	for (i=0;i<AllPCSMaps.Length;i++)
	   if (AllPCSMaps[i].bRequired || (InStr(TempStr,AllPCSMaps[i].MapName)!=-1) || bIncludeNonActive)
    	   CurS.setMap(AllPCSMaps[i].MapName,AllPCSMaps[i].MapListOrder,True,AllPCSMaps[i].bRequired);

	TempArr = LadderRules.GetProfileServerActors(string(ProfileIndex));
	TempStr = class'wUtils103.wArray'.static.Join(TempArr,",");

// Load ServerActor info
	CurS.ClearServerActors();
	for (i=0;i<AllPCSServerActors.Length;i++)
		if (AllPCSServerActors[i].bRequired || InStr(TempStr,AllPCSServerActors[i].ActorClass)!=-1 || bIncludeNonActive)
			CurS.setServerActor(AllPCSServerActors[i].ActorClass, True, AllPCSServerActors[i].bRequired);

// Load profile PlayInfo data
	for (i=0;i<TempPCS.Count();i++)
        CurS.setValue(TempPCS.GetProfileParamName(i),TempPCS.GetProfileParam(i),True);

    TempPCS.EndEdit(bSave,bQuiet);
}

// Initialize session data for recently imported profile
function ProfileConfigSet CreateImportSessionInfo(Session CurS)
{
	local int						i;
	local string 					TempStr, Tmp;
	local PlayInfo					TempPI;
	local ProfileConfigSet 			TempPCS;

	local class<Mutator>			MutClass;
	local class<GameInfo> 			GIClass;
	local class<AccessControl> 		ACClass;
	local class<Info>				ServerActorClass;
	local array<class<Info> >		SAClass;
	local array<class<Mutator> > 	MClass;

	TempPCS = LadderRules.AddProfile(CurS.getValue("ProfileName"),CurS.getValue("GameType"));
	if (TempPCS.StartEdit())
	{
		TempStr = CurS.getValue("GameType");
		if (TempStr != "")
			GIClass = class<GameInfo>(DynamicLoadObject(TempStr,class'Class'));
		if (GIClass == None) return None;

		TempStr = CurS.getValue("ACClass",Level.Game.AccessControlClass);
		if (TempStr != "")
			ACClass = class<AccessControl>(DynamicLoadObject(TempStr,class'Class'));
		if (ACClass == None) return None;

		TempPI = new(None) class'PlayInfo';

// Mutators
		for (i=0;i<CurS.getMutatorLength();i++)
		{
			TempStr = CurS.getMutator(i);
			MutClass = class<Mutator>(DynamicLoadObject(TempStr, class'Class'));
			if (MutClass != None)
			{
				MClass[MClass.Length] = MutClass;
				TempPCS.AddProfileMutator(TempStr,CurS.MutRequired(TempStr));
			}

			else log(ErrorRemoteMutator@TempStr,LOGNAME);
		}

		for (i=0;i<CurS.getSALength();i++)
		{
			TempStr = CurS.getServerActor(i);
			ServerActorClass = class<Info>(DynamicLoadObject(TempStr,class'Class'));
			if (ServerActorClass != None)
			{
				SAClass[SAClass.Length] = ServerActorClass;
				TempPCS.AddProfileServerActor(TempStr,CurS.SARequired(TempStr));
			}
			else log(ErrorRemoteServerActor@TempStr,LOGNAME);
		}

		LoadThisPlayInfo(TempPI, GIClass, ACClass, MClass, SAClass);

// Maps
		for (i=0;i<CurS.getMapLength();i++)
		{
			TempStr = CurS.getMap(i);
			TempPCS.AddProfileMap(TempStr,CurS.MapRequired(TempStr));
		}

// PlayInfo
		for (i=0;i<TempPI.Settings.Length;i++)
		{
			TempStr = ""; Tmp="";
			if (CurS.getIdValue(i,TempStr,Tmp) == -1)
				break;
			if ( !SettingIsAllowed(TempPI.Settings[i].SecLevel) ) continue;
			if (!TempPCS.SetProfileNamedParam(TempPI.Settings[i].SettingName, CurS.getValue(TempPI.Settings[i].SettingName,TempPI.Settings[i].Value)))
				TempPCS.SetProfileParam(TempPCS.Count(),TempPI.Settings[i].SettingName,CurS.getValue(TempPI.Settings[i].SettingName,TempPI.Settings[i].Value));
		}

		TempPCS.EndEdit(True);
		return TempPCS;
	}

	else log(ErrorServer,LOGNAME);

	return None;
}

// Clear session data and optionally create new session object
function string ResetSessionID(Session CurS, optional bool bDestroySession)
{
	local string OldSID;

	if (bDestroySession) SMaster.destroySession(CurS);
	else CurS.ResetSession();

	CurS = SMaster.getSession(OldSID,true);
	return CurS.GetHash();
}


////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Profile manipulation methods
//

function int LoadThisPlayInfo(PlayInfo PI, class<GameInfo> ThisGameInfo, class<AccessControl> ThisAccessControl, array<class<Mutator> > ThisMutator, array<class<Info> > ThisServerActor)
{
	local int i;
	local class<Mutator> M;

	if (PI == None)
		return 0;
	PI.Clear();

	if (ThisGameInfo == None)
		return 1;

	if (ThisAccessControl == None)
		return 2;

	if (ThisMutator.Length > 0)
		for (i=0;i<ThisMutator.Length;i++)
			if (ThisMutator[i] == None)
				return 3;

	if (ThisServerActor.Length > 0)
		for (i=0;i<ThisServerActor.Length;i++)
			if (ThisServerActor[i] == None)
				return 4;

	ThisGameInfo.static.FillPlayInfo(PI);
	PI.PopClass();

	if (ThisGameInfo.default.MutatorClass != "")
	{
		M = class<Mutator>(DynamicLoadObject(ThisGameInfo.default.MutatorClass,class'Class'));
		if (M != None)
			M.static.FillPlayInfo(PI);
	}

	ThisAccessControl.static.FillPlayInfo(PI);
	PI.PopClass();

	for (i = 0; i < ThisMutator.Length; i++)
	{
		ThisMutator[i].static.FillPlayInfo(PI);
		PI.PopClass();
	}

	for (i = 0; i < ThisServerActor.Length; i++)
	{
		ThisServerActor[i].static.FillPlayInfo(PI);
		PI.PopClass();
	}

	class'CommandLineParams'.static.FillPlayInfo(PI);
	PI.PopClass();

	return -1;
}

function ProfileConfigSet GetCurrentConfigSet()
{
	local int i;

	if (CurrentProfile == None)
	{
		i = LadderRules.FindActiveProfile();
		if (i >= 0)	CurrentProfile = LadderRules.LadderProfiles[i];
	}

	return CurrentProfile;
}

function int GetCurrentConfigIndex()
{
	local int i;

	if (CurrentProfile != None)
		for (i = 0;i < LadderRules.LadderProfiles.Length;i++)
			if (LadderRules.LadderProfiles[i] == CurrentProfile)
				return i;

	return LadderRules.FindActiveProfile();
}

// Returns an array of class names for mutators that this profile will use in the game
function array<string> LoadActiveProfileMutators(optional string CurrentProfileItem)
{
	return LadderRules.GetProfileMutators(CurrentProfileItem);
}

function array<string> LoadActiveProfileServerActors(optional string CurrentProfileItem)
{
	return LadderRules.GetProfileServerActors(CurrentProfileItem);
}

// Returns an array of map names that in the maplist for this profile
function array<string> LoadActiveProfileMaps(optional string CurrentProfileItem)
{
	return LadderRules.GetProfileServerActors(CurrentProfileItem);
}
/*
function CreateFullMutatorList(out StringArray Mutators, out StringArray GroupsOnly)
{
	local StringArray Grouped;
	local int i,j,z;
	local string GrpName;
	local string thisgroup, nextgroup;

// This class provides a specialized sorting function
// since mutators may be grouped - mutators in the same groups
// are not allowed to be selected togther

	Grouped = new(None) class'SortedStringArray';

// Create array sorted on GroupName...this allows to flag
// the mutator for a different .inc file
	for (i = 0; i < Mutators.Count(); i++)
	{
		j = int(Mutators.GetItem(i));

// If the mutator author forgot to configure a group name for the mutator
		if (AllMutators[j].GroupName == "")
			GrpName = "Z" $ string (z++);
		else GrpName = AllMutators[j].GroupName;

		Grouped.Add(string(j),GrpName $ "." $ AllMutators[j].FriendlyName);
	}

// Move all grouped mutators to GroupsOnly StringArray for sorting by friendly name
	for (i=0;i<Grouped.Count();i++)
	{
		thisgroup = AllMutators[int(Grouped.GetItem(i))].GroupName;
		nextgroup = "";

		if (thisgroup=="")
			continue;
		if (i+1<Grouped.Count())
			nextgroup = AllMutators[int(Grouped.GetItem(i+1))].GroupName;

		if (thisgroup ~= nextgroup)
		{
			j=i;
			while(nextgroup~=thisgroup && j<Grouped.Count())
			{
				GroupsOnly.MoveFromId(Grouped, Grouped.FindItemId(Grouped.GetItem(j)));
				thisgroup = nextgroup;
				if (j+1==Grouped.Count())
					nextgroup="";
				else nextgroup = AllMutators[int(Grouped.GetItem(j+1))].GroupName;
			}
			if (j<Grouped.Count())
				GroupsOnly.MoveFromId(Grouped,Grouped.FindItemId(Grouped.GetItem(j)));
			i=-1;
		}
	}

// Move all non-grouped mutators back to Mutators array
// for re-sorting by Friendly Name
	Mutators.Reset();
	for (i=0;i<Grouped.Count();i++)
	{
		j=int(Grouped.GetItem(i));
		Mutators.Add(string(j),AllMutators[j].FriendlyName);
	}
	Grouped.Reset();
}
*/
//****************************************************************
//****************************************************************
// Helper functions
//
//****************************************************************
//**************************
// HTML Generation
//*************************

// Creates a listing of PlayInfo data
// Pass "defaults_row" in the 'IncludeFile' param to add the
// input form objects to the output

function string GeneratePlayInfoRows(WebRequest Req, WebResponse Res, string IncludeFile)
{
	local int i,j,k,z;
	local string 	TempStr, Data, Op, SID,
					TempRow, HeadingRow, PlayInfoRows;

	local Session 		CurS;
	local PlayInfo 		TempPI;
	local StringArray 	Info, InfoC, InfoS, InfoT;

	local array<string> 		Options;
	local class<Mutator> 		TempMut;
	local class<Info>			TempSA;
	local class<GameInfo> 		GIClass;
	local class<AccessControl> 	ACClass;
	local array<class<Mutator> > MClass;
	local array<class<Info> >	SAClass;

	SID = Req.GetVariable("SessionID");
	CurS = SMaster.GetSession(SID);

	TempStr = CurS.getValue("GameType");
	if (TempStr != "")
		GIClass = class<GameInfo>(DynamicLoadObject(TempStr,class'Class'));
	if (GIClass == None)
	{
		ShowMessage(Res,ErrorGameClassTitle,ErrorGameClassText);
		return "";
	}

	TempStr = CurS.getValue("ACClass",Level.Game.AccessControlClass);
	if (TempStr != "")
		ACClass = class<AccessControl>(DynamicLoadObject(TempStr,class'Class'));
	if (ACClass == None)
	{
		ShowMessage(Res,ErrorAccessControlTitle,ErrorAccessControlText);
		return "";
	}

	for (i=0;i<CurS.GetMutatorLength();i++)
	{
		TempStr = CurS.getMutator(i);
		if (TempStr != "")
		{
			TempMut = class<Mutator>(DynamicLoadObject(TempStr,class'Class'));
			if (TempMut != None)
				MClass[MClass.Length] = TempMut;
		}
	}

	for (i=0;i<CurS.getSALength();i++)
	{
		TempStr = CurS.getServerActor(i);
		if (TempStr != "")
		{
			TempSA = class<Info>(DynamicLoadObject(TempStr,class'Class'));
			if (TempSA != None)
				SAClass[SAClass.Length] = TempSA;
		}
	}

	// Initialize and fill PlayInfo
	TempPI = new(None) class'PlayInfo';
	LoadThisPlayInfo(TempPI, GIClass, ACClass, MClass, SAClass);

	for (i=0;i<TempPI.Settings.Length;i++)
		if (CurS.HasData(TempPI.Settings[i].SettingName))
			TempPI.StoreSetting(i, CurS.getValue(TempPI.Settings[i].SettingName,TempPI.Settings[i].Value));

	InfoC = new(None) class'SortedStringArray';	// Checkboxes
	InfoS = new(None) class'SortedStringArray';	// Selects
	InfoT = new(None) class'SortedStringArray';	// Textboxes
	Info  = new(None) class'SortedStringArray';	// Contains each unique ClassFrom value

	// Want to allow admin to input values for each setting
	if (IncludeFile ~= "defaults_row")
	{
		for (i=0;i<TempPI.Settings.Length;i++)
		{
			if (TempPI.Settings[i].RenderType~="Text")
			{
				// Hack for MOTD sorting
				if (Left(TempPI.Settings[i].SettingName,Len(TempPI.Settings[i].SettingName) - 1) ~= "GameReplicationInfo.MOTDLine")
					InfoT.Add(string(i), TempPI.Settings[i].SettingName);
				else InfoT.Add(string(i),TempPI.Settings[i].DisplayName);
			}

			else if (TempPI.Settings[i].RenderType~="Check")
				InfoC.Add(string(i),TempPI.Settings[i].DisplayName);

			else if (TempPI.Settings[i].RenderType~="Select")
				InfoS.Add(string(i),TempPI.Settings[i].DisplayName);

			for (j=0;j<Info.Count();j++)
				if (Info.GetTag(j) ~= string(TempPI.Settings[i].ClassFrom))
					break;

			if (j == Info.Count())
				Info.Add(string(i), string(TempPI.Settings[i].ClassFrom));
		}

		for (k=0;k<Info.Count();k++)
		{
			TempRow = ""; HeadingRow = "";
			Res.Subst("RowContent", "<td class=ttext colspan=\"3\">"$Info.GetTag(k)$"</td>");
			HeadingRow = WebInclude(RowLeft);

			for (i=0;i<InfoC.Count();i++)
			{
				TempStr = "";
				j=int(InfoC.GetItem(i));
				if (!SettingIsAllowed(TempPI.Settings[j].SecLevel) || (string(TempPI.Settings[j].ClassFrom)!=Info.GetTag(k)))
					continue;

				Res.Subst("Content", Checkbox(TempPI.Settings[j].SettingName, TempPI.Settings[j].Value ~= "True", TempPI.Settings[j].Data != ""));
				TempStr = WebInclude(CellLeft);

				Res.Subst("Content", "<span class=location title=\"Required Security Level\">"$TempPI.Settings[j].SecLevel$"</span>");
				TempStr += WebInclude(CellLeft);

				Res.Subst("Content", "<span class='location'>"$HtmlEncode(TempPI.Settings[j].DisplayName)$"</span>");
				Res.Subst("RowContent", WebInclude(CellLeft) $ TempStr);
				TempRow += WebInclude(RowLeft);
			}

			for (i=0;i<InfoT.Count();i++)
			{
				TempStr = "";
				j = int(InfoT.GetItem(i));
				if (!SettingIsAllowed(TempPI.Settings[j].SecLevel) || (string(TempPI.Settings[j].ClassFrom)!=Info.GetTag(k)))
					continue;

				Data = "8";
				if (TempPI.Settings[j].Data != "")
				{
					Data = TempPI.Settings[j].Data;
					Divide(TempPI.Settings[j].Data, ";", Data, Op);
					TempPI.SplitStringToArray(Options, Op, ":");
				}

				Op = "";
				if (Options.Length > 1)
					Op = " ("$Options[0]$" - "$Options[1]$")";

				Res.Subst("Content", Textbox(TempPI.Settings[j].SettingName, Data, 3 * int(Data), TempPI.Settings[j].Value) $ Op);
				TempStr = WebInclude(CellLeft);

				Res.Subst("Content", "<span class=location title=\"Required Security Level\">"$TempPI.Settings[j].SecLevel$"</span>");
				TempStr += WebInclude(CellLeft);

				Res.Subst("Content", "<span class='location'>"$HtmlEncode(TempPI.Settings[j].DisplayName)$"</span>");
				Res.Subst("RowContent", WebInclude(CellLeft) $ TempStr);
				TempRow += WebInclude(RowLeft);
			}

			for (i=0;i<InfoS.Count();i++)
			{
				Data = ""; TempStr = "";
				j=int(InfoS.GetItem(i));
				if (!SettingIsAllowed(TempPI.Settings[j].SecLevel) || (string(TempPI.Settings[j].ClassFrom)!=Info.GetTag(k)))
					continue;
				// Build a set of options from PID.Data
				TempPI.SplitStringToArray(Options, TempPI.Settings[j].Data, ";");
				for (z = 0; (z+1)<Options.Length; z += 2)
				{
					Data += ("<option value='"$Options[z]$"'");
					If (TempPI.Settings[j].Value == Options[z])
						Data @= "selected";
					Data += (">"$HtmlEncode(Options[z+1])$"</option>");
				}

				Res.Subst("Content", Select(TempPI.Settings[j].SettingName, Data));
				TempStr = WebInclude(CellLeft);

				Res.Subst("Content", "<span class=location title=\"Required Security Level\">"$TempPI.Settings[j].SecLevel$"</span>");
				TempStr += WebInclude(CellLeft);

				Res.Subst("Content", "<span class='location'>"$HtmlEncode(TempPI.Settings[j].DisplayName)$"</span>");
				Res.Subst("RowContent", WebInclude(CellLeft) $ TempStr);
				TempRow += WebInclude(RowLeft);
			}

			if (TempRow!="")
				PlayInfoRows += (HeadingRow$TempRow);
		}
	}
	else	// Displaying values only
	{
		for (i=0;i<TempPI.Settings.Length;i++)
		{
			if (Left(TempPI.Settings[i].SettingName,Len(TempPI.Settings[i].SettingName) - 1) ~= "GameReplicationInfo.MOTDLine")
				InfoS.Add(string(i), TempPI.Settings[i].SettingName);
			else InfoS.Add(string(i),TempPI.Settings[i].DisplayName);

			for (j=0;j<Info.Count();j++)
				if (Info.GetTag(j) ~= string(TempPI.Settings[i].ClassFrom))
					break;
			if (j == Info.Count())
				Info.Add(string(i), string(TempPI.Settings[i].ClassFrom));
		}

		for (k=0;k<Info.Count();k++)
		{
			TempRow = "";
			HeadingRow = "<tr><td class=ttext colspan=\"2\">"$Info.GetTag(k)$"</td></tr>";
			for (i=0;i<InfoS.Count();i++)
			{
				j=int(InfoS.GetItem(i));
				Data = "";

				if (!SettingIsAllowed(TempPI.Settings[j].SecLevel) || (string(TempPI.Settings[j].ClassFrom)!=Info.GetTag(k)))
					continue;

				Res.Subst("SettingName", HtmlEncode(TempPI.Settings[j].DisplayName));

				if (TempPI.Settings[j].Value ~= "true")
					Data = "True";

				else if (TempPI.Settings[j].Value ~= "false")
					Data = "False";

				else
				{
					if (TempPI.Settings[j].RenderType ~= "Select")
					{
						class'wUtils103.wString'.static.Split2(TempPI.Settings[j].Data, ";", Options);
						for (z=0;(z+1)<Options.Length;z+=2)
						{
							if (TempPI.Settings[j].Value == Options[z])
							{
								Data = Options[z+1];
								break;
							}
						}
					}
					else Data = TempPI.Settings[j].Value;
				}

				Res.Subst("SettingData", HtmlEncode(Data));
				Res.Subst("Extra", "");
				TempRow += WebInclude(IncludeFile);
			}

			if (TempRow!="")
				PlayInfoRows += (HeadingRow$TempRow);
		}
	}
	return PlayInfoRows;
}

// Creates the drop-down box containing profiles names
function String GenerateSwitchProfileOptions(optional string CurrentProfileItem)
{
	local int i;
	local string SelectedStr, OptionStr;

	if (LadderRules.AllLadderProfiles.Count() == 0)
		return "<option value=\"\">***"@NoneText@"***</option>";

	for (i=0; i < LadderRules.AllLadderProfiles.Count(); i++)
	{
	// This item was the item passed in by CurrentProfileItem, so make it active
		if (CurrentProfileItem ~= LadderRules.AllLadderProfiles.GetItem(i))
			SelectedStr = " selected";
		else SelectedStr = "";

		OptionStr = OptionStr$"<option value=\""$LadderRules.AllLadderProfiles.GetItem(i)$"\""$SelectedStr$">"$LadderRules.AllLadderProfiles.GetTag(i)@"</option>";
	}
	return OptionStr;
}

function bool SettingIsAllowed(byte SettingSecLevel)
{
	return SettingSecLevel <= CurAdmin.MaxSecLevel();
}

// Handles generation of the double map select boxes (Exclude & Include)
function String CreateMapListSelect(array<ProfileConfigSet.ProfileMap> AllPCSMaps, StringArray MapList, StringArray MovedMaps)
{
	local int i,j;
	local String ResponseStr, SelectedStr, Tmp;

	if (MapList.Count() == 0)
		return "<option value=\"\">***"@NoneText@"***</option>";

	for (i = 0; i<MapList.Count(); i++)
	{
	// Index for map within AllPCSMaps
		j = int(MapList.GetItem(i));

		Tmp = AllPCSMaps[j].MapName;
		SelectedStr = "";
		if (MovedMaps != None && MovedMaps.FindItemId(string(j)) >= 0)
			SelectedStr = " selected";
		ResponseStr = ResponseStr$"<option value=\""$Tmp$"\""$SelectedStr$">"$Tmp$"</option>";
	}

	return ResponseStr;
}

//**********************
// Import / Export
//**********************

// Handles spacing for textboxes on export page
function int GetLongestURL()
{
	local int i, Current, Largest;

	for (i = 0; i < ExportMaster.Zips.Length; i++)
	{
		Current = Len(ExportMaster.Zips[i].ZipURL);
		if (Current > Largest)
			Largest = Current;
	}

	return Largest + 1;
}

// Handles filling in zip-location textboxes on export page
function string GetLocation(string Package)
{
	local int i;

	// Check if we're dealing with a map or a mutator
	i = InStr(Package, ".");

	// Dealing with a mutator - remove class name
	if (i != -1)
		Package = Left(Package, i);

	return ExportMaster.FindZipLocation(Package);
}

// Handles saving zip file locations from export page
function SetLocation(string Package, string NewLocation)
{
	local int i;

	// Check if we're dealing with a map or a mutator
	i = InStr(Package, ".");

	// Dealing with a mutator - remove class name
	if (i != -1)
		Package = Left(Package, i);

	if (NewLocation == "")
		ExportMaster.DeleteZipLocation(Package);
	else ExportMaster.AddZipLocation(Package, NewLocation);
}

// Utility function to remove strings from profile names before
// creating .txt for exported profile
function string RemoveStr(string Source,string mask)
{
	local int i;
	local string left,right;

	i = InStr(Source,mask);
	while (i != -1)
	{
		Divide(Source,mask,left,right);
		Source = Left$Right;
		i = InStr(Source,Mask);
	}

	return Source;
}

// Prevents entering a zip file location for any package
// that is already included with the game
function bool EpicPackage(string Package)
{
	local int i;

	// Check if we're dealing with a map or a mutator
	i = InStr(Package, ".");

	// Dealing with a mutator - remove class name
	if (i != -1)
		Package = Left(Package, i);

	for (i = 0;i < EPICNUMBER; i++)
		if (Package ~= EpicPackages[i])
			return true;
	return false;
}

function CheckSettingValue(string SettingData, out string NewValue)
{
	local string sMaxLength, sMinMax, sMin, sMax;
	local int iMaxLength, iMin, iMax, iValue;
	local float fMin, fMax, fValue;
	local bool bFloat;

	if (SettingData == "")
		return;

// This setting has no valid min/max (it is a password setting or something)
	if (InStr(SettingData,";") < 0 )
	{
		// Allow only up to three times the configured max length
		iMaxLength = 3 * int(SettingData);
		if (Len(NewValue) > iMaxLength)
			NewValue = Left(NewValue,iMaxLength);
		return;
	}

	Divide(SettingData,";",sMaxLength,sMinMax);	// Split the Max Length & MinMax Info
	Divide(sMinMax,":",sMin,sMax);				// Split the Min & Max info for casting
	iMaxLength = int(sMaxLength);
	if ( InStr(sMin,".") >= 0 || InStr(sMax,".") >= 0)	// Are we dealing with floating point numbers?
	{
		bFloat = True;
		fMin = float(sMin);
		fMax = float(sMax);
	}

	else
	{
		iMin = int(sMin);
		iMax = int(sMax);
	}

	// First check for length
	if (Len(NewValue) > iMaxLength)
		NewValue = Left(NewValue,iMaxLength);

	if (bFloat)
	{
		fValue = float(NewValue);
		fValue = FClamp(fValue,fMin,fMax);
		NewValue = string(fValue);
	}
	else
	{
		iValue = int(NewValue);
		iValue = Clamp(iValue,iMin,iMax);
		NewValue = string(iValue);
	}
}
//*****************************************************************************
//*****************************************************************************
// Query Methods
//
//

// Passes the SessionID variable to all pages in this query action
// if SessionID is not present in WebRequest, generates one
// (Helpful when dealing with frames)
function bool PreQuery(WebRequest Req, WebResponse Res)
{
	local string SID;

	SID = Req.GetVariable("SessionID");
	if (SID == "")
	{
		SID = SMaster.getSession("xxxxx",true).GetHash();
		Req.AddVariable("SessionID", SID);
	}
	Res.Subst("SessionID", SID);
	return true;
}

function bool Query(WebRequest Request, WebResponse Response)
{
	switch (Mid(Request.URI, 1))
	{
	case DefaultPage: 		if (!MapIsChanging()) QueryLadderFrame(Request, Response);		return true;
	case ProfileMenuURI:	if (!MapIsChanging()) QueryLadderMenu(Request, Response);		return true;
	case ProfileViewURI:	if (!MapIsChanging()) QueryLadderView(Request, Response);		return true;
	case ProfileEditURI: 	if (!MapIsChanging()) QueryLadderEdit(Request, Response);		return true;
	case ProfileSwitchURI:	if (!MapIsChanging()) QueryLadderSwitch(Request, Response);		return true;
	case ProfileManageURI:	if (!MapIsChanging()) QueryLadderManage(Request, Response);		return true;

// Management subframes
	case SubFrameURI: 		if (!MapIsChanging()) QuerySubFrame(Request, Response);			return true;
	case SubFrameMenuURI: 	if (!MapIsChanging()) QuerySubMenu(Request, Response);			return true;
	case SubGametypeURI:	if (!MapIsChanging()) QuerySubGameType(Request, Response);		return true;
	case SubMapsURI:		if (!MapIsChanging()) QuerySubMaps(Request, Response);			return true;
	case SubMutatorsURI:	if (!MapIsChanging()) QuerySubMutators(Request, Response);		return true;
	case SubRulesURI:		if (!MapIsChanging()) QuerySubRules(Request, Response);			return true;
	case SubReviewURI:		if (!MapIsChanging()) QuerySubReview(Request, Response);		return true;
	case SubSavedURI:		if (!MapIsChanging()) QuerySubSaved(Request, Response);			return true;
	case SubImportURI: 		if (!MapIsChanging()) QuerySubImport(Request, Response);		return true;
	case SubExportURI: 		if (!MapIsChanging()) QuerySubExport(Request, Response);		return true;
	}
// This query is looking for a page from another query handler
// or the map is changing
	return false;
}

function QueryLadderFrame(WebRequest Req, WebResponse Res)
{
	local String Page;
	local string SID;

// if no page specified, use the default
	Page = Req.GetVariable("Page", ProfileViewURI);
	SID = Req.GetVariable("SessionID");

// Tell the menu page which page we're viewing (with the ?Page=),
// so that it can highlight the appropriate link
	Res.Subst("IndexURI",ProfileMenuURI$"?Page="$Page$"?SessionID="$SID);

// Load the main page into the frameset
	Res.Subst("MainURI", Page$"?SessionID="$SID);

	ShowFrame(Res, DefaultPage);
}

function QuerySubFrame(WebRequest Req, WebResponse Res)
{
	local string Page, SID, TempStr, Action;
	local Session CurS;

	SID = Req.GetVariable("SessionID");
	CurS = SMaster.getSession(SID, true);

	Action = Req.GetVariable("Actions","Add");

	if (Action ~= "Modify")
	{
		TempStr = Req.GetVariable("ProfileNameSelect", "");
		if (TempStr != "") CreateSessionInfo(TempStr,SID,True);
	}

	else if (Action ~= "Add")
	{
		TempStr = Req.GetVariable("TextInput");
		if (TempStr != "") CurS.setValue("ProfileName",TempStr,True);
	}

// if no page specified, use the default
	Page = Req.GetVariable("Page", SubGameTypeURI);
	CurS.setValue("CurrentPage", Page, True);

// Tell the SubMenu page which page we're viewing, so that it can highlight
// the appropriate link
	Res.Subst("SubMenuURI", SubFrameMenuURI$"?SessionID="$SID);

// Load the main page into the frameset
	Res.Subst("SubMainURI", Page$"?SessionID="$SID);
	ShowFrame(Res, SubFrame);
}

function QueryLadderMenu(WebRequest Req, WebResponse Res)
{
	local string SID;

	SID = Req.GetVariable("SessionID");
/*
If ?SessionID= is appended to the link, PreQuery() will not generate
a new Session when this link is queried
*/
	// Set URIs
	if (CanPerform("Lv"))
		Res.Subst("ViewProfiles", "<a href=\""$ProfileViewURI$"\" target=\"main\">"$LinkViewProfile$"</a><br />");

	if (CanPerform("Le"))
		Res.Subst("EditProfiles", "<a href=\""$ProfileEditURI$"\" target=\"main\">"$LinkEditProfile$"</a><br>");

	if (CanPerform("Ls"))
		Res.Subst("SwitchProfiles", "<a href=\""$ProfileSwitchURI$"\" target=\"main\">"$LinkSwitchProfile$"</a><br>");

	if (CanPerform("Lm"))
		Res.Subst("ManageProfiles", "<a href=\""$ProfileManageURI$"\" target=\"main\">"$LinkManageProfile$"</a><br>");

	ShowPage(Res, ProfileMenuURI);
}

function QuerySubMenu(WebRequest Req, WebResponse Res)
{
// Highlight the current page
	local string Highlighted, Background, SID;
	local string GameLink, MutatorLink, MapLink, RuleLink, ReviewLink;
	local string GameSpan, MutatorSpan, MapSpan, RuleSpan, ReviewSpan;

	local string Page;
	local Session CurS;

	if (CanPerform("Lm"))
	{
		Highlighted = "<span class=\"blue\">";
		Background = "<span class=\"red\">";
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.getSession(SID, true);

		// We are managing an existing profile
		if (CurS.HasData("ProfileIndex"))
	    	Res.Subst("Section", LabelModifyProfile);

		else Res.Subst("Section", LabelAddProfile);

		Page = CurS.getValue("CurrentPage",SubGametypeURI);

	// Including ?SessionID= in link prevents PreQuery() from generating a new Session
		GameLink = 		"<a href=\""$SubFrameURI$"?SessionID="$SID$"?Page="$SubGametypeURI$	"\" target=\"_parent\">";
		MutatorLink = 	"<a href=\""$SubFrameURI$"?SessionID="$SID$"?Page="$SubMutatorsURI$	"\" target=\"_parent\">";
		MapLink = 		"<a href=\""$SubFrameURI$"?SessionID="$SID$"?Page="$SubMapsURI$		"\" target=\"_parent\">";
		RuleLink = 		"<a href=\""$SubFrameURI$"?SessionID="$SID$"?Page="$SubRulesURI$	"\" target=\"_parent\">";
		ReviewLink = 	"<a href=\""$SubFrameURI$"?SessionID="$SID$"?Page="$SubReviewURI$	"\" target=\"_parent\">";

	// Initially set all spans to the background color
		GameSpan 	= Background;
		MutatorSpan = Background;
		MapSpan 	= Background;
		RuleSpan 	= Background;
		ReviewSpan 	= Background;

		// Then set the appropriate span to the highlighted color
		Res.Subst("SubTitle", CurS.GetValue("ProfileName"));
		switch (Page)
		{
			case SubGametypeURI:
				Curs.SetValue("ButtonName",		ProfileButtonName[1],true);
				Curs.SetValue("ButtonValue", 	ProfileButtonValue[1],true);

				Res.Subst("PageHelp", HelpGameType);
				GameSpan = Highlighted; break;

			case SubMutatorsURI:
				Curs.SetValue("ButtonName", 	ProfileButtonName[2],true);
				Curs.SetValue("ButtonValue", 	ProfileButtonValue[2],true);

				Res.Subst("PageHelp", HelpMutators);
				MutatorSpan = Highlighted; break;

			case SubMapsURI:
				Curs.SetValue("ButtonName", 	ProfileButtonName[3],true);
				Curs.SetValue("ButtonValue", 	ProfileButtonValue[3],true);

				Res.Subst("PageHelp", HelpMaps);
				Mapspan = Highlighted; break;

			case SubRulesURI:
				Curs.SetValue("ButtonName", 	ProfileButtonName[4],true);
				Curs.SetValue("ButtonValue", 	ProfileButtonValue[4],true);

				Res.Subst("PageHelp", HelpPlayInfo);
				RuleSpan = Highlighted; break;

			case SubReviewURI:
				Curs.SetValue("ButtonName", 	ProfileButtonName[5],true);
				Curs.SetValue("ButtonValue", 	ProfileButtonValue[5],true);

				Res.Subst("PageHelp", HelpReview);
				ReviewSpan = Highlighted; break;
		}

		GameLink 	= GameLink 		$ GameSpan 		$ TableTitle[1]$"</span></a>";
		MutatorLink = MutatorLink 	$ MutatorSpan 	$ TableTitle[2]$"</span></a>";
		MapLink 	= MapLink 		$ MapSpan 		$ TableTitle[3]$"</span></a>";
		RuleLink 	= RuleLink 		$ RuleSpan 		$ TableTitle[4]$"</span></a>";
		ReviewLink 	= ReviewLink 	$ ReviewSpan 	$ TableTitle[5]$"</span></a>";

		Res.Subst("GameTypeLink", 	GameLink);
		Res.Subst("MutatorsLink", 	MutatorLink);
		Res.Subst("MapsLink", 		MapLink);
		Res.Subst("RulesLink", 		RuleLink);
		Res.Subst("ReviewLink", 	ReviewLink);

		ShowPage(Res, SubFrameMenu);
	}

	else AccessDenied(Res);
}

function QueryLadderView(WebRequest Req, WebResponse Res)
{
	local int i,j;
	local Session 	CurS;
	local string	SID,
					TempStr,
					ViewRows,
					NewProfile,
					NewGameType;

	local StringArray 			Mutators, GroupsOnly, ServerActors;
	local class<GameInfo> 		GIClass;
	local class<AccessControl> 	ACClass;
	local class<Mutator>		TempMut;
	local class<Info>			TempSA;


	if (CanPerform("Lv"))
	{
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.getSession(SID,true);

		if (CanPerform("Ls"))
		{

			if (Req.GetVariable("SetProfileName","")!="") // We would like to view a different profile
				TempStr = Req.GetVariable("ProfileNameSelect", "");

			If (TempStr == "")	// Not selecting a new profile to view
			{
			// First, see if the current session is holding a profile
				NewProfile = CurS.getValue("ProfileIndex");
				if (NewProfile == "")
				{
				// See if a profile is currently loaded
					j = GetCurrentConfigIndex();
					if (j!=-1)		// Found an active profile - this profile should be selected
						NewProfile = string(j);
				}
			}

			if (TempStr!="")
				NewProfile = TempStr;

			Res.Subst("ButtonName", "SetProfileName");
			Res.Subst("ButtonValue", ButtonLoadProfile);
			Res.Subst("PageCaption", "<select class=mini name='ProfileNameSelect'>"$GenerateSwitchProfileOptions(NewProfile)$"</select>");
		}

	// If admin doesn't have switch privileges, check for current profile
		if (NewProfile == "")
		{
			NewProfile = CurS.getValue("ProfileIndex");
			if (NewProfile == "")
			{
				j = GetCurrentConfigIndex();
				if (j != -1)
					NewProfile = string(j);
			}
		}

		if (NewProfile != "" && LadderRules.AllLadderProfiles.Count() > 0)
		{
			CurS.setValue("ProfileIndex", NewProfile, True);
            CreateSessionInfo(NewProfile,SID);
			NewGameType = CurS.getValue("GameType");

			if (NewGameType != "")
				GIClass = class<GameInfo>(DynamicLoadObject(NewGameType,class'Class'));
			if (GIClass==None)
			{
				ShowMessage(Res,ErrorGameClassTitle,ErrorGameClassText);
				return;
			}

			TempStr = CurS.getValue("ACClass",Level.Game.AccessControlClass);
			if (TempStr != "")
				ACClass = class<AccessControl>(DynamicLoadObject(TempStr,class'Class'));
			if (ACClass == None)
			{
				ShowMessage(Res,ErrorAccessControlTitle,ErrorAccessControlText);
				return;
			}

			Mutators = new(None) class'SortedStringArray';
			GroupsOnly = new(None) class'SortedStringArray';
			ServerActors = new(None) class'SortedStringArray';
		// Mutator Section
			for (i=0;i<CurS.getMutatorLength();i++)
			{
			    TempStr = CurS.getMutator(i);
                for (j=0;j<AllMutators.Length;j++)
				{
					if (TempStr ~= AllMutators[j].ClassName)
					{
						TempMut = class<Mutator>(DynamicLoadObject(TempStr,class'Class'));
						if (TempMut != None)
							Mutators.Add(string(j), AllMutators[j].FriendlyName);
					}
				}
			}

			for (i=0;i<CurS.getSALength();i++)
			{
				TempStr = CurS.GetServerActor(i);
				for (j=0;j<ConfigM.ManagedActors.Length;j++)
				{
					if (TempStr ~= string(ConfigM.ManagedActors[j].SAClass))
					{
						TempSA = class<Info>(DynamicLoadObject(TempStr,class'Class'));
						if (TempSA != None)
							ServerActors.Add(string(j),ConfigM.ManagedActors[j].SAName);
					}
				}
			}

			Res.Subst("SectionName", TableTitle[2]);
			Res.Subst("SpecialInstructions", "");
			ViewRows = ViewRows$WebInclude(IncludeSection);

			for (i = 0; i<ServerActors.Count(); i++)
			{
				j = int(ServerActors.GetItem(i));
				Res.Subst("SettingName", ServerActors.GetTag(i));
				if (CurS.SARequired(string(ConfigM.ManagedActors[j].SAClass)))
					Res.Subst("SettingData", RequiredText);
				else Res.Subst("SettingData", "&nbsp;&nbsp;");
				ViewRows = ViewRows $ WebInclude(IncludeRows);
			}
			ViewRows = ViewRows $ "<tr><td colspan=\"2\">&nbsp;&nbsp;</td></tr>";
			for (i = 0; i<Mutators.Count(); i++)
			{
				j = int(Mutators.GetItem(i));
				Res.Subst("SettingName", AllMutators[j].FriendlyName);
				if (CurS.MutRequired(AllMutators[j].ClassName))
					Res.Subst("SettingData", RequiredText);
				else Res.Subst("SettingData", "&nbsp;&nbsp;");
				ViewRows = ViewRows$WebInclude(IncludeRows);
			}

			Res.Subst("SectionName", TableTitle[3]);
			Res.Subst("SpecialInstructions", "");
			ViewRows = ViewRows$WebInclude(IncludeSection);

			Res.Subst("SettingName", LabelMaxMaps);
			TempStr = CurS.getValue("MaxMaps");
			if (TempStr == "0")
				TempStr = UnlimitedText;
			Res.Subst("SettingData", TempStr);
			Res.Subst("Extra", "<tr><td colspan=\"2\">&nbsp;&nbsp;</td></tr>");
			ViewRows = ViewRows$WebInclude(IncludeRows);
			for (i=0;i<CurS.getMapLength();i++)
			{
                TempStr = CurS.getMap(i);
                Res.Subst("SettingName", TempStr);
				Res.Subst("Extra", "");
				if (CurS.MapRequired(TempStr))
					Res.Subst("SettingData", RequiredText);
				else Res.Subst("SettingData", "&nbsp;&nbsp");

				ViewRows = ViewRows$WebInclude(IncludeRows);
			}

			Res.Subst("SectionName", TableTitle[4]);
			Res.Subst("SpecialInstructions", "&nbsp;&nbsp;");
			ViewRows = ViewRows $ WebInclude(IncludeSection);
			ViewRows = ViewRows $ "<tr><td colspan=\"2\">&nbsp;</td></tr>" $ GeneratePlayInfoRows(Req, Res, IncludeRows);
		}

	// We have no profile currently active
		else
		{
			Res.Subst("SettingName", PageCaption[0]);
			ViewRows = ViewRows $ WebInclude(IncludeRows);
		}

		TempStr = CurS.getValue("ProfileName", PageCaption[0]);
		if (LadderRules.GetRemainingMatches() > 0 && TempStr == LadderRules.Profiles[GetCurrentConfigIndex()].ProfileName)
			TempStr = TempStr @ LabelNumberMatchesLeft @ string(LadderRules.GetRemainingMatches());

		Res.Subst("TableRows", ViewRows);
		Res.Subst("LadderTable", WebInclude(IncludeCaptionButton)$WebInclude(IncludeTable));
		Res.Subst("Section", LinkViewProfile);
		Res.Subst("SubTitle", TempStr);
		Res.Subst("PageHelp", HelpViewProfile);
		Res.Subst("PostAction", ProfileViewURI$"?SessionID="$SID);

		ShowPage(Res, MasterPage);
	}

	else AccessDenied(Res);
}

function QueryLadderEdit(WebRequest Req, WebResponse Res)
{
	local Session CurS;
	local bool 	bAllMuts;
	local int 	i,j,k,z,
				id,
				Count,
				MoveCount;

	local string	SID,
					EditRows,
					TempStr, Tmp, NewProfile, NewGameType,
					lastgroup, thisgroup, nextgroup, EmptyRow;

	local ProfileConfigSet 		TempPCS;
	local array<string> 		TempMutes;
	local class<GameInfo> 		GIClass;
	local class<AccessControl> 	ACClass;
	local array<ProfileConfigSet.ProfileMap> 		AllPCSMaps;
	local array<ProfileConfigSet.ProfileMutator> 	AllPCSMutators;
	local array<ProfileConfigSet.ProfileSA>			AllPCSServerActors;
	local StringArray
				Mutators, GroupsOnly, Required, ServerActors, ReqSA,
				RequiredMaps, IncludeMaps, ExcludeMaps, MovedMaps;

	SID = Req.GetVariable("SessionID");
	CurS = SMaster.getSession(SID,true);
	if (CanPerform("Le"))
	{
		if (CanPerform("Ls"))
		{
			if (Req.GetVariable("SetProfileName","")!="") // We would like to view a different profile
				TempStr = Req.GetVariable("ProfileNameSelect", "");

			If (TempStr == "")	// Not selecting a new profile to view
			{
			// First, see if the current session is holding a profile
				NewProfile = CurS.getValue("ProfileIndex");

				if (NewProfile == "")
				{
				// See if a profile is currently loaded
					j = GetCurrentConfigIndex();
					if (j!=-1)		// Found an active profile - this profile should be selected
						NewProfile = string(j);
				}

			}

			if (TempStr!="")
				NewProfile = TempStr;

			Res.Subst("ButtonName", "SetProfileName");
			Res.Subst("ButtonValue", ButtonLoadProfile);
			Res.Subst("PageCaption", "<select class=mini name='ProfileNameSelect'>"$GenerateSwitchProfileOptions(NewProfile)$"</select>");
		}
		CurS.setValue("CurrentPage", ProfileEditURI, true);
		CurS.setValue("ButtonName", "ApplyProfileChanges",true);
		CurS.setValue("ButtonValue", ButtonApplyProfileChanges, true);

// If admin doesn't have switch privileges, check for current profile
		if (NewProfile == "")
		{
			NewProfile = CurS.getValue("ProfileIndex");
			if (NewProfile == "")
			{
				j = GetCurrentConfigIndex();
				if (j != -1)
					NewProfile = string(j);
			}
		}

		CurS.setValue("ProfileIndex", NewProfile, True);
		if (NewProfile != "" && LadderRules.AllLadderProfiles.Count() > 0)
		{
			ProfileMaps.Reset();
			Required = new(None) 		class'SortedStringArray';
			ReqSA	= new(None)			class'SortedStringArray';
			Mutators = new(None) 		class'SortedStringArray';
			MovedMaps = new(None) 		class'SortedStringArray';
			GroupsOnly = new(None) 		class'SortedStringArray';
			ServerActors = new(None)	class'SortedStringArray';
			IncludeMaps = new(None) 	class'SortedStringArray';
			ExcludeMaps = new(None) 	class'SortedStringArray';
			RequiredMaps = new(None) 	class'SortedStringArray';

            CreateSessionInfo(NewProfile,SID);
			TempPCS = LadderRules.LadderProfiles[int(NewProfile)];
			TempPCS.StartEdit();

			NewGameType = CurS.getValue("GameType");
			GIClass = class<GameInfo>(DynamicLoadObject(NewGameType,class'Class'));
			if (GIClass==None)
			{
				ShowMessage(Res,ErrorGameClassTitle,ErrorGameClassText);
				return;
			}

			TempStr = CurS.getValue("ACClass", Level.Game.AccessControlClass);
			if (TempStr != "")
				ACClass = class<AccessControl>(DynamicLoadObject(TempStr,class'Class'));
			AllPCSMutators = TempPCS.GetProfileMutators();
			AllPCSMaps = TempPCS.GetProfileMaps();
			AllPCSServerActors = TempPCS.GetProfileServerActors();

	// Load Exclude Maps
			for (i=0;i<AllPCSMaps.Length;i++)
			{
				ProfileMaps.Add(string(i), AllPCSMaps[i].MapName);
				if (AllPCSMaps[i].bRequired)
				{
					RequiredMaps.Add(string(i),string(AllPCSMaps[i].MapListOrder));
					IncludeMaps.Add(string(i),string(AllPCSMaps[i].MapListOrder));
				}

				else ExcludeMaps.Add(string(i),string(AllPCSMaps[i].MapListOrder));
			}

			for (i=0;i<CurS.GetMapLength();i++)
			{
				j = ExcludeMaps.FindItemId(ProfileMaps.GetItem(ProfileMaps.FindTagId(CurS.getMap(i))));
				if (j >= 0) IncludeMaps.MoveFromId(ExcludeMaps, j);
			}

			if (Req.GetVariable("ApplyProfileChanges","")!="")
			{
				for (i=0;i<AllPCSMutators.Length;i++)
					for (j=0;j<AllMutators.Length;j++)
						if (AllPCSMutators[i].MutatorName ~= AllMutators[j].ClassName)
							Mutators.Add(string(j),AllPCSMutators[i].MutatorName);

				for (i=0;i<Mutators.Count();i++)
				{
					if ((Req.GetVariable(Mutators.GetTag(i),"")!="")||(Req.GetVariable(AllMutators[int(Mutators.GetItem(i))].GroupName)~=Mutators.GetTag(i)))
						TempPCS.AddMutator(Mutators.GetTag(i));
					else TempPCS.DelMutator(Mutators.GetTag(i));
				}

				for (i=0;i<AllPCSServerActors.Length;i++)
				{
					if (Req.GetVariable(AllPCSServerActors[i].ActorClass,"") != "")
						TempPCS.AddServerActor(AllPCSServerActors[i].ActorClass);
					else TempPCS.DelServerActor(AllPCSServerActors[i].ActorClass);
				}

				for (i=0;i<AllPCSMaps.Length;i++)
				{
					if (AllPCSMaps[i].bRequired) TempPCS.AddMap(AllPCSMaps[i].MapName,int(RequiredMaps.GetTag(RequiredMaps.FindItemId(string(i)))));
					else if (IncludeMaps.FindItemId(string(i)) >= 0) TempPCS.AddMap(AllPCSMaps[i].MapName,int(IncludeMaps.GetTag(IncludeMaps.FindItemId(string(i)))));
					else TempPCS.RemoveMap(AllPCSMaps[i].MapName);
				}

                CreateSessionInfo(NewProfile,SID,false,true);
			}
			else if (Req.GetVariable("AddMap", "") != "")
			{
				Count = Req.GetVariableCount("ExcludeMapsSelect");
				for (i=0; i<Count; i++)
				{
					if (IncludeMaps.Count() >= TempPCS.MaxMaps && TempPCS.MaxMaps > 0)
						break;
					if (ExcludeMaps.Count() > 0)
					{
						id = IncludeMaps.MoveFromId(ExcludeMaps, ExcludeMaps.FindItemId(ProfileMaps.GetItem(ProfileMaps.FindTagId(Req.GetVariableNumber("ExcludeMapsSelect", i)))));
						if (id >= 0)
						{
							TempPCS.AddMap(AllPCSMaps[int(IncludeMaps.getItem(id))].MapName,int(IncludeMaps.getTag(id)));
							MovedMaps.CopyFromId(IncludeMaps, id);
						}
						else
							Log(ErrorExcludeMapNotFound@Req.GetVariableNumber("ExcludeMapsSelect", i),LOGNAME);
					}
				}
				for (i=0;i<IncludeMaps.Count();i++)
					TempPCS.AddMap(AllPCSMaps[int(IncludeMaps.getItem(i))].MapName,i);

                CreateSessionInfo(NewProfile,SID,false,true,True);
			}

			else if (Req.GetVariable("DelMap", "") != "" && Req.GetVariableCount("IncludeMapsSelect") > 0)
			{
				Count = Req.GetVariableCount("IncludeMapsSelect");
				for (i=0; i<Count; i++)
				{
					if (RequiredMaps.FindItemId(ProfileMaps.GetItem(ProfileMaps.FindTagId(Req.GetVariableNumber("IncludeMapsSelect", i)))) < 0)
					{
						if (IncludeMaps.Count() > 0)
						{
							id = ExcludeMaps.MoveFromId(IncludeMaps, IncludeMaps.FindItemId(ProfileMaps.GetItem(ProfileMaps.FindTagId(Req.GetVariableNumber("IncludeMapsSelect", i)))));
							if (id >= 0)
							{
								TempPCS.RemoveMap(AllPCSMaps[int(ExcludeMaps.GetItem(id))].MapName);
								MovedMaps.CopyFromId(ExcludeMaps, id);
							}
							else
								Log(ErrorIncludeMapNotFound@Req.GetVariableNumber("IncludeMapsSelect", i),LOGNAME);
						}
					}
				}
                CreateSessionInfo(NewProfile,SID,false,true,True);
			}
			else if (Req.GetVariable("AddAllMap", "") != "")
			{
				while (ExcludeMaps.Count() > 0 && (IncludeMaps.Count() < TempPCS.MaxMaps || TempPCS.MaxMaps == 0) )
				{
					id = IncludeMaps.MoveFromId(ExcludeMaps, ExcludeMaps.Count()-1);
					if (id >= 0)
					{
						TempPCS.AddMap(AllPCSMaps[int(IncludeMaps.getItem(id))].MapName,int(IncludeMaps.getTag(id)));
						MovedMaps.CopyFromId(IncludeMaps, id);
					}
				}
				for (i=0;i<IncludeMaps.Count();i++)
					TempPCS.AddMap(AllPCSMaps[int(IncludeMaps.getItem(i))].MapName,i);
               CreateSessionInfo(NewProfile,SID,false,true,True);
			}
			else if (Req.GetVariable("DelAllMap", "") != "")
			{
				i = IncludeMaps.Count();
				while (i > 0 && IncludeMaps.Count() > RequiredMaps.Count())
				{
					if (RequiredMaps.FindItemId(IncludeMaps.GetItem(i-1)) < 0)
					{
						id =  ExcludeMaps.MoveFromId(IncludeMaps, i-1);
						if (id >= 0)
						{
							TempPCS.RemoveMap(AllPCSMaps[int(ExcludeMaps.GetItem(id))].MapName);
							MovedMaps.CopyFromId(ExcludeMaps, id);
						}
					}
					i--;
				}
                CreateSessionInfo(NewProfile,SID,false,true,True);
			}
			else if (Req.GetVariable("MoveMap", "") != "")
			{
				MoveCount = int(Abs(float(Req.GetVariable("MoveMapCount"))));
				if (MoveCount != 0)
				{
					Count = Req.GetVariableCount("IncludeMapsSelect");

					// 1) Create a sorted MovedMaps list
					for (i = 0; i<Count; i++)
						MovedMaps.CopyFromId(IncludeMaps, IncludeMaps.FindItemId(ProfileMaps.GetItem(ProfileMaps.FindTagId(Req.GetVariableNumber("IncludeMapsSelect", i)))));

					if (Req.GetVariable("MoveMap") ~= "Down")
					{
						// 2) Browse from Last to 0 until all maps are moved by count
						//    but stop moving them if hitting bottom
						for (i = IncludeMaps.Count()-1; i >= 0; i--)
							if (MovedMaps.FindItemId(IncludeMaps.GetItem(i)) >= 0)
								IncludeMaps.ShiftStrict(i, MoveCount);
					}
					else
					{
						MoveCount = -MoveCount;
						for (i = 0; i<IncludeMaps.Count(); i++)
							if (MovedMaps.FindItemId(IncludeMaps.GetItem(i)) >= 0)
								IncludeMaps.ShiftStrict(i, MoveCount);
					}
				}

				for (i=0;i<IncludeMaps.Count();i++)
					TempPCS.AddMap(AllPCSMaps[int(IncludeMaps.getItem(i))].MapName,i);

                CreateSessionInfo(NewProfile,SID,false,true,True);
			}

			if (TempPCS.CanEdit())
				TempPCS.StartEdit();
// Mutator Section
			Mutators.Reset();
	// Set active ServerActors
			TempMutes = LoadActiveProfileServerActors(NewProfile);
			TempStr = class'wUtils103.wArray'.static.Join(TempMutes,",");

			bAllMuts = TempMutes.Length == ConfigM.ManagedActors.Length;

			for (i=0;i<AllPCSServerActors.Length;i++)
			{
				for (j = 0; j < ConfigM.ManagedActors.Length; j++)
				{
					if (AllPCSServerActors[i].ActorClass ~= string(ConfigM.ManagedActors[j].SAClass))
					{
						if (AllPCSServerActors[i].bRequired)
							ReqSA.Add(string(j), ConfigM.ManagedActors[j].SAName);
						else ServerActors.Add(string(j),ConfigM.ManagedActors[j].SAName);
						break;
					}
				}
			}
			TempMutes = LoadActiveProfileMutators(NewProfile);
			TempStr = class'wUtils103.wArray'.static.Join(TempMutes,",");
		// Set Available Mutators for this profile
			bAllMuts = bAllMuts && AllPCSMutators.Length == AllMutators.Length;
			for (i=0;i<AllPCSMutators.Length;i++)
			{
				for (j=0;j<AllMutators.Length;j++)
				{
					if (AllMutators[j].ClassName ~= AllPCSMutators[i].MutatorName)
					{
						if (AllPCSMutators[i].bRequired)
							Required.Add(string(j),AllMutators[j].FriendlyName);
						else
						{
					// Don't add available mutator if it belongs in the same group as a required mutator
							for (k=0;k<Required.Count();k++)
								if (AllMutators[int(Required.GetItem(k))].GroupName ~= AllMutators[j].GroupName)
									break;
							if (k==Required.Count())
								Mutators.Add(string(j),AllPCSMutators[i].MutatorName);
						}
						break;
					}
				}
			}
// BEGIN OUTPUT ////////////////////////////////////////////
/*
	TempMutes is an array of active mutators, including required ones
	AllPCSMutators is a ProfileMutator array, which includes a required flag

	Need to generate a regular mutator selection list, but list all required
	first, followed by the list of all other mutators which are available
*/
// MAPS
			Res.Subst("SpanLength", "2");
			Res.Subst("SpanContent", "&nbsp;&nbsp;");
			EmptyRow = "<tr>" $ WebInclude(CellColSpan) $ "</tr>";

			Res.Subst("SectionName", TableTitle[3]);
			TempStr = string(TempPCS.GetMaxMaps());
			if (TempStr == "0") TempStr = "Unlimited";
			Res.Subst("SpecialInstructions", LabelMaxMaps$":"@TempStr);
			EditRows += WebInclude(IncludeSection);

			j = RequiredMaps.Count();
			if (j > 0)
			{
				EditRows = EditRows $ EmptyRow $ "<tr><td class=ttext>" $ RequiredText @ MapText $ "</td><td class=ttext>" $ FollowingMaps@AreAssignedText $ "</td></tr>";
				i=0;
				while (i<j)
				{
					Res.Subst("SettingName", AllPCSMaps[int(RequiredMaps.GetItem(i++))].MapName);
					Res.Subst("SettingData", StringIf(i < j, AllPCSMaps[int(RequiredMaps.GetItem(i++))].MapName, ""));

					Res.Subst("Extra", "");
					EditRows += WebInclude(IncludeRows);
				}
			}

			EditRows = EditRows $ EmptyRow $ "<tr><td class=ttext>" $ AvailableText@MapText $ "</td><td class=ttext>" $ LabelChangeMaplist $ "</td></tr>";

			Res.Subst("ExcludeMapsOptions", CreateMapListSelect(AllPCSMaps, ExcludeMaps,MovedMaps));
			Res.Subst("IncludeMapsOptions", CreateMapListSelect(AllPCSMaps, IncludeMaps,MovedMaps));
			Res.Subst("SpanContent", WebInclude(IncludeMapSelect));
			Res.Subst("RowContent", WebInclude(CellColSpan));
			EditRows += WebInclude(RowLeft);

// MUTATORS
			CreateFullMutatorList(Mutators,GroupsOnly);
			Res.Subst("SectionName", TableTitle[2]);
			Res.Subst("SpecialInstructions", "");
			EditRows += WebInclude(IncludeSection);

			if (ReqSA.Count() > 0 || Required.Count() > 0)
				EditRows = EditRows $ EmptyRow $ "<tr><td class=ttext>" $ RequiredText@MutatorText $ "</td><td class=ttext>" $ FollowingMutators@AreAssignedText $ "</td></tr>";
// Include ServerActors first
			for (i=0;i<ReqSA.Count();i++)
			{
				j = int(ReqSA.GetItem(i));
				Res.Subst("SettingName", ReqSA.GetTag(i));
				Res.Subst("SettingData", ConfigM.ManagedActors[j].SADescription);
				Res.Subst("Extra", "");

				EditRows += WebInclude(IncludeRows);
			}

			for (i=0;i<Required.Count();i++)
			{
				j=int(Required.GetItem(i));
				Res.Subst("SettingName", AllMutators[j].FriendlyName);
				Res.Subst("SettingData", AllMutators[j].Description);
				Res.Subst("Extra", "");

				EditRows += WebInclude(IncludeRows);
			}

			EditRows = EditRows $ EmptyRow $ "<tr><td class=ttext>" $ AvailableText@MutatorText $ "</td><td class=ttext>" $ FollowingMutators@AreAvailableText $ "</td></tr>";
			for (i=0;i<GroupsOnly.Count();i++)
			{
				j=int(GroupsOnly.GetItem(i));
				if (AllMutators[j].GroupName=="")
					thisgroup="Z"$string(z++);
				else thisgroup = AllMutators[j].GroupName;
				if ( (i + 1) == GroupsOnly.Count())
					nextgroup = "";
				else
				{
				// Assign nextgroup to the next mutators group
					k = int(GroupsOnly.GetItem(i + 1));
					if (AllMutators[k].GroupName=="")
						nextgroup="Z"$string(z);
					else nextgroup = AllMutators[k].GroupName;
				}

				Res.Subst("GroupName", thisgroup);
				Res.Subst("MutatorClass", AllMutators[j].ClassName);
				Res.Subst("MutatorName", AllMutators[j].FriendlyName);
				Res.Subst("MutatorDesc", AllMutators[j].Description);

				if ((lastgroup != thisgroup) && (thisgroup == nextgroup)) // and the next mut is in the same group as this one
				{
					Res.Subst("Checked", " checked");
					EditRows += WebInclude("current_mutators_group");
				}

				Res.Subst("Checked", StringIf(CurS.HasMutator(AllMutators[j].ClassName), " checked", ""));
				EditRows += WebInclude("current_mutators_group_row");

				lastgroup=thisgroup;
			}
// Add ServerActors first
			for (i = 0; i < ServerActors.Count(); i++)
			{
				TempStr = "";
				j = int(ServerActors.GetItem(i));

				Res.Subst("Content", Checkbox(string(ConfigM.ManagedActors[j].SAClass), CurS.HasServerActor(string(ConfigM.ManagedActors[j].SAClass))) $ "&nbsp;" $ ServerActors.GetTag(i));
				TempStr = WebInclude(NowrapLeft);
				Res.Subst("Content", ConfigM.ManagedActors[j].SADescription);
				Res.Subst("RowContent", TempStr $ WebInclude(CellLeft));
				EditRows += WebInclude(RowLeft);
			}

			for (i = 0; i<Mutators.Count(); i++)
			{
				TempStr = "";
				j = int(Mutators.GetItem(i));

				Res.Subst("Content", Checkbox(AllMutators[j].ClassName,CurS.HasMutator(AllMutators[j].ClassName)) $ "&nbsp;" $ AllMutators[j].FriendlyName);
				TempStr += WebInclude(NowrapLeft);
				Res.Subst("Content", AllMutators[j].Description);
				Res.Subst("RowContent", TempStr $ WebInclude(CellLeft));
				EditRows += WebInclude(RowLeft);
			}
			TempPCS.EndEdit(False);
		}
	// We have no profile currently active
		else
		{
			Res.Subst("SettingName", PageCaption[0]);
			EditRows += WebInclude(IncludeRows);
		}
		Res.Subst("TableRows", EditRows);
		TempStr = "";
		TempStr = WebInclude(IncludeCaptionButton)$Tmp$WebInclude(IncludeTable);

		if (NewProfile != "")
		{
			Res.Subst("ButtonName", curS.getValue("ButtonName","ApplyProfileChanges"));
			Res.Subst("ButtonValue", ButtonApplyProfileChanges);
			Res.Subst("PageCaption", "");
			TempStr += WebInclude(IncludeCaptionButton);
		}

		Tmp = CurS.getValue("ProfileName",PageCaption[0]);
		if (LadderRules.GetRemainingMatches() > 0 && Tmp == LadderRules.Profiles[GetCurrentConfigIndex()].ProfileName)
			Tmp = Tmp @ LabelNumberMatchesLeft @ string(LadderRules.GetRemainingMatches());

		Res.Subst("LadderTable", 	TempStr);
		Res.Subst("Section",	 	LinkEditProfile);
		Res.Subst("SubTitle", 		Tmp);
		Res.Subst("PageHelp", 		HelpEditProfile);
		Res.Subst("PostAction", 	ProfileEditURI$"?SessionID="$SID);
		ShowPage(Res, MasterPage);
	}

	else AccessDenied(Res);
}

function QueryLadderSwitch(WebRequest Req, WebResponse Res)
{
	local int 		j, NumMatches;
	local bool		bDelayChange, bSwitching;
	local Session 	CurS;
	local string 	SID,
					TempStr, Tmp,
					Checked;

	SID = Req.GetVariable("SessionID");
	CurS = SMaster.getSession(SID,true);

	if (CanPerform("Ls"))
	{
		TempStr = Req.GetVariable("ProfileNameSelect", "");
		Tmp = Req.GetVariable("SetNewProfile", "");
		if (Tmp != "")
		{
			if (TempStr != "")
			{
				NumMatches = int(Req.GetVariable("NumMatches", "0"));
				bDelayChange = Req.GetVariable("DelaySwitch","") != "";
				if (bDelayChange)
				{
					if (LadderRules.WaitApplyProfile(TempStr, NumMatches))
					{
						CurS.setValue("ProfileIndex", TempStr, True);
						bSwitching = True;
					}
				}
				else
				{
				// We would like to switch immediately
					LadderRules.ApplyProfile(TempStr, NumMatches);
					MapIsChanging();
					return;
				}
			}
			else StatusError(Res, ErrorNoProfilesExist);
		}

		else if (LadderRules.Profiles.Length > 0)
		{
			j = GetCurrentConfigIndex();
			if (j >= 0) TempStr = string(j);
		}

		bDelayChange = Req.GetVariable("DelaySwitch","") != "" || LadderRules.bWaitingToSwitch;
		if (bDelayChange)
			Checked = " checked";
		else Checked = "";

		Res.Subst("ProfileSelect", 		GenerateSwitchProfileOptions(TempStr));
		Res.Subst("NumberGamesText", 	LabelNumberGames);
		Res.Subst("TextName", 			"NumMatches");
		Res.Subst("TextValue", 			string(NumMatches));
		Res.Subst("TextSize", 			"3");
		Res.Subst("TextLength", 		"3");
		Res.Subst("SwitchButtonName", 	"SetNewProfile");
		Res.Subst("SwitchButtonValue", 	ButtonLoadProfile);
		Res.Subst("DelayCheckName", 	"DelaySwitch");
		Res.Subst("Checked", 			Checked);
		Res.Subst("DelayCheckLabel", 	LabelDelayProfileChange);

		if (Tmp != "" && bSwitching)
			StatusOK(Res, SuccessSwitchProfile);

		Tmp = CurS.getValue("ProfileName", PageCaption[0]);
		if (LadderRules.GetRemainingMatches() > 0 && Tmp == LadderRules.Profiles[GetCurrentConfigIndex()].ProfileName)
			Tmp = Tmp @ LabelNumberMatchesLeft @ string(LadderRules.GetRemainingMatches());

		Res.Subst("LadderTable", 	WebInclude(IncludeSwitch));
		Res.Subst("Section", 		LinkSwitchProfile);
		Res.Subst("SubTitle", 		Tmp);
		Res.Subst("PageHelp", 		HelpSwitchProfile);
		Res.Subst("PostAction", 	ProfileSwitchURI$"?SessionID="$SID);

		ShowPage(Res, MasterPage);
	}
	else AccessDenied(Res);
}

function QuerySubImport(WebRequest Req, WebResponse Res)
{
	local string 					SID;
	local Session 					CurS;
	local ProfileConfigSet 			TempPCS;

	SID = Req.GetVariable("SessionID");
	CurS = SMaster.getSession(SID,true);
	if (CanPerform("Lm"))
	{
	// We've gotten our profile - want to integrate it into the server now
		if (Req.GetVariable("ImportRemoteProfile","") != "")
		{
			TempPCS = CreateImportSessionInfo(CurS);
			if (TempPCS == None)
			{
				ShowMessage(Res, ImportProfileFailTitle, ImportProfileFailText);
				return;
			}
			ShowMessage(Res, SuccessImportProfileTitle, SuccessImportProfile);
			return;
		}
	}

	else AccessDenied(Res);
}

function RemoteImport(ProfileImporter ProfileConn)
{
	local int i,j;
	local Session 	CurS;
	local string 	Tmp, TempStr,
					ImportRows,
					PackageLink,
					NewGameType, CustomGameLocation,
					NewAccessType, CustomAccessLocation,
					ImportProfileHTML,
					MapLocation, MutatorLocation, MutatorName;
	local StringArray ImportMaps, ImportMutators;

	local WebRequest 		Req;
	local WebResponse 		Res;

	Req = ProfileConn.QueryRequest;
	Res = ProfileConn.QueryResponse;

	if (Res == None)
	{
		ShowMessage(Res, ErrorText, ErrorTimeout);
		return;
	}

	switch (ProfileConn.ImportResult)
	{
	case IMPORT_BadURL: 		ShowMessage(Res,ErrorText,ErrorBadURL); 		CleanupQuery();	return;
	case IMPORT_ResolveError: 	ShowMessage(Res,ErrorText,ErrorResolve); 		CleanupQuery();	return;
	case IMPORT_BindError:		ShowMessage(Res,ErrorText,ErrorBind); 			CleanupQuery();	return;
	case IMPORT_TimeOut:		ShowMessage(Res,ErrorText,ErrorTimeout);		CleanupQuery();	return;
	case IMPORT_ServerError:	ShowMessage(Res,ErrorText,ErrorServer); 		CleanupQuery();	return;
	case IMPORT_BadRequest:		ShowMessage(Res,ErrorText,ErrorBadRequest); 	CleanupQuery();	return;
	case IMPORT_Unauthorized:	ShowMessage(Res,ErrorText,ErrorUnauthorized);	CleanupQuery();	return;
	case IMPORT_Forbidden:		ShowMessage(Res,ErrorText,ErrorForbidden);		CleanupQuery();	return;
	case IMPORT_NotFound:		ShowMessage(Res,ErrorText,ErrorNotFound); 		CleanupQuery();	return;
	case IMPORT_InvalidProfile:	ShowMessage(Res,ErrorText,ErrorProfile);		CleanupQuery();	return;
	}

	CurS = ProfileConn.ImportS;
	ImportMaps = new(None) class'SortedStringArray';
	ImportMutators = new(None) class'SortedStringArray';

// General Profile Info
	Res.Subst("SectionName", LabelGeneralProfile);
	Res.Subst("SpecialInstructions", "");
	ImportRows += WebInclude(IncludeSection);

	Res.Subst("SettingName", LabelProfileName);
	Res.Subst("SettingData", CurS.getValue("ProfileName"));
	Res.Subst("Extra","");
	ImportRows += WebInclude(IncludeRows);

// Profile Gametype Class
	NewGameType = CurS.getValue("GameType");
	CustomGameLocation = CurS.getValue("CustomGameLoc");

	if (CustomGameLocation != "")
		PackageLink = "<a href=\""$CustomGameLocation$"\" target=\"_blank\">"$CurS.getValue("GameName")$"</a>";
	else PackageLink = CurS.getValue("GameName");

	Res.Subst("SettingName", LabelGameType);
	Res.Subst("SettingData", PackageLink);
	Res.Subst("Extra","");
	ImportRows += WebInclude(IncludeRows);

// Profile access control class
	NewAccessType = CurS.getValue("ACClass");
	CustomAccessLocation = CurS.getValue("CustomACLoc");

	if (CustomAccessLocation != "")
		PackageLink = "<a href=\""$CustomAccessLocation$"\" target=\"_blank\">"$NewAccessType$"</a>";
	else PackageLink = NewAccessType;

	Res.Subst("SettingName", LabelAccessControl);
	Res.Subst("SettingData", PackageLink);
	Res.Subst("Extra","");
	ImportRows += WebInclude(IncludeRows);

// Map Information

	Res.Subst("SectionName", TableTitle[3]);
	Res.Subst("SpecialInstructions", "");
	ImportRows += WebInclude(IncludeSection);

	for (i = 0; i < CurS.getMapLength(); i++)
		ImportMaps.Add(string(i), string(CurS.MapOrder(i)));

	for (i = 0; i < ImportMaps.Count(); i++)
	{
		TempStr = CurS.getMap( int(ImportMaps.getItem(i)) );
		MapLocation = ProfileConn.FindRemoteZipLocation(TempStr,ProfileConn.ProfileServerAddress $ ProfileConn.RemoteProfilePath);
		if (MapLocation != "") PackageLink = "<a href=\""$MapLocation$"\" target=\"_blank\">"$TempStr$"</a>";
		else PackageLink = TempStr;

		if (CurS.MapRequired( TempStr ))
			TempStr = "<font color=\"red\">"$RequiredText$"</font>";
		else TempStr = AvailableText;

		Res.Subst("SettingName", TempStr);
		Res.Subst("SettingData", PackageLink);
		Res.Subst("Extra", "");
		ImportRows = ImportRows $ WebInclude(IncludeRows);
	}

// Mutator Information
	Res.Subst("SectionName", TableTitle[2]);
	Res.Subst("SpecialInstructions", "");
	ImportRows += WebInclude(IncludeSection);

	for (i = 0; i < CurS.getMutatorLength(); i++)
		ImportMutators.Add(string(i), CurS.getMutator(i));

	for (i = 0; i < ImportMutators.Count(); i++)
	{
		TempStr = CurS.getMutator( int(ImportMutators.getItem(i)) );
		for (j=0;j<CurS.getDataLength();j++)
		{
			CurS.getIDValue(j,MutatorName,Tmp);
			if (Tmp ~= TempStr) break;
		}

		MutatorLocation = ProfileConn.FindRemoteZipLocation(TempStr,ProfileConn.ProfileServerAddress $ ProfileConn.RemoteProfilePath);
		PackageLink = StringIf(MutatorLocation != "", "<a href=\""$MutatorLocation$"\" target=\"_blank\">"$MutatorName$"</a>", MutatorName);
		TempStr = Stringif(CurS.MutRequired(TempStr), "<font color=\"red\">"$RequiredText$"</font>", AvailableText);

		Res.Subst("SettingName", TempStr);
		Res.Subst("SettingData", PackageLink);
		Res.Subst("Extra", "");
		ImportRows += WebInclude(IncludeRows);
	}

// PlayInfo Information
	Res.Subst("SectionName", TableTitle[4]);
	Res.Subst("SpecialInstructions", "");
	ImportRows += WebInclude(IncludeSection);
	ImportRows = ImportRows $"<tr><td colspan=\"2\">&nbsp;</td></tr>"$ GeneratePlayInfoRows(Req,Res,IncludeRows);

// Build Subst's
	Res.Subst("ButtonName", "ImportRemoteProfile");
	Res.Subst("ButtonValue", ButtonApplyImportProfile);
	Res.Subst("PageCaption", LabelApplyImportProfile);
	ImportProfileHTML = WebInclude(IncludeCaptionButton);

	Res.Subst("TableRows", ImportRows);
	Res.Subst("LadderTable", WebInclude(IncludeTable) $ ImportProfileHTML);

	Tmp = CurS.getValue("ProfileName", PageCaption[0]);
	if (LadderRules.GetRemainingMatches() > 0 && Tmp == LadderRules.Profiles[GetCurrentConfigIndex()].ProfileName)
		Tmp = Tmp @ LabelNumberMatchesLeft @ string(LadderRules.GetRemainingMatches());

	Res.Subst("Section", LinkImportProfile);
	Res.Subst("SubTitle", Tmp);
	Res.Subst("PageHelp", HelpImportProfile);
	Res.Subst("PostAction", SubImportURI$"?SessionID="$CurS.getHash());
	ShowPage(Res, MasterPage);
	CleanupQuery();
}

function QuerySubExport(WebRequest Req, WebResponse Res)
{
	local int i,j;
	local PlayInfo 	TempPI;
	local FileLog 	EProfile;
	local Session 	CurS;
	local string 	SID, Logname,
					TempStr, Tmp,
					dataname, datavalue,
					ProfileIndex, NewGameType,
					ExportRows, Location;

	local class<GameInfo> 		GIClass;
	local class<AccessControl> 	ACClass;
	local class<Mutator>		TempMut;
	local array<class<Mutator> > MClass;
	local class<Info>			TempSA;
	local array<class<Info> >	SAClass;
	local StringArray Mutators,GroupsOnly;


	if (CanPerform("Lm"))
	{
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.getSession(SID,true);
		ProfileIndex = Req.GetVariable("ProfileNameSelect", CurS.getValue("ProfileIndex"));

        CreateSessionInfo(ProfileIndex,SID,True);
		Logname = CurS.getValue("Logname",Req.GetVariable("TextInput"));
		if (Logname == "")
			Logname = CurS.getValue("ProfileName");

		CurS.setValue("Logname",Logname,True);
		for (i=0;i<CurS.getMutatorLength();i++)
		{
			TempStr = CurS.getMutator(i);
			if (EpicPackage(TempStr))
				continue;
			if (Req.GetVariable("Save"$TempStr,"") != "")
				SetLocation(TempStr,Req.GetVariable(TempStr));
		}

		for (i=0;i<CurS.getMapLength();i++)
		{
			TempStr = CurS.getMap(i);
			if (EpicPackage(TempStr))
				continue;
			if (Req.GetVariable("Save"$TempStr,"") != "")
				SetLocation(TempStr,Req.GetVariable(TempStr));
		}

		NewGameType = CurS.getValue("GameType");
		if (NewGameType != "")
			GIClass = class<GameInfo>(DynamicLoadObject(NewGameType,class'Class'));
		if (GIClass==None)
		{
			ShowMessage(Res,ErrorGameClassTitle,ErrorGameClassText);
			return;
		}

		TempStr = CurS.getValue("ACClass", Level.Game.AccessControlClass);
		if (TempStr != "")
			ACClass = class<AccessControl>(DynamicLoadObject(TempStr,class'Class'));
		if (ACClass == None)
		{
			ShowMessage(Res,ErrorAccessControlTitle,ErrorAccessControlText);
			return;
		}

		if (Req.GetVariable("Export","")!="")
		{
		// Write file locations to .ini
			EProfile = Level.Spawn(class'FileLog');
			if (EProfile == None)
			{
				ShowMessage(Res, ErrorText, ErrorExportingProfile);
				return;
			}

			EProfile.OpenLog(RemoveStr(CurS.getValue("Logname")," "));
			EProfile.Logf("Profile:"$CurS.getValue("ProfileName")$Chr(21)$CurS.getValue("GameType")$Chr(21)$CurS.getValue("MaxMaps")$Chr(21)$GIClass.default.GameName$Chr(21)$string(ACClass));

			for (i=0;i<CurS.getMapLength();i++)
			{
				TempStr = CurS.getMap(i);

				if (Req.GetVariable(TempStr,"") != "")
					Location = Chr(21) $ Req.GetVariable(TempStr);
				else Location = "";

				EProfile.Logf("Map:"$TempStr$Chr(21)$CurS.MapOrder(i)$Chr(21)$CurS.MapRequired(TempStr)$Location);
			}

			for (i=0;i<CurS.getSALength();i++)
			{
				TempStr = CurS.getServerActor(i);
				for (j=0;j<ConfigM.ManagedActors.Length;j++)
					if (string(ConfigM.ManagedActors[j].SAClass) ~= TempStr)
						DataName = ConfigM.ManagedActors[j].SAName;

				if (Req.GetVariable(TempStr,"") != "")
					Location = Chr(21) $ Req.GetVariable(TempStr);
				else Location = "";

				EProfile.Logf("Mutator:"$DataName$Chr(21)$TempStr$Chr(21)$CurS.SARequired(TempStr)$Location);
			}

			for (i=0;i<CurS.getMutatorLength();i++)
			{
				TempStr = CurS.getMutator(i);
				for (j=0;j<AllMutators.Length;j++)
					if (AllMutators[j].ClassName == TempStr)
						DataName = AllMutators[j].FriendlyName;

				if (Req.GetVariable(TempStr,"") != "")
					Location = Chr(21) $ Req.GetVariable(TempStr);
				else Location = "";

				EProfile.Logf("Mutator:"$DataName$Chr(21)$TempStr$Chr(21)$CurS.MutRequired(TempStr)$Location);
			}

			i=0;
			j=0;
			while (i<CurS.getDataLength())
			{
				CurS.getIdValue(i,dataname,datavalue);
				if (InStr(dataname,".") != -1)
					EProfile.Logf("Data:"$DataName$Chr(21)$DataValue);
				i++;
			}
			EProfile.Destroy();
			ShowMessage(Res, SuccessText, SuccessExportProfile$RemoveStr(LogName," ")$".txt");
			return;
		}


	// Mutator Section
		Mutators = new(None) class'SortedStringArray';
		GroupsOnly = new(None) class'SortedStringArray';
		for (i=0;i<CurS.getMutatorLength();i++)
		{
		    TempStr = CurS.getMutator(i);
            for (j=0;j<AllMutators.Length;j++)
				if (TempStr ~= AllMutators[j].ClassName)
					Mutators.Add(string(j), AllMutators[j].FriendlyName);
			TempMut = class<Mutator>(DynamicLoadObject(TempStr,class'Class'));
			if (TempMut != None)
				MClass[MClass.Length] = TempMut;
		}

		Res.Subst("SectionName", TableTitle[2]);
		Res.Subst("SpecialInstructions", "");
		ExportRows += WebInclude(IncludeSection);

		for (i = 0; i<Mutators.Count(); i++)
		{
			j = int(Mutators.GetItem(i));

			Res.Subst("SettingName", AllMutators[j].FriendlyName);
			if (EpicPackage(AllMutators[j].ClassName))
				Res.Subst("SettingData", "");
			else Res.Subst("SettingData", Textbox(AllMutators[j].ClassName,Min(Max(GetLongestURL(),16),64),128,GetLocation(AllMutators[j].ClassName)) $ "&nbsp;&nbsp;" $ SubmitButton("Save"$AllMutators[j].ClassName,ProfileButtonName[0]));

			Res.Subst("Extra", "");
			ExportRows += WebInclude(IncludeRows);
		}

		for (i=0;i<CurS.getSALength();i++)
		{
			TempStr = CurS.getServerActor(i);
			if (TempStr != "")
			{
				TempSA = class<Info>(DynamicLoadObject(TempStr,class'class'));
				if (TempSA != None)
					SAClass[SAClass.Length] = TempSA;
			}
		}

		TempPI = new(None) class'PlayInfo';
		LoadThisPlayInfo(TempPI, GIClass, ACClass, MClass, SAClass);

		Res.Subst("SectionName", TableTitle[3]);
		Res.Subst("SpecialInstructions", "");
		ExportRows += WebInclude(IncludeSection);

		Res.Subst("SettingName", LabelMaxMaps);
		TempStr = CurS.getValue("MaxMaps");
		if (TempStr == "0")
			TempStr = UnlimitedText;
		Res.Subst("SettingData", TempStr);
		Res.Subst("Extra", "<tr><td colspan=\"2\">&nbsp;&nbsp;</td></tr>");
		ExportRows += WebInclude(IncludeRows);

// Map Section
		for (i=0;i<CurS.getMapLength();i++)
		{
            TempStr = CurS.getMap(i);
            Res.Subst("SettingName", TempStr);
			Res.Subst("Extra", "");
			if (EpicPackage(TempStr))
				Res.Subst("SettingData", "");
			else Res.Subst("SettingData", Textbox(TempStr,Min(Max(GetLongestURL(),16),64),128,GetLocation(TempStr)) $ "&nbsp;&nbsp;" $ SubmitButton("Save"$TempStr,ProfileButtonName[0]));

			ExportRows += WebInclude(IncludeRows);
		}

// PlayInfo section
		Res.Subst("SectionName", TableTitle[4]);
		Res.Subst("SpecialInstructions", "&nbsp;&nbsp;");
		ExportRows += WebInclude(IncludeSection);
		ExportRows += ("<tr><td colspan=\"2\">&nbsp;</td></tr>" $ GeneratePlayInfoRows(Req, Res, IncludeRows));
		ExportRows += ("<tr><td colspan=\"2\">&nbsp;</td></tr><tr><td colspan=\"2\">"$LabelApplyExportProfile$"</td></tr><tr><td colspan=\"2\">"$SubmitButton("Export",ButtonApplyExportProfile)$"</td></tr>");

		Res.Subst("TableRows", ExportRows);
		Res.Subst("LadderTable", WebInclude(IncludeTable));

		Tmp = CurS.getValue("ProfileName", PageCaption[0]);
		if (LadderRules.GetRemainingMatches() > 0 && Tmp == LadderRules.Profiles[GetCurrentConfigIndex()].ProfileName)
			Tmp = Tmp @ LabelNumberMatchesLeft @ string(LadderRules.GetRemainingMatches());

		Res.Subst("Section", LinkExportProfile);
		Res.Subst("PageHelp", HelpExportProfile);
		Res.Subst("SubTitle", Tmp);
		Res.Subst("PostAction", SubExportURI$"?SessionID="$SID);
		ShowPage(Res, MasterPage);
	}

	else AccessDenied(Res);
}

//****************************************************************
//****************************************************************
// Query subpages for Add & Manage Modules
//
//

// Page 0
function QueryLadderManage(WebRequest Req, WebResponse Res)
{
	local string 	HeaderRows, ManagementRows,
					TempStr, Tmp, SID;
	local bool		bAdd, bModify, bCopy, bDelete, bImport, bExport;
	local Session 	CurS;
	local ProfileImporter ProfileConn;

	if (CanPerform("Lm"))
	{
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.getSession(SID, True);

		// Submit button was pressed
		if (Req.GetVariable(ProfileButtonName[0], "") != "")
		{
			switch (Req.GetVariable("Actions",""))
			{
				case "Add":
					bAdd = True;
					QuerySubFrame(Req, Res);
					break;

				case "Modify":
					bModify = True;
					TempStr = Req.GetVariable("ProfileNameSelect");
					if (TempStr == "")
					{
						StatusError(Res, ErrorNoProfilesExist);
						break;
					}
					QuerySubFrame(Req, Res);
					return;

				case "Copy":
					bCopy = True;
					Tmp = Req.GetVariable("ProfileNameSelect");
					if (Tmp == "")
						StatusError(Res, ErrorNoProfilesExist);

					TempStr = Req.GetVariable("TextInput");
					if (TempStr == "")
					{
						StatusError(Res, ErrorCopyNoName);
						break;
					}

					if (LadderRules.CopyProfile(int(Tmp),TempStr))
						StatusOK(Res,SuccessCopyProfile);
					else StatusError(Res, ErrorCopyFrom);
					break;

				case "Delete":
					bDelete = True;
					TempStr = Req.GetVariable("ProfileNameSelect");
					if (TempStr != "")
					{
						if (LadderRules.RemoveProfile(int(TempStr)))
							StatusOK(Res, SuccessDeleteProfile);
						else StatusError(Res, ErrorDeletingProfile);
					}
					else StatusError(Res, ErrorNoProfilesExist);
					break;

				case "Import":
					bImport = True;
					TempStr = Req.GetVariable("TextInput","");
					if (!(Left(TempStr,7) ~= "http://"))
					{
						StatusError(Res, ErrorBadURL);
						break;
					}

				// Spawn class to handle network interaction to receive remote profile
					ProfileConn = Level.Spawn(Class'ProfileImporter');
					If (ProfileConn == None)
					{
						ShowMessage(Res, ErrorText, ImportClassSpawnError);
						return;
					}

				// Assign local connection objects to importer class
					ProfileConn.QueryPage = Self;
					ProfileConn.QueryRequest = Req;
					ProfileConn.QueryResponse = Res;

				// Setting bDelayCleanup prevents the connection from being closed
				// when QueryProfileImport() returns to Query()
				// Set this prior to initializing connection, to ensure correct behavior
					Res.Connection.bDelayCleanup = True;

				// Initialize connection to foreign host, pass Importer class a Session
				// object to allow it to build a profile config set.
					ProfileConn.Connect(TempStr, CurS);
					return;

				case "Export":
					bExport = True;
					TempStr = Req.GetVariable("ProfileNameSelect", "");
					if (TempStr == "")
					{
						StatusError(Res, ErrorNoProfilesExist);
						break;
					}
					QuerySubExport(Req,Res);
					return;

				default:
					bModify = True;
			}
		}

	// Output code -
	// TODO: move to .inc file
		HeaderRows = "<tr><td nowrap>"$LabelChooseAction$"</td><td nowrap>"$LabelInputDescription$"</td></tr>";
	    ManagementRows += ("<tr><td nowrap>"$RadioButton("Actions", "Add", bAdd)$LabelAddProfile$"<br>");
		ManagementRows += (RadioButton("Actions", "Modify", bModify)$LabelModifyProfile$"</td>");
		ManagementRows += ("<td><select class=mini name='ProfileNameSelect'>"$GenerateSwitchProfileOptions(TempStr)$"</select>&nbsp;"$LabelSelectActions$"</td></tr>");
		ManagementRows += ("<tr><td nowrap>"$RadioButton("Actions", "Copy", bCopy)$LabelCopyProfile$"<br>");
		ManagementRows += (RadioButton("Actions", "Delete", bDelete)$LabelDeleteProfile$"</td>");
		ManagementRows += ("<td nowrap>"$Textbox("TextInput",64,128)$"&nbsp;"$LabelTextboxActions$"</td></tr>");
		ManagementRows += ("<tr><td nowrap>"$RadioButton("Actions", "Import", bImport)$LabelImportProfile$"<br>");
		ManagementRows += (RadioButton("Actions", "Export", bExport)$LabelExportProfile$"</td>");
		ManagementRows += ("<td>"$SubmitButton(ProfileButtonName[0],ProfileButtonValue[0])$"</td></tr>");

		Res.Subst("TableTitle", TableTitle[0]);
		Res.Subst("HeaderRow", HeaderRows);
		Res.Subst("TableRows",ManagementRows);

		Tmp = CurS.getValue("ProfileName", PageCaption[0]);
		if (LadderRules.GetRemainingMatches() > 0 && Tmp == LadderRules.Profiles[GetCurrentConfigIndex()].ProfileName)
			Tmp @= (LabelNumberMatchesLeft @ string(LadderRules.GetRemainingMatches()));

		Res.Subst("Section", LabelManagement);
		Res.Subst("PageHelp", HelpManageProfile);
		Res.Subst("SubTitle", Tmp);
		Res.Subst("LadderTable", WebInclude(IncludeTable));
		Res.Subst("PostAction", ProfileManageURI);
		ShowPage(Res, MasterPage);
	}

	else AccessDenied(Res);
}

// Page 1
function QuerySubGameType(WebRequest Req, WebResponse Res)
{
	local int i;
	local string DataName, DataValue, SID, NewName, OldName;
	local string GameTypeRows, OldGameType, CommandLineRows;
	local string GameTypeTable, CommandLineTable, ApplyTable;
	local Session CurS;
	local LadderProfiles LadderMaster;


	if (CanPerform("Lm"))
	{
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.getSession(SID, true);

        if (Req.GetVariable(CurS.getValue("ButtonName",""),"")!="")
    	{
    		NewName = Req.GetVariable("NewProfile",DefaultProfileName);
    		OldName = CurS.getValue("ProfileName");
    		if ((OldName != "" && !(OldName ~= NewName)) || OldName == "")
    		{
	    		for (i=0;i<LadderRules.Profiles.Length;i++)
	    		{
	    			if (NewName ~= LadderRules.Profiles[i].ProfileName)
	    			{
	    				StatusError(Res, ErrorSameProfileName);
	    				NewName = DefaultProfileName;
	    				break;
	    			}
	    		}
	    	}
			CurS.setValue("ProfileName", NewName, true);
			CurS.setValue("GameType", Req.GetVariable("NewGameType"), True, OldGameType);
			if (!(OldGameType ~= CurS.getValue("GameType")))
			{
				CurS.ClearMaps();
			// Reset all stored PlayInfo information
				for (i=0;i<CurS.getDataLength();i++)
				{
					CurS.getIDValue(i,DataName,DataValue);
					if (InStr(DataName, ".") != -1)
					{
						CurS.delValue(DataName);
						i--;
					}
				}
			}
		}

		Res.Subst("NewProfileName", CurS.getValue("ProfileName", DefaultProfileName));
		GameTypeRows = WebInclude(IncludeProfileName);

		for (i=0;i<AGameType.Count();i++)
		{
			Res.Subst("GameTypeClass", 	AGameType.GetItem(i));
			Res.Subst("GameTypeName", 	AGameType.GetTag(i));
			Res.Subst("Selected", 		StringIf(CurS.getValue("GameType") ~= AGameType.GetItem(i), " checked", ""));
			GameTypeRows += WebInclude(IncludeGameType);
		}

	// Fill in caption table values
		Res.Subst("PageCaption", 	CurS.getValue("PageCaption", PageCaption[1]));
		Res.Subst("ButtonName", 	CurS.getValue("ButtonName", ProfileButtonName[1]));
		Res.Subst("ButtonValue", 	CurS.getValue("ButtonValue", ProfileButtonValue[1]));

  		Res.Subst("TableRows", 		GameTypeRows);
		Res.Subst("LadderTable", 	WebInclude(IncludeTable)$WebInclude(IncludeCaptionButton));
		Res.Subst("PostAction", 	CurS.GetValue("CurrentPage"));
		ShowPage(Res, SubFrameContent);
	}

	else AccessDenied(Res);
}

// Page 2
function QuerySubMutators(WebRequest Req, WebResponse Res)
{
	local int i,j;
	local bool 		bAllMuts,
					bClearMuts,
					bChecked;
	local string 	MutatorRows,
					SID, TempStr;

	local Session CurS;
	local StringArray Mutators, ServerActors;
	local class<Mutator> MutClass;


	if (CanPerform("Lm"))
	{
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.getSession(SID, true);

		if (Req.GetVariable(CurS.getValue("ButtonName",""),"")!="")
		{
			bAllMuts = Req.GetVariable("AllowAllMutators")!="";
			bClearMuts = Req.GetVariable("ClearAllMutators")!="";
//			CurS.ClearMutators();

			for (i=0;i<ConfigM.ManagedActors.Length;i++)
			{
				TempStr = string(ConfigM.ManagedActors[i].SAClass);
				if (bAllMuts || (!bClearMuts && Req.GetVariable(TempStr,"") != ""))
					CurS.setServerActor(TempStr, True, CurS.SARequired(TempStr));
				else CurS.delServerActor(TempStr);
			}

			for (i=0;i<AllMutators.Length;i++)
			{
				if (bAllMuts || (!bClearMuts && (Req.GetVariable(AllMutators[i].ClassName,"")!="" || Req.GetVariable(AllMutators[i].GroupName)~=AllMutators[i].ClassName)))
					CurS.setMutator(AllMutators[i].ClassName,true,CurS.MutRequired(AllMutators[i].ClassName));
				else if (Req.GetVariable(AllMutators[i].GroupName) ~= "None")
					CurS.setMutator(AllMutators[i].GroupName,true);
				else
				{
					CurS.delMutator(AllMutators[i].ClassName);
					CurS.delMutator(AllMutators[i].GroupName);
				}
			}
		}

		Mutators = new(None) class'SortedStringArray';
		ServerActors = new(None) class'SortedStringArray';
//		GroupsOnly = new(None) class'SortedStringArray';
		for (i = 0; i<AllMutators.Length; i++)
		{
			MutClass = class<Mutator>(DynamicLoadObject(AllMutators[i].ClassName, class'Class'));
			if (MutClass != None)
				Mutators.Add(string(i),AllMutators[i].FriendlyName);
		}

		for (i=0;i<ConfigM.ManagedActors.Length;i++)
			if (ConfigM.ManagedActors[i].SAClass != None)
				ServerActors.Add(string(i),ConfigM.ManagedActors[i].SAName);

// Uncomment this to allow mutators to be grouped according to MutatorGroup
/*
		CreateFullMutatorList(Mutators,GroupsOnly);

		for (i=0;i<CurS.GetMutatorLength();i++)
		{
			k = 0-1;
			for (j=0;j<AllMutators.Length;j++)
				if (CurS.GetMutator(i) ~= AllMutators[j].ClassName)
					break;
			if (j<AllMutators.Length) k = GroupsOnly.FindItemId(string(j));
			if (k >= 0)
			{
				for (z=0;z<TempA.Length;z++)
				{
					if (TempA[z] ~= AllMutators[j].GroupName)
					{
						bAllMuts = True;
						break;
					}
				}

				if (!bAllMuts) TempA[TempA.Length] = AllMutators[j].GroupName;
			}
			if (bAllMuts)
       			break;
		}

		lastgroup = "";
		thisgroup = "";
		nextgroup = "";
		MutatorRows="";

		for (i=0;i<GroupsOnly.Count();i++)
		{
			j=int(GroupsOnly.GetItem(i));
			if (AllMutators[j].GroupName=="")
				thisgroup="Z"$string(z++);
			else thisgroup = AllMutators[j].GroupName;
			if ( (i + 1) == GroupsOnly.Count())
				nextgroup = "";
			else
			{
			// Assign nextgroup to the next mutators group
				k = int(GroupsOnly.GetItem(i + 1));
				if (AllMutators[k].GroupName=="")
					nextgroup="Z"$string(z);
				else nextgroup = AllMutators[k].GroupName;
			}

			Res.Subst("GroupName", thisgroup);
			Res.Subst("MutatorClass", AllMutators[j].ClassName);
			Res.Subst("MutatorName", AllMutators[j].FriendlyName);
			Res.Subst("MutatorDesc", AllMutators[j].Description);

			if ((lastgroup != thisgroup) && (thisgroup == nextgroup)) // and the next mut is in the same group as this one
			{
				if (bAllMuts||CurS.HasMutator(thisgroup)) Res.Subst("Checked", "checked");
				MutatorRows = MutatorRows$WebInclude("current_mutators_group");
			}
			else
			{
				if (CurS.HasMutator(AllMutators[j].ClassName) && !bAllMuts) Res.Subst("Checked", "checked"); else Res.Subst("Checked", "");
				MutatorRows = MutatorRows$WebInclude("current_mutators_group_row");
			}

			lastgroup=thisgroup;
		}
*/
		bAllMuts = True;
		for (i = 0; i<ServerActors.Count(); i++)
		{
			TempStr = ""; bChecked = False;
			j = int(ServerActors.GetItem(i));

			if (CurS.HasServerActor(string(ConfigM.ManagedActors[j].SAClass)))
				bChecked = True;
			else if (bAllMuts) bAllMuts = False;

			Res.Subst("Content", Checkbox(string(ConfigM.ManagedActors[j].SAClass),bChecked) $ "&nbsp;" $ ServerActors.GetTag(i));
			TempStr = WebInclude(NowrapLeft);
			Res.Subst("Content", ConfigM.ManagedActors[j].SADescription);
			Res.Subst("RowContent", TempStr $ WebInclude(CellLeft));

			MutatorRows += WebInclude(RowLeft);
		}

		for (i = 0; i<Mutators.Count(); i++)
		{
			TempStr = ""; bChecked = False;
			j = int(Mutators.GetItem(i));

			if (CurS.HasMutator(AllMutators[j].ClassName))
				bChecked = True;
			else if (bAllMuts) bAllMuts = False;

			Res.Subst("Content", CheckBox(AllMutators[j].ClassName, bChecked) $ "&nbsp;" $ AllMutators[j].FriendlyName);
			TempStr = WebInclude(NowrapLeft);
			Res.Subst("Content", AllMutators[j].Description);
			Res.Subst("RowContent", TempStr $ WebInclude(CellLeft));

			MutatorRows += WebInclude(RowLeft);
		}

		if (bAllMuts)
			Res.Subst("Content", Checkbox("ClearAllMutators", False)@LabelUncheckAllMutator);
		else
			Res.Subst("Content", Checkbox("AllowAllMutators", False)@LabelCheckAllMutator);
		Res.Subst("SpanLength", "2");
		Res.Subst("SpanContent", "&nbsp;&nbsp;");
		Res.Subst("RowContent", WebInclude(CellColSpan));
		MutatorRows += WebInclude(RowLeft);

		Res.Subst("RowContent", WebInclude(CellLeft));
		MutatorRows += WebInclude(RowLeft);

		Res.Subst("TableRows", MutatorRows);
	// Fill in caption table values
		Res.Subst("PageCaption", 	CurS.getValue("PageCaption", PageCaption[2]));
		Res.Subst("ButtonName", 	CurS.getValue("ButtonName", ProfileButtonName[2]));
		Res.Subst("ButtonValue", 	CurS.getValue("ButtonValue", ProfileButtonValue[2]));

		Res.Subst("LadderTable", 	WebInclude(IncludeTable)$WebInclude(IncludeCaptionButton));
		Res.Subst("PostAction", 	CurS.GetValue("CurrentPage"));
		ShowPage(Res, SubFrameContent);
	}

	else AccessDenied(Res);
}

// Page3
function QuerySubMaps(WebRequest Req, WebResponse Res)
{
	local int i;
	local string 	SID,
					MapRows,
					TempStr, Temp,
					CheckString;
	local bool 		bAllMaps,
					bClearMaps,
					bChecked;

	local Session CurS;
	local StringArray SortedMaps;
	local array<string> TempMaps;
	local class<GameInfo> GIClass;


	if (CanPerform("Lm"))
	{
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.getSession(SID);

		TempStr = CurS.getValue("GameType");
		if (TempStr != "")
			GIClass = class<GameInfo>(DynamicLoadObject(TempStr,class'Class'));
		if (GIClass == None)
		{
			ShowFramedMessage(Res,ErrorGameClassText,True);
			return;
		}
		// Build a string array of maps valid for this gametype.
		GIClass.static.LoadMapList(GIClass.default.MapPrefix, TempMaps);

		if (Req.GetVariable(CurS.getValue("ButtonName",""),"")!="")
		{
			bAllMaps = Req.GetVariable("AllowAllMaps","")!="";
			bClearMaps = Req.GetVariable("ClearAllMaps","")!="";

			for (i=0;i<TempMaps.Length;i++)
				if (bAllMaps || (!bClearMaps && Req.GetVariable(TempMaps[i])!=""))
					CurS.setMap(TempMaps[i],0,True,CurS.MapRequired(TempMaps[i]));
				else CurS.delMap(TempMaps[i]);
		}
		// Add the map name array to a sorted string array for sorting
		SortedMaps = new(None) class'SortedStringArray';
		for(i=0;i<TempMaps.Length;i++)
			SortedMaps.Add(string(i),TempMaps[i]);

		bAllMaps = True;
		// Now build the map rows
		for (i=0;i<SortedMaps.Count();i++)
		{
			Temp = ""; bChecked = False;
			TempStr = SortedMaps.GetTag(i);
			if (CurS.HasMap(TempStr))
				bChecked = True;
			else if (bAllMaps) bAllMaps = False;

			Res.Subst("Content", Checkbox(TempStr, bChecked) $ "&nbsp;" $ TempStr);
			Res.Subst("RowContent", WebInclude(CellLeft));
		// Build string for javascript function
			if (CheckString!="")
				CheckString += "','";
			CheckString += TempStr;

			MapRows += WebInclude(RowLeft);
		}

	// Fill Javascript substs
		Res.Subst("JavaScripts","<script language=\"JavaScript\" type=\"text/JavaScript\">function MM_findObj(n,d){var p,i,x;if(!d)d=document;if((p=n.indexOf(\"?\"))>0&&parent.frames.length){d=parent.frames[n.substring(p+1)].document;n=n.substring(0,p);}if(!(x=d[n])&&d.all)x=d.all[n];for(i=0;!x&&i<d.forms.length;i++)x=d.forms[i][n];for(i=0;!x&&d.layers&&i<d.layers.length;i++)x=MM_findObj(n,d.layers[i].document);if(!x&&d.getElementById)x=d.getElementById(n);return x;}function CheckMaps(){var maps=CheckMaps.arguments;var MapChecked=false;for(var i=0;i<maps.length;i++){var myObj=MM_findObj(maps[i]);if(myObj.checked==true){MapChecked=true}}if(MapChecked==false){alert('You must check at least one map to continue!')}document.MM_returnValue=(MapChecked);}</script>");
		Res.Subst("JCheck"," onClick=\"CheckMaps('AllowAllMaps','"$CheckString$"');return document.MM_returnValue\"");
		Res.Subst("Checked","");

		if (bAllMaps)
			Res.Subst("Content", Checkbox("ClearAllMaps", False) @ LabelUncheckAllMap);
		else Res.Subst("Content", Checkbox("AllowAllMaps", False) @ LabelCheckAllMap);

		Res.Subst("SpanLength", "2");
		Res.Subst("SpanContent", "&nbsp;&nbsp;");
		Res.Subst("RowContent", WebInclude(CellColSpan));
		MapRows += WebInclude(RowLeft);

		Res.Subst("RowContent", WebInclude(CellLeft));
		MapRows += WebInclude(RowLeft);

		// Set ladder_caption_table subst's
		Res.Subst("PageCaption", 	CurS.getValue("PageCaption",PageCaption[3]));
		Res.Subst("ButtonName", 	CurS.getValue("ButtonName", ProfileButtonName[3]));
		Res.Subst("ButtonValue", 	CurS.getValue("ButtonValue", ProfileButtonValue[3]));

		Res.Subst("TableRows", MapRows);

		Res.Subst("LadderTable", 	WebInclude(IncludeTable)$WebInclude(IncludeCaptionButton));
		Res.Subst("PostAction", 	CurS.getValue("CurrentPage"));
		ShowPage(Res, SubFrameContent);
	}

	else AccessDenied(Res);
}

// Page 4
function QuerySubRules(WebRequest Req, WebResponse Res)
{
	local int i;
	local string 	TempStr,
					SID;
	local Session 	CurS;
	local PlayInfo 	TempPI;
	local class<Mutator> 		TempClass;
	local class<GameInfo> 		GIClass;
	local class<AccessControl> 	ACClass;
	local array<class<Mutator> > MClass;
	local class<Info>			TempSA;
	local array<class<Info> >	SAClass;

	if (CanPerform("Lm"))
	{
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.GetSession(SID);

		TempStr = CurS.getValue("GameType");
		if (TempStr != "")
			GIClass = class<GameInfo>(DynamicLoadObject(TempStr,class'Class'));
		if (GIClass == None)
		{
			ShowFramedMessage(Res,ErrorGameClassText,True);
			return;
		}

		TempStr = CurS.getValue("ACClass", Level.Game.AccessControlClass);
		if (TempStr != "")
			ACClass = class<AccessControl>(DynamicLoadObject(TempStr,class'Class'));
		if (ACClass == None)
		{
			ShowFramedMessage(Res,ErrorAccessControlText,True);
			return;
		}

		if (Req.GetVariable(CurS.getValue("ButtonName",""),"")!="")
		{
			for (i=0;i<CurS.GetMutatorLength();i++)
			{
				TempStr = CurS.getMutator(i);
				if (TempStr != "")
				{
					TempClass = class<Mutator>(DynamicLoadObject(TempStr,class'Class'));
					if (TempClass != None)
						MClass[MClass.Length] = TempClass;
				}
			}

			TempPI = new(None) class'PlayInfo';
			for (i=0;i<CurS.getSALength(); i++)
			{
				TempStr = CurS.getServerActor(i);
				if (TempStr != "")
				{
					TempSA = class<Info>(DynamicLoadObject(TempStr,class'Class'));
					if (TempSA != None)
						SAClass[SAClass.Length] = TempSA;
				}
			}

			LoadThisPlayInfo(TempPI, GIClass, ACClass, MClass, SAClass);
			for (i=0;i<TempPI.Settings.Length;i++)
			{
				if (!SettingIsAllowed(TempPI.Settings[i].SecLevel)) continue;
				TempStr = Req.GetVariable(TempPI.Settings[i].SettingName,TempPI.Settings[i].Value);

				if (TempPI.Settings[i].RenderType == "Text")
					CheckSettingValue(TempPI.Settings[i].Data, TempStr);

				CurS.setValue(TempPI.Settings[i].SettingName,TempStr,true);
			}
		}

		Res.Subst("TableRows",		GeneratePlayInfoRows(Req, Res, "defaults_row"));

		// Set ladder_caption_table subst's
		Res.Subst("PageCaption", 	CurS.getValue("PageCaption",PageCaption[4]));
		Res.Subst("ButtonName", 	CurS.getValue("ButtonName", ProfileButtonName[4]));
		Res.Subst("ButtonValue", 	CurS.getValue("ButtonValue", ProfileButtonValue[4]));

		Res.Subst("LadderTable", 	WebInclude(IncludeTable)$WebInclude(IncludeCaptionButton));
		Res.Subst("PostAction", 	CurS.getValue("CurrentPage"));
		ShowPage(Res, SubFrameContent);
	}

	else AccessDenied(Res);
}

// Page 5
function QuerySubReview(WebRequest Req, WebResponse Res)
{
	local int i,j,k,z;
	local string 	SID,
					TempStr, TempSA,
					ReviewRows,
					thisgroup, lastgroup, nextgroup;

	local Session CurS;
	local StringArray Mutators, GroupsOnly;
	local class<Mutator> 		TempClass;
	local class<Info>			TempSAClass;
	local class<GameInfo> 		GIClass;
	local class<AccessControl> 	ACClass;
	local array<class<Mutator> > MClass;

	if (CanPerform("Lm"))
	{
		SID = Req.GetVariable("SessionID");
		CurS = SMaster.getSession(SID);

		TempStr = CurS.getValue("GameType");
		if (TempStr != "")
			GIClass = class<GameInfo>(DynamicLoadObject(TempStr,class'Class'));
		if (GIClass == None)
		{
			ShowFramedMessage(Res,ErrorGameClassText,True);
			return;
		}

		TempStr = CurS.getValue("ACClass",Level.Game.AccessControlClass);
		if (TempStr != "")
			ACClass = class<AccessControl>(DynamicLoadObject(TempStr,class'Class'));
		if (ACClass == None)
		{
			ShowFramedMessage(Res,ErrorAccessControlText,True);
			return;
		}

		Mutators = new(None) class'SortedStringArray';
		GroupsOnly = new(None) class'SortedStringArray';
		for (i = 0; i<AllMutators.Length; i++)
		{
			if (CurS.HasMutator(AllMutators[i].ClassName)||CurS.HasMutator(AllMutators[i].GroupName))
			{
				TempClass = class<Mutator>(DynamicLoadObject(AllMutators[i].ClassName, class'Class'));
				if (TempClass != None)
				{
					MClass[MClass.Length] = TempClass;
					Mutators.Add(string(i),AllMutators[i].FriendlyName);
				}
			}
		}

		CreateFullMutatorList(Mutators,GroupsOnly);
		lastgroup = ""; thisgroup = ""; nextgroup = "";

		Res.Subst("SectionName", TableTitle[2]);
		Res.Subst("SpecialInstructions", LabelRequiredMutators);
		ReviewRows += WebInclude(IncludeSection);

		for (i=0;i<GroupsOnly.Count();i++)
		{
			j=int(GroupsOnly.GetItem(i));

			if (AllMutators[j].GroupName=="")
				thisgroup="Z"$string(z++);
			else thisgroup = AllMutators[j].GroupName;

			if ( (i + 1) == GroupsOnly.Count())
				nextgroup = "";
			else
			{
			// Assign nextgroup to the next mutators group
				k = int(GroupsOnly.GetItem(i + 1));
				if (AllMutators[k].GroupName=="")
					nextgroup="Z"$string(z);
				else nextgroup = AllMutators[k].GroupName;
			}

			Res.Subst("GroupName", thisgroup);
			Res.Subst("MutatorClass", AllMutators[j].ClassName);
			Res.Subst("MutatorName", AllMutators[j].FriendlyName);
			Res.Subst("MutatorDesc", AllMutators[j].Description);

			if ((lastgroup != thisgroup) && (thisgroup == nextgroup)) // and the next mut is in the same group as this one
			{
                Res.Subst("Checked", " checked");
				ReviewRows += WebInclude("current_mutators_group");
			}

			Res.Subst("Checked", StringIf(CurS.MutRequired(AllMutators[j].ClassName), " checked", ""));
			ReviewRows += WebInclude("current_mutators_group_row");

			lastgroup=thisgroup;
		}

		for (i = 0; i<Mutators.Count(); i++)
		{
			TempStr = "";
			j = int(Mutators.GetItem(i));

			Res.Subst("Content", CheckBox(AllMutators[j].ClassName, CurS.MutRequired(AllMutators[j].ClassName)) $ "&nbsp;" $ AllMutators[j].FriendlyName);
			TempStr = WebInclude(NowrapLeft);
			Res.Subst("Content", AllMutators[j].Description);
			Res.Subst("RowContent", TempStr $ WebInclude(CellLeft));

			ReviewRows += WebInclude(RowLeft);
		}

		Mutators.Reset();
		for (i = 0; i < ConfigM.ManagedActors.Length; i++)
		{
			TempSA = string(ConfigM.ManagedActors[i].SAClass);
			if (CurS.HasServerActor(TempSA))
			{
				TempSAClass = class<Info>(DynamicLoadObject(TempSA,class'Class'));
				if (TempSAClass != None)
					Mutators.Add(string(i),ConfigM.ManagedActors[i].SAName);
			}
		}
		lastgroup = "";
		Res.Subst("SpanLength", "2");
		Res.Subst("SpanContent", "&nbsp;&nbsp;");
		Res.Subst("RowContent", WebInclude(CellColSpan));
		lastgroup = WebInclude(RowLeft);
		ReviewRows += lastgroup;

		for (i=0;i<Mutators.Count();i++)
		{
			TempStr = "";
			j = int(Mutators.GetItem(i));

			Res.Subst("Content", Checkbox(string(ConfigM.ManagedActors[j].SAClass), CurS.SARequired(string(ConfigM.ManagedActors[j].SAClass))) $ "&nbsp;" $ Mutators.GetTag(i));
			TempStr = WebInclude(NowrapLeft);
			Res.Subst("Content", ConfigM.ManagedActors[j].SADescription);
			Res.Subst("RowContent", TempStr $ WebInclude(CellLeft));
			ReviewRows += WebInclude(RowLeft);
		}

		Res.Subst("SectionName", TableTitle[3]);
		Res.Subst("SpecialInstructions", LabelRequiredMaps);
		ReviewRows += WebInclude(IncludeSection);

		Res.Subst("SettingName", LabelMaxMaps);
		Res.Subst("SettingData", Textbox("MaxMaps", 3, 3, CurS.getValue("MaxMaps","3")));
		Res.Subst("Extra", lastgroup);
		ReviewRows += WebInclude(IncludeRows);
		for (i=0;i<CurS.GetMapLength();i++)
		{
			Res.Subst("SettingName", CurS.getMap(i));
			Res.Subst("Extra", "");
			Res.Subst("SettingData", Checkbox(CurS.getMap(i), CurS.MapRequired(CurS.getMap(i))));

			ReviewRows += WebInclude(IncludeRows);
		}

		Res.Subst("SectionName", TableTitle[4]);
		Res.Subst("SpecialInstructions", LabelPlayInfo);
		Res.Subst("Extra", lastgroup);
		ReviewRows += WebInclude(IncludeSection);

		ReviewRows += (lastgroup $ GeneratePlayInfoRows(Req, Res, IncludeRows));
		Res.Subst("TableRows", ReviewRows);

	// Set ladder_caption_table subst's
		Res.Subst("PageCaption", 	CurS.getValue("PageCaption",PageCaption[5]));
		Res.Subst("ButtonName", 	CurS.getValue("ButtonName", ProfileButtonName[5]));
		Res.Subst("ButtonValue", 	CurS.getValue("ButtonValue", ProfileButtonValue[5]));

		Res.Subst("LadderTable", 	WebInclude(IncludeTable)$WebInclude(IncludeCaptionButton));
		Res.Subst("PostAction", 	SubSavedURI$"?SessionID="$SID);
		Res.Subst("NewTarget",		" target=\"_parent\"");
		ShowPage(Res, SubFrameContent);
	}

	else AccessDenied(Res);
}

// Page 6
function QuerySubSaved(WebRequest Req, WebResponse Res)
{
	local int i,k, x;
	local string 	SID,
					tmp, tmp1,
					TempStr;
	local Session 				CurS;
	local PlayInfo 				TempPI;
	local ProfileConfigSet 		TempPCS;
	local array<string> 		TempArr;
	local array<class<Mutator> > MClass;
	local array<class<Info> >	SAClass;

	local class<Mutator> 		MutClass;
	local class<GameInfo> 		GIClass;
	local class<AccessControl> 	ACClass;
	local class<Info>			TempSA;


	SID = Req.GetVariable("SessionID");
	CurS = SMaster.getSession(SID);
	if (CanPerform("Lm"))
	{
		TempStr = CurS.getValue("GameType");
		if (TempStr != "")
			GIClass = class<GameInfo>(DynamicLoadObject(TempStr,class'Class'));
		if (GIClass == None)
		{
			ShowFramedMessage(Res,ErrorGameClassText,True);
			return;
		}

		TempStr = CurS.getValue("ACClass",Level.Game.AccessControlClass);
		if (TempStr != "")
			ACClass = class<AccessControl>(DynamicLoadObject(TempStr,class'Class'));
		if (ACClass == None)
		{
			ShowFramedMessage(Res,ErrorAccessControlText,True);
			return;
		}
		TempPI = new(None) class'PlayInfo';

		// We are managing an existing profile
		if (CurS.hasData("ProfileIndex"))
        {
            k = int(CurS.getValue("ProfileIndex"));
            TempPCS = LadderRules.LadderProfiles[k];
            if (LadderRules.Profiles[k].ProfileName != CurS.getValue("ProfileName"))
			{
				LadderRules.Profiles[k].ProfileName = CurS.getValue("ProfileName");
				LadderRules.AllLadderProfiles.Remove(LadderRules.AllLadderProfiles.FindItemId(string(k)));
				LadderRules.AllLadderProfiles.Add(string(k),LadderRules.Profiles[k].ProfileName);
				LadderRules.SaveConfig();
			}

			if (TempPCS.StartEdit())
			{
				TempPCS.SetGameType(CurS.getValue("GameType"));
				TempPCS.EndEdit(False);
			}
        }

		else TempPCS = LadderRules.AddProfile(CurS.getValue("ProfileName"), CurS.getValue("GameType"));

        if (TempPCS.StartEdit())
		{
			for (i=0;i<AllMutators.Length;i++)
			{
				MutClass = class<Mutator>(DynamicLoadObject(AllMutators[i].ClassName,class'Class'));
				if (MutClass != None)
				{
					if ((Req.GetVariable(AllMutators[i].ClassName,"")!="")||(Req.GetVariable(AllMutators[i].GroupName,"")~=AllMutators[i].ClassName))
						CurS.setMutator(AllMutators[i].ClassName,true,true);
					else CurS.setMutator(AllMutators[i].ClassName,false);

					if (CurS.HasMutator(AllMutators[i].ClassName))
					{
						MClass[MClass.Length] = MutClass;
						TempPCS.AddProfileMutator(AllMutators[i].ClassName,CurS.MutRequired(AllMutators[i].ClassName));
					}
					else TempPCS.DelProfileMutator(AllMutators[i].ClassName);
				}
			}

			for (i=0;i<ConfigM.ManagedActors.Length;i++)
			{
				TempSA = ConfigM.ManagedActors[i].SAClass;
				if (TempSA != None)
				{
					TempStr = string(ConfigM.ManagedActors[i].SAClass);
					if (Req.GetVariable(TempStr,"") != "")
						CurS.setServerActor(TempStr, True, True);
					else CurS.setServerActor(TempStr, False);

					if (CurS.HasServerActor(TempStr))
					{
						SAClass[SAClass.Length] = TempSA;
						TempPCS.AddProfileServerActor(TempStr,CurS.SARequired(TempStr));
					}
					else TempPCS.DelProfileServerActor(TempStr);
				}
			}

			x = LoadThisPlayInfo(TempPI, GIClass, ACClass, MClass, SAClass);
			if (x >= 0)
			{
				ShowFramedMessage(Res, ErrorLoadingPlayInfoText[x],True);
				return;
			}
			CurS.setValue("MaxMaps",Req.GetVariable("MaxMaps"),true);
			TempPCS.MaxMaps = int(CurS.getValue("MaxMaps"));

			GIClass.static.LoadMapList(GIClass.default.MapPrefix, TempArr);
			for (i=0;i<TempArr.Length;i++)
			{
				if (Req.GetVariable(TempArr[i],"")!="")
					CurS.setMap(TempArr[i],0,true,true);
				else CurS.setMap(TempArr[i],0,false,false);

				if (CurS.HasMap(TempArr[i]))
					TempPCS.AddProfileMap(TempArr[i], CurS.MapRequired(TempArr[i]));
				else TempPCS.DelProfileMap(TempArr[i]);
			}

			for (i=0;i<TempPI.Settings.Length;i++)
			{
				if (TempPI.Settings[i].SecLevel > CurAdmin.MaxSecLevel()) continue;
				tmp = ""; tmp1="";
				if (CurS.getIdValue(i,tmp,tmp1) == -1)
					break;
				if (!TempPCS.SetProfileNamedParam(TempPI.Settings[i].SettingName, CurS.getValue(TempPI.Settings[i].SettingName,TempPI.Settings[i].Value)))
					TempPCS.SetProfileParam(TempPCS.Count(),TempPI.Settings[i].SettingName,CurS.getValue(TempPI.Settings[i].SettingName,TempPI.Settings[i].Value));
			}

			TempPCS.EndEdit(true);
		}

		else
		{
			StatusError(Res,ErrorSavingProfile);
			QuerySubReview(Req, Res);
			return;
		}
		Tmp = CurS.getValue("ProfileName", PageCaption[0]);
		if (LadderRules.GetRemainingMatches() > 0 && Tmp == LadderRules.Profiles[GetCurrentConfigIndex()].ProfileName)
			Tmp = Tmp @ LabelNumberMatchesLeft @ string(LadderRules.GetRemainingMatches());
		Res.Subst("SubTitle", Tmp);
		ShowMessage(Res, TableTitle[6], PageCaption[6]);
		return;
	}

	else AccessDenied(Res);
}

defaultproperties
{
     EpicPackages(0)="BR-Anubis"
     EpicPackages(1)="BR-Bifrost"
     EpicPackages(2)="BR-Disclosure"
     EpicPackages(3)="BR-IceFields"
     EpicPackages(4)="BR-Skyline"
     EpicPackages(5)="BR-Slaughterhouse"
     EpicPackages(6)="BR-TwinTombs"
     EpicPackages(7)="CTF-Chrome"
     EpicPackages(8)="CTF-Citadel"
     EpicPackages(9)="CTF-December"
     EpicPackages(10)="CTF-Face3"
     EpicPackages(11)="CTF-Geothermal"
     EpicPackages(12)="CTF-Lostfaith"
     EpicPackages(13)="CTF-Magma"
     EpicPackages(14)="CTF-Maul"
     EpicPackages(15)="CTF-Orbital2"
     EpicPackages(16)="DM-Antalus"
     EpicPackages(17)="DM-Asbestos"
     EpicPackages(18)="DM-Compressed"
     EpicPackages(19)="DM-Curse3"
     EpicPackages(20)="DM-Flux2"
     EpicPackages(21)="DM-Gael"
     EpicPackages(22)="DM-Inferno"
     EpicPackages(23)="DM-Insidious"
     EpicPackages(24)="DM-Leviathan"
     EpicPackages(25)="DM-Oceanic"
     EpicPackages(26)="DM-Phobos2"
     EpicPackages(27)="DM-Plunge"
     EpicPackages(28)="DM-Serpentine"
     EpicPackages(29)="DM-TokaraForest"
     EpicPackages(30)="DM-TrainingDay"
     EpicPackages(31)="DOM-Core"
     EpicPackages(32)="DOM-OutRigger"
     EpicPackages(33)="DOM-Ruination"
     EpicPackages(34)="DOM-ScorchedEarth"
     EpicPackages(35)="DOM-SepukkuGorge"
     EpicPackages(36)="DOM-Suntemple"
     EpicPackages(37)="Entry"
     EpicPackages(38)="NvidiaLogo"
     EpicPackages(39)="TUT-BR"
     EpicPackages(40)="TUT-CTF"
     EpicPackages(41)="TUT-DM"
     EpicPackages(42)="TUT-DOM"
     EpicPackages(43)="UT2-intro"
     EpicPackages(44)="VehicleDemo"
     EpicPackages(45)="endgame"
     EpicPackages(46)="Core"
     EpicPackages(47)="Editor"
     EpicPackages(48)="Engine"
     EpicPackages(49)="Fire"
     EpicPackages(50)="GamePlay"
     EpicPackages(51)="IpDrv"
     EpicPackages(52)="UWeb"
     EpicPackages(53)="UnrealEd"
     EpicPackages(54)="UnrealGame"
     EpicPackages(55)="Vehicles"
     EpicPackages(56)="XAdmin"
     EpicPackages(57)="XEffects"
     EpicPackages(58)="XGame"
     EpicPackages(59)="XGame_rc"
     EpicPackages(60)="XInterface"
     EpicPackages(61)="XPickups"
     EpicPackages(62)="XPickups_rc"
     EpicPackages(63)="XWeapons"
     EpicPackages(64)="XWeapons_rc"
     EpicPackages(65)="XWebAdmin"
     EpicPackages(66)="BR-DE-ElecFields"
     EpicPackages(67)="CTF-DE-ElecFields"
     EpicPackages(68)="CTF-DE-LavaGiant2"
     EpicPackages(69)="DM-DE-Grendelkeep"
     EpicPackages(70)="DM-DE-Ironic"
     EpicPackages(71)="DM-DE-Osiris2"
     EpicPackages(72)="BR-Canyon"
     EpicPackages(73)="CTF-Avaris"
     EpicPackages(74)="DM-1on1-Crash"
     EpicPackages(75)="DM-1on1-Mixer"
     EpicPackages(76)="DM-Icetomb"
     EpicPackages(77)="DM-Injector"
     EpicPackages(78)="DM-IronDeity"
     EpicPackages(79)="DM-Rustatorium"
     EpicPackages(80)="DOM-Junkyard"
     EpicPackages(81)="SkaarjPack"
     EpicPackages(82)="SkaarjPack_rc"
     EpicPackages(83)="BonusPack"
     EpicPackages(84)="OGGPlayer"
     MasterPage="ladder"
     SubFrame="ladder_frame"
     SubFrameMenu="ladder_frame1"
     SubFrameContent="ladder_frame2"
     IncludeTable="ladder_table"
     IncludeSection="ladder_section"
     IncludeRows="ladder_rows"
     IncludeCaptionButton="ladder_caption"
     IncludeMapSelect="ladder_mapselect"
     IncludeProfileName="ladder_name"
     IncludeGameType="ladder_gametype"
     IncludeCheckBox="ladder_checkrow"
     IncludeSwitch="ladder_switch"
     ProfileViewURI="ladder_view"
     ProfileEditURI="ladder_edit"
     ProfileMenuURI="ladder_menu"
     ProfileSwitchURI="ladder_switch"
     ProfileManageURI="ladder_manage"
     SubFrameURI="profile_management"
     SubFrameMenuURI="management_menu"
     SubMapsURI="ladder_maps"
     SubSavedURI="ladder_save"
     SubRulesURI="ladder_rules"
     SubReviewURI="ladder_review"
     SubGametypeURI="management_game_type"
     SubMutatorsURI="ladder_mutators"
     SubImportURI="profile_import"
     SubExportURI="profile_export"
     LinkEditProfile="Edit Profiles"
     LinkViewProfile="View Profiles"
     LinkSwitchProfile="Switch Profiles"
     LinkManageProfile="Manage Profiles"
     LinkImportProfile="Import Profile"
     LinkExportProfile="Export Profile"
     TableTitle(0)="Profile Management Portal"
     TableTitle(1)="Profile GameType"
     TableTitle(2)="Profile Mutators"
     TableTitle(3)="Profile Maps"
     TableTitle(4)="Profile Settings"
     TableTitle(5)="Review Profile"
     TableTitle(6)="Profile Saved"
     PageCaption(0)="No Profile Loaded"
     PageCaption(1)="Select the gametype that will be associated with this profile."
     PageCaption(2)="Select the mutators that will be available in this profile."
     PageCaption(3)="Select the maps that will be available in this profile."
     PageCaption(4)="Select the settings that will be associated with this profile."
     PageCaption(5)="Review all settings for accuracy before saving to file."
     PageCaption(6)="Settings were saved successfully!"
     ProfileButtonName(0)="Save Location"
     ProfileButtonName(1)="GameType"
     ProfileButtonName(2)="Mutators"
     ProfileButtonName(3)="Maps"
     ProfileButtonName(4)="Rules"
     ProfileButtonName(5)="Review"
     ProfileButtonName(6)="Saved"
     ProfileButtonValue(0)="Proceed"
     ProfileButtonValue(1)="Set GameType"
     ProfileButtonValue(2)="Set Mutators"
     ProfileButtonValue(3)="Set Maps"
     ProfileButtonValue(4)="Set Rules"
     ProfileButtonValue(5)="Save Profile"
     ProfileButtonValue(6)="Profile Saved"
     MapText="Maps"
     ErrorText="Error"
     MutatorText="Mutators"
     RequiredText="Required"
     AvailableText="Available"
     UnlimitedText="Unlimited"
     FollowingMaps="The following maps"
     AreAssignedText="are assigned to this profile and cannot be removed."
     AreAvailableText="may be added to this profile."
     FollowingMutators="The following mutators"
     CommandLineOptionsTitle="Command Line Options"
     ButtonLoadProfile="Load Profile"
     ButtonDeleteProfile="Delete Profile"
     ButtonPreviewProfile="Load Profile"
     ButtonApplyImportProfile="Import Profile"
     ButtonApplyExportProfile="Export Profile"
     ButtonApplyProfileChanges="Apply Profile Changes"
     LabelCopyProfile="Copy Profile"
     LabelDeleteProfile="Delete Profile"
     LabelImportProfile="Import Profile"
     LabelExportProfile="Export Profile"
     LabelAddProfile="Add Profile"
     LabelSelectActions="(Copy, Modify, Delete, Export)"
     LabelTextboxActions="(Add, Copy, Import)"
     LabelPlayInfo="Profile Game Parameters"
     LabelGeneralProfile="General Profile Information"
     LabelGameType="Profile Game Type"
     LabelProfileName="Profile Name"
     LabelAccessControl="Profile Access Control"
     LabelChooseAction="Choose action to perform:"
     LabelManagement="Profile Management"
     LabelInputDescription="Actions which require input from this field are listed on the right."
     LabelMaxMaps="Maximum maps allowed in this profile's maplist"
     LabelNumberGames="Number of matches (0 = unlimited)"
     LabelCheckAllMap="Select all maps"
     LabelRequiredMaps="Check each map that will be required (non-removable) for this profile."
     LabelUncheckAllMap="Deselect all maps"
     LabelChangeMaplist="Changes to the maplist are saved immediately."
     LabelCheckAllMutator="Select all mutators"
     LabelRequiredMutators="Check each mutator that will be required (non-removable) for this profile."
     LabelNumberMatchesLeft="Matches Remaining:"
     LabelUncheckAllMutator="Deselect all mutators"
     LabelDelayProfileChange="Check to delay applying profile until end of current match."
     LabelApplyImportProfile="Click Apply Import to make this profile available in your server's list of profiles."
     LabelApplyExportProfile="Click Apply Export to save this profile to a .txt file."
     LabelModifyProfile="Modify Profile"
     SuccessText="Success"
     SuccessDeleteProfile="Profile successfully deleted!  It will no longer appear in your list of available profiles."
     SuccessExportProfile="Profile exported successfully!  Exported profile has been saved to the server's System folder as "
     SuccessImportProfile="Profile imported successfully! The remote profile will now appear in your list of available profiles."
     SuccessImportProfileTitle="Import Successful!"
     SuccessCopyProfile="Profile copied successfully! Copied profile will now be available in your list of available profiles."
     SuccessSwitchProfile="Profile successfully applied! Changes take effect at the end of the current match."
     ErrorLoadingPlayInfoText(0)="Error while loading playinfo.  LoadThisPlayInfo() received bad playinfo object!"
     ErrorLoadingPlayInfoText(1)="Error while loading playinfo.  LoadThisPlayInfo() could not load incoming game class!"
     ErrorLoadingPlayInfoText(2)="Error while loading playinfo.  LoadThisPlayInfo() could not load associated access control!"
     ErrorLoadingPlayInfoText(3)="Error while loading playinfo.  LoadThisPlayInfo() had exception while loading mutators!"
     ErrorLoadingPlayInfoText(4)="Error while loading playinfo.  LoadThisPlayInfo() had exception while loading server actors!"
     ErrorGameClassText="No gametype was selected, or the files required by this gametype do not exist on this server."
     ErrorGameClassTitle="Invalid GameType"
     ErrorAccessControlText="Exception while loading access control.  Please double check the AccessControlClass= line of the [Engine.GameInfo] section of your server's .ini file."
     ErrorAccessControlTitle="Invalid Access Control"
     ErrorSameProfileName="Another profile already exists with that name. Please select a different name!"
     ErrorRemoteMutator="Error loading mutator from remote profile. Could not find mutator package:"
     ErrorRemoteServerActor="Error loading ServerActor from remote profile.  Could not find ServerActor package:"
     ErrorSavingProfile="Exception while saving new profile! Please resubmit save request."
     ErrorDeletingProfile="Error deleting profile!  Please check server log for details."
     ErrorExportingProfile="Error exporting profile! Export class could not be spawned!"
     ErrorNoProfilesExist="No profiles exist! Could not perform selected action!"
     ErrorCopyFrom="Error copying profile.  No Profile Found!"
     ErrorCopyNoName="You must specify a unique name for the new profile!"
     ErrorExcludeMapNotFound="Error! Exclude map not found:"
     ErrorIncludeMapNotFound="Error! Include map not found:"
     ErrorReplacingWebConnection="Exception replacing WebServer.AcceptClass! (Very bad) Please send an email to Evolution containing your server's ini and log files."
     ImportClassSpawnError="Exception occured spawning connection class.  Please try again!"
     ImportProfileFailText="Error while importing profile.  Please ensure you have all the files necessary to load this remote profile."
     ImportProfileFailTitle="Import Failed!"
     ErrorBind="Failed to bind port! Please try again!"
     ErrorBadURL="URL must start with 'http://' !"
     ErrorServer="Unspecified error while importing profile!"
     ErrorResolve="URL could not be resolved!"
     ErrorProfile="Attempted to import a file that does not contain a profile!"
     ErrorTimeout="Timed out while waiting for response from foreign host!"
     ErrorNotFound="Profile does not exist on remote server!"
     ErrorForbidden="Remote profile could not be imported: Forbidden"
     ErrorBadRequest="Request could not be processed by remote server!"
     ErrorUnauthorized="Remote profile could not be imported: Unauthorized"
     DefaultPage="ladder_main"
     Title="Ladder"
}