#include "stdafx.h"
#include <xuiresource.h>
#include <xuiapp.h>
#include <assert.h>
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "..\..\Common\Tutorial\TutorialMode.h"
#include "..\..\..\Minecraft.World\ConsoleSaveFileIO.h"
#include "..\..\LocalPlayer.h"
#include "..\..\Minecraft.h"
#include "..\..\ProgressRenderer.h"
#include "..\..\..\Minecraft.World\AABB.h"
#include "..\..\..\Minecraft.World\Vec3.h"
#include "..\..\..\Minecraft.World\ArrayWithLength.h"
#include "..\..\..\Minecraft.World\File.h"
#include "..\..\..\Minecraft.World\InputOutputStream.h"
#include "XUI_Ctrl_4JList.h"
#include "XUI_Ctrl_4JIcon.h"
#include "XUI_LoadSettings.h"
#include "XUI_MultiGameInfo.h"
#include "XUI_MultiGameJoinLoad.h"
#include "XUI_MultiGameCreate.h"
#include "..\..\MinecraftServer.h"
#include "..\..\Options.h"

#include "..\GameRules\LevelGenerationOptions.h"
#include "..\..\TexturePackRepository.h"
#include "..\..\TexturePack.h"
#include "..\..\..\Minecraft.World\LevelSettings.h"

#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID 3
#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME 100

//----------------------------------------------------------------------------------
// Performs initialization tasks - retrieves controls.
//----------------------------------------------------------------------------------
HRESULT CScene_MultiGameJoinLoad::OnInit( XUIMessageInit* pInitData, BOOL& bHandled )
{

	m_iPad=*(int *)pInitData->pvInitData;
	m_bReady=false;
	MapChildControls();

	m_iTexturePacksNotInstalled=0;
	m_iConfigA=NULL;
	
	XuiControlSetText(m_LabelNoGames,app.GetString(IDS_NO_GAMES_FOUND));
	XuiControlSetText(m_GamesList,app.GetString(IDS_JOIN_GAME));
	XuiControlSetText(m_SavesList,app.GetString(IDS_START_GAME));


	const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string 
	WCHAR szResourceLocator[ LOCATOR_SIZE ];

	const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL);
	swprintf(szResourceLocator, LOCATOR_SIZE ,L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/TexturePackIcon.png");

	m_DefaultMinecraftIconSize = 0;
	HRESULT hr = XuiResourceLoadAllNoLoc(szResourceLocator, &m_DefaultMinecraftIconData, &m_DefaultMinecraftIconSize);

	m_localPlayers = 1;
	m_bKillSaveInfoEnumerate=false;

	m_bShowingPartyGamesOnly = false;

	m_bRetrievingSaveInfo=false;
	m_bSaveTransferInProgress=false;
	
	// check for a default custom cloak in the global storage
	// 4J-PB - changed to a config file
// 	if(ProfileManager.IsSignedInLive( m_iPad ))
// 	{		
// 		app.InstallDefaultCape();
// 	}

	m_initData= new JoinMenuInitData();
	m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad);
	
	XPARTY_USER_LIST partyList;

	if((XPartyGetUserList(  &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1))
	{
		m_bInParty=true;
	}
	else
	{
		m_bInParty=false;
	}

	int iLB = -1;
	if(m_bInParty) iLB = IDS_TOOLTIPS_PARTY_GAMES;

	XuiSetTimer(m_hObj,JOIN_LOAD_ONLINE_TIMER_ID,JOIN_LOAD_ONLINE_TIMER_TIME);
	
	m_iSaveInfoC=0;

	VOID *pObj;
	XuiObjectFromHandle( m_SavesList, &pObj );
	m_pSavesList = (CXuiCtrl4JList *)pObj;

	XuiObjectFromHandle( m_GamesList, &pObj );
	m_pGamesList = (CXuiCtrl4JList *)pObj;

	// block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again
	if(app.StartInstallDLCProcess(m_iPad)==true)
	{
		// not doing a mount, so enable input
		m_bIgnoreInput=true;
	}
	else
	{
		// if we're waiting for DLC to mount, don't fill the save list. The custom message on end of dlc mounting will do that
		m_bIgnoreInput=false;
	


		m_iChangingSaveGameInfoIndex = 0;

		m_generators = app.getLevelGenerators();
		m_iDefaultButtonsC = 0;
		m_iMashUpButtonsC=0;

		// check if we're in the trial version
		if(ProfileManager.IsFullVersion()==false)
		{
			ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1, -1, -1,iLB);

			AddDefaultButtons();

			m_pSavesList->SetCurSelVisible(0);
		}
		else if(StorageManager.GetSaveDisabled())
		{
			if(StorageManager.GetSaveDeviceSelected(m_iPad))
			{
				// saving is disabled, but we should still be able to load from a selected save device

				ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,-1,-1,-1,iLB,IDS_TOOLTIPS_DELETESAVE);
			
				GetSaveInfo();
			}
			else
			{
				ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,-1,-1,-1,iLB);

				AddDefaultButtons();
				m_SavesListTimer.SetShow( FALSE );

				m_pSavesList->SetCurSelVisible(0);
			}
		}
		else
		{
			// 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename)
			bool bCanRename = StorageManager.EnoughSpaceForAMinSaveGame();
			ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,-1,-1,-1,-1,bCanRename?IDS_TOOLTIPS_SAVEOPTIONS:IDS_TOOLTIPS_DELETESAVE);
		
			GetSaveInfo();
		}
	}
	//XuiElementSetDisableFocusRecursion( m_pGamesList->m_hObj, TRUE);

	UpdateGamesList();

	g_NetworkManager.SetSessionsUpdatedCallback( &CScene_MultiGameJoinLoad::UpdateGamesListCallback, this );

	// 4J Stu - Fix for #12530 -TCR 001 BAS Game Stability: Title will crash if the player disconnects while starting a new world and then opts to play the tutorial once they have been returned to the Main Menu.
	MinecraftServer::resetFlags();
	
	// If we're not ignoring input, then we aren't still waiting for the DLC to mount, and can now check for corrupt dlc. Otherwise this will happen when the dlc has finished mounting.
	if( !m_bIgnoreInput)
	{
		app.m_dlcManager.checkForCorruptDLCAndAlert();
	}


	// 4J-PB - Load up any texture pack data we have locally in the XZP
	for(int i=0;i<TMS_COUNT;i++)
	{
		if(app.TMSFileA[i].eTMSType==eTMSFileType_TexturePack)
		{
			app.LoadLocalTMSFile(app.TMSFileA[i].wchFilename,app.TMSFileA[i].eEXT);
			app.AddMemoryTPDFile(app.TMSFileA[i].iConfig, app.TMSFileA[i].pbData,app.TMSFileA[i].uiSize);
		}
	}

	// 4J-PB - there may be texture packs we don't have, so use the info from TMS for this

	DLC_INFO *pDLCInfo=NULL;

	// first pass - look to see if there are any that are not in the list
	bool bTexturePackAlreadyListed;
	bool bNeedToGetTPD=false;
	Minecraft *pMinecraft = Minecraft::GetInstance();
	int texturePacksCount = pMinecraft->skins->getTexturePackCount();	
	//CXuiCtrl4JList::LIST_ITEM_INFO ListInfo;
	//HRESULT hr;


	for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i)
	{
		bTexturePackAlreadyListed=false;
		ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i);
		pDLCInfo=app.GetDLCInfoForFullOfferID(ull);
		for(unsigned int i = 0; i < texturePacksCount; ++i)
		{
			TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i);
			if(pDLCInfo->iConfig==tp->getDLCParentPackId())
			{
				bTexturePackAlreadyListed=true;
			}
		}
		if(bTexturePackAlreadyListed==false)
		{
			// some missing
			bNeedToGetTPD=true;

			m_iTexturePacksNotInstalled++;
		}
	}

	if(bNeedToGetTPD==true)
	{
		// add a TMS request for them
		app.DebugPrintf("+++ Adding TMSPP request for texture pack data\n");
		app.AddTMSPPFileTypeRequest(e_DLC_TexturePackData);
		m_iConfigA= new int [m_iTexturePacksNotInstalled];
		m_iTexturePacksNotInstalled=0;

		for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i)
		{
			bTexturePackAlreadyListed=false;
			ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i);
			pDLCInfo=app.GetDLCInfoForFullOfferID(ull);
			for(unsigned int i = 0; i < texturePacksCount; ++i)
			{
				TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i);
				if(pDLCInfo->iConfig==tp->getDLCParentPackId())
				{
					bTexturePackAlreadyListed=true;
				}
			}
			if(bTexturePackAlreadyListed==false)
			{
				m_iConfigA[m_iTexturePacksNotInstalled++]=pDLCInfo->iConfig;
			}
		}
	}

	XuiSetTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME);

	return S_OK;
}

void CScene_MultiGameJoinLoad::AddDefaultButtons()
{
	CXuiCtrl4JList::LIST_ITEM_INFO ListInfo;

	// Add two for New Game and Tutorial
	ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO));

	ListInfo.pwszText = app.GetString(IDS_CREATE_NEW_WORLD);
	ListInfo.fEnabled = TRUE;
	ListInfo.iData = -1;
	m_pSavesList->AddData(ListInfo);

	int iSavesListIndex = 0;
	int iGeneratorIndex = 0;
	m_iMashUpButtonsC=0;

	for(AUTO_VAR(it, m_generators->begin()); it != m_generators->end(); ++it)
	{
		LevelGenerationOptions *levelGen = *it;
		ListInfo.pwszText = levelGen->getWorldName();
		ListInfo.fEnabled = TRUE;
		ListInfo.iData = iGeneratorIndex++; // used to index into the list of generators

		// need to check if the user has disabled this pack in the save display list
		unsigned int uiTexturePackID=levelGen->getRequiredTexturePackId();

		if(uiTexturePackID!=0)
		{
			unsigned int uiMashUpWorldsBitmask=app.GetMashupPackWorlds(m_iPad);

			if((uiMashUpWorldsBitmask & (1<<(uiTexturePackID-1024)))==0)
			{
				// this world is hidden, so skip
				continue;
			}
		}
		m_pSavesList->AddData(ListInfo);

		// retrieve the save icon from the texture pack, if there is one
		if(uiTexturePackID!=0)
		{
			// increment the count of the mash-up pack worlds in the save list
			m_iMashUpButtonsC++;
			TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackById(levelGen->getRequiredTexturePackId());
			DWORD dwImageBytes;	
			PBYTE pbImageData = tp->getPackIcon(dwImageBytes);
			HXUIBRUSH hXuiBrush;

			if(dwImageBytes > 0 && pbImageData)
			{
				XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush);
				// the index inside the list item for this will be i+1 because they start at m_vListData.size(), so the first etry (tutorial) is 1
				m_pSavesList->UpdateGraphic(iSavesListIndex+1,hXuiBrush);
			}
		}

		++iSavesListIndex;
	}

	m_iDefaultButtonsC = iSavesListIndex + 1;
}


HRESULT CScene_MultiGameJoinLoad::GetSaveInfo(  )
{
	unsigned int uiSaveC=0;

	// This will return with the number retrieved in uiSaveC

	if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled())
	{
		uiSaveC = 0;
		File savesDir(L"GAME:\\Saves");
		if( savesDir.exists() )
		{
			m_saves = savesDir.listFiles();
			uiSaveC = (unsigned int)m_saves->size();
		}
		// add the New Game and Tutorial after the saves list is retrieved, if there are any saves

		// Add two for New Game and Tutorial
		unsigned int listItems = uiSaveC;

		CXuiCtrl4JList::LIST_ITEM_INFO ListInfo;
		ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO));

		AddDefaultButtons();

		for(unsigned int i=0;i<listItems;i++)
		{

			wstring wName = m_saves->at(i)->getName();
			wchar_t *name = new wchar_t[wName.size()+1];
			for(unsigned int j = 0; j < wName.size(); ++j)
			{
				name[j] = wName[j];
			}
			name[wName.size()] = 0;
			ListInfo.pwszText = name;
			ListInfo.fEnabled=TRUE;
			ListInfo.iData = -1;
			m_pSavesList->AddData(ListInfo);
		}	
		m_pSavesList->SetCurSelVisible(0);	
	}
	else
	{
		m_bRetrievingSaveInfo=true; // we're blocking the exit from this scene until complete

		// clear the saves list
		m_pSavesList->RemoveAllData();

		m_iSaveInfoC=0;
#ifdef _XBOX
		C4JStorage::ESGIStatus eSGIStatus=StorageManager.GetSavesInfo(ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::GetSavesInfoCallback,this,"savegame.dat");

		if(eSGIStatus==C4JStorage::ESGIStatus_NoSaves)
		{
			uiSaveC=0;
			m_SavesListTimer.SetShow( FALSE );
			m_SavesList.SetEnable(TRUE);
		}
#else

		//C4JStorage::ESaveGameState eStatus=StorageManager.GetSavesInfo(ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::GetSavesInfoCallback,this,"savegame.dat");

#endif
	}

	return S_OK;
}

HRESULT CScene_MultiGameJoinLoad::OnDestroy()
{
	g_NetworkManager.SetSessionsUpdatedCallback( NULL, NULL );

	for(AUTO_VAR(it, currentSessions.begin()); it < currentSessions.end(); ++it)
	{
		delete (*it);
	}

	if(m_bSaveTransferInProgress)
	{
		CancelSaveUploadCallback(this);
	}

	// Reset the background downloading, in case we changed it by attempting to download a texture pack
	XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO);

	// clear out the texture pack data
	for(int i=0;i<TMS_COUNT;i++)
	{
		if(app.TMSFileA[i].eTMSType==eTMSFileType_TexturePack)
		{
			app.RemoveMemoryTPDFile(app.TMSFileA[i].iConfig);
		}
	}
	app.FreeLocalTMSFiles(eTMSFileType_TexturePack);

	return S_OK;
}


int CScene_MultiGameJoinLoad::DeviceRemovedDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;

	// results switched for this dialog
	if(result==C4JStorage::EMessage_ResultDecline) 
	{
		StorageManager.SetSaveDisabled(true);
		StorageManager.SetSaveDeviceSelected(ProfileManager.GetPrimaryPad(),false);
		// use the device select returned function to wipe the saves list and change the tooltip
		CScene_MultiGameJoinLoad::DeviceSelectReturned(pClass,true);
	}
	else // continue without saving
	{
		// Change device
		StorageManager.SetSaveDevice(&CScene_MultiGameJoinLoad::DeviceSelectReturned,pClass,true);
	}

	pClass->m_bIgnoreInput=false;
	return 0;
}
//----------------------------------------------------------------------------------
// Handler for the button press message.
//----------------------------------------------------------------------------------
HRESULT CScene_MultiGameJoinLoad::OnNotifyPressEx(HXUIOBJ hObjPressed, XUINotifyPress* pNotifyPressData, BOOL& rfHandled)
{
	if(m_bIgnoreInput) return S_OK;

	// if we're retrieving save info, ignore key presses
	if(m_bRetrievingSaveInfo)
	{
		return S_OK;
	}

	// This assumes all buttons can only be pressed with the A button
	ui.AnimateKeyPress(pNotifyPressData->UserIndex, VK_PAD_A);

	if ( hObjPressed == m_GamesList )
	{
		m_bIgnoreInput=true;

		DWORD nIndex = m_pGamesList->GetCurSel();

		if( m_pGamesList->GetItemCount() > 0 && nIndex < currentSessions.size() )
		{
			//CScene_MultiGameInfo::JoinMenuInitData *initData = new CScene_MultiGameInfo::JoinMenuInitData();
			m_initData->iPad = m_iPad;
			m_initData->selectedSession = currentSessions.at( nIndex );
		
			// check that we have the texture pack available
			// If it's not the default texture pack
			if(m_initData->selectedSession->data.texturePackParentId!=0)
			{
				int texturePacksCount = Minecraft::GetInstance()->skins->getTexturePackCount();
				bool bHasTexturePackInstalled=false;

				for(int i=0;i<texturePacksCount;i++)
				{
					TexturePack *tp = Minecraft::GetInstance()->skins->getTexturePackByIndex(i);
					if(tp->getDLCParentPackId()==m_initData->selectedSession->data.texturePackParentId)
					{
						bHasTexturePackInstalled=true;
						break;
					}
				}

				if(bHasTexturePackInstalled==false)
				{
					// upsell the texture pack
					// tell sentient about the upsell of the full version of the skin pack
					ULONGLONG ullOfferID_Full;
					app.GetDLCFullOfferIDForPackID(m_initData->selectedSession->data.texturePackParentId,&ullOfferID_Full);

					TelemetryManager->RecordUpsellPresented(pNotifyPressData->UserIndex, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF);

					UINT uiIDA[3];

					// Need to check if the texture pack has both Full and Trial versions - we may do some as free ones, so only Full
					DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full);

					if(pDLCInfo->ullOfferID_Trial!=0LL)
					{
						uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION;
						uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION;
						uiIDA[2]=IDS_CONFIRM_CANCEL;
						// Give the player a warning about the texture pack missing
						StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 3, ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::TexturePackDialogReturned,this,app.GetStringTable());
					}
					else
					{
						uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION;
						uiIDA[1]=IDS_CONFIRM_CANCEL;
						// Give the player a warning about the texture pack missing
						StorageManager.RequestMessageBox(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::TexturePackDialogReturned,this,app.GetStringTable());
					}

					return S_OK;
				}
			}
			
			m_NetGamesListTimer.SetShow( FALSE );

			// Reset the background downloading, in case we changed it by attempting to download a texture pack
			XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO);

			// kill the texture pack check timer
			XuiKillTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID);
			app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_JoinMenu,m_initData);
		}
	}
	else if(hObjPressed==m_SavesList)
	{
		m_bIgnoreInput=true;

		CXuiControl pItem;
		int iIndex;
		// get the selected item
		iIndex=m_SavesList.GetCurSel(&pItem);

		CXuiCtrl4JList::LIST_ITEM_INFO info = m_pSavesList->GetData(iIndex);

		if(iIndex == JOIN_LOAD_CREATE_BUTTON_INDEX)
		{		
			app.SetTutorialMode( false );
			m_NetGamesListTimer.SetShow( FALSE );

			app.SetCorruptSaveDeleted(false);

			CreateWorldMenuInitData *params = new CreateWorldMenuInitData();
			params->iPad = m_iPad;
			app.NavigateToScene(pNotifyPressData->UserIndex,eUIScene_CreateWorldMenu,(void *)params);
		}
		else if(info.iData >= 0)
		{
			LevelGenerationOptions *levelGen = m_generators->at(info.iData);
			app.SetTutorialMode( levelGen->isTutorial() );
			// Reset the autosave time
			app.SetAutosaveTimerTime();

			if(levelGen->isTutorial())
			{
				LoadLevelGen(levelGen);
			}
			else
			{
				LoadMenuInitData *params = new LoadMenuInitData();
				params->iPad = m_iPad;
				// need to get the iIndex from the list item, since the position in the list doesn't correspond to the GetSaveGameInfo list because of sorting
				params->iSaveGameInfoIndex=-1;
				//params->pbSaveRenamed=&m_bSaveRenamed;
				params->levelGen = levelGen;

				// navigate to the settings scene
				app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_LoadMenu, params);
			}
		}
		else
		{			
			// check if this is a damaged save
			if(m_pSavesList->GetData(iIndex).bIsDamaged)
			{
				// give the option to delete the save
				UINT uiIDA[2];
				uiIDA[0]=IDS_CONFIRM_CANCEL;
				uiIDA[1]=IDS_CONFIRM_OK;
				StorageManager.RequestMessageBox(IDS_CORRUPT_OR_DAMAGED_SAVE_TITLE, IDS_CORRUPT_OR_DAMAGED_SAVE_TEXT, uiIDA, 2, pNotifyPressData->UserIndex,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,this, app.GetStringTable());
			}
			else
			{		
				app.SetTutorialMode( false );
				if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled())
				{
					LoadSaveFromDisk(m_saves->at(iIndex-m_iDefaultButtonsC));
				}
				else
				{
					LoadMenuInitData *params = new LoadMenuInitData();
					params->iPad = m_iPad;
					// need to get the iIndex from the list item, since the position in the list doesn't correspond to the GetSaveGameInfo list because of sorting
					params->iSaveGameInfoIndex=m_pSavesList->GetData(iIndex).iIndex-m_iDefaultButtonsC;
					//params->pbSaveRenamed=&m_bSaveRenamed;
					params->levelGen = NULL;

					// kill the texture pack timer
					XuiKillTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID);
					// navigate to the settings scene
					app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_LoadMenu, params);
				}
			}
		}
	}
	
	return S_OK;
}

HRESULT CScene_MultiGameJoinLoad::OnKeyDown(XUIMessageInput* pInputData, BOOL& rfHandled)
{
	if(m_bIgnoreInput) return S_OK;

	// if we're retrieving save info, ignore key presses
	if(m_bRetrievingSaveInfo)
	{
		return S_OK;
	}

	ui.AnimateKeyPress(pInputData->UserIndex, pInputData->dwKeyCode);

	HRESULT hr = S_OK;

	// Explicitly handle B button presses
	switch(pInputData->dwKeyCode)
	{
	case VK_PAD_B:
	case VK_ESCAPE:
		m_NetGamesListTimer.SetShow( FALSE );

		app.NavigateBack(XUSER_INDEX_ANY);
		rfHandled = TRUE;
		break;		
	case VK_PAD_X:

		// Change device
		// Fix for #12531 - TCR 001: BAS Game Stability: When a player selects to change a storage 
		// device, and repeatedly backs out of the SD screen, disconnects from LIVE, and then selects a SD, the title crashes.
		m_bIgnoreInput=true;
		StorageManager.SetSaveDevice(&CScene_MultiGameJoinLoad::DeviceSelectReturned,this,true);
		CXuiSceneBase::PlayUISFX(eSFX_Press);
		break;
	case VK_PAD_Y:
		if(m_pGamesList->TreeHasFocus() && m_pGamesList->GetItemCount() > 0)
		{
			DWORD nIndex = m_pGamesList->GetCurSel();
			FriendSessionInfo *pSelectedSession = currentSessions.at( nIndex );

			PlayerUID xuid = pSelectedSession->data.hostPlayerUID;
			if( xuid != INVALID_XUID )
				hr = XShowGamerCardUI(ProfileManager.GetLockedProfile(), xuid);
			CXuiSceneBase::PlayUISFX(eSFX_Press);
		}
		else if(DoesSavesListHaveFocus())
		{
			// save transfer - make sure they want to overwrite a save that is up there
			if(ProfileManager.IsSignedInLive( m_iPad ))
			{	
				// 4J-PB - required for a delete of the save if it's found to be a corrupted save
				DWORD nIndex = m_pSavesList->GetCurSel();
				m_iChangingSaveGameInfoIndex=m_pSavesList->GetData(nIndex).iIndex;

				UINT uiIDA[2];
				uiIDA[0]=IDS_UPLOAD_SAVE;
				uiIDA[1]=IDS_CONFIRM_CANCEL;

				ui.RequestMessageBox(IDS_SAVE_TRANSFER_TITLE, IDS_SAVE_TRANSFER_TEXT, uiIDA, 2, pInputData->UserIndex,&CScene_MultiGameJoinLoad::SaveTransferDialogReturned,this, app.GetStringTable());
			}
		}		
		break;
	case VK_PAD_RSHOULDER:
		if(DoesSavesListHaveFocus())
		{
			m_bIgnoreInput = true;
			
			int iIndex=m_SavesList.GetCurSel();
			m_iChangingSaveGameInfoIndex=m_pSavesList->GetData(iIndex).iIndex;

			// Could be delete save or Save Options
			if(StorageManager.GetSaveDisabled())
			{
				// delete the save game
				// Have to ask the player if they are sure they want to delete this game
				UINT uiIDA[2];
				uiIDA[0]=IDS_CONFIRM_CANCEL;
				uiIDA[1]=IDS_CONFIRM_OK;
				StorageManager.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, pInputData->UserIndex,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,this, app.GetStringTable());
			}
			else
			{
				if(StorageManager.EnoughSpaceForAMinSaveGame())
				{
					UINT uiIDA[3];
					uiIDA[0]=IDS_CONFIRM_CANCEL;
					uiIDA[1]=IDS_TITLE_RENAMESAVE;
					uiIDA[2]=IDS_TOOLTIPS_DELETESAVE;
					StorageManager.RequestMessageBox(IDS_TOOLTIPS_SAVEOPTIONS, IDS_TEXT_SAVEOPTIONS, uiIDA, 3, pInputData->UserIndex,&CScene_MultiGameJoinLoad::SaveOptionsDialogReturned,this, app.GetStringTable());
				}
				else
				{
					// delete the save game
					// Have to ask the player if they are sure they want to delete this game
					UINT uiIDA[2];
					uiIDA[0]=IDS_CONFIRM_CANCEL;
					uiIDA[1]=IDS_CONFIRM_OK;
					StorageManager.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, pInputData->UserIndex,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,this, app.GetStringTable());
				}
			}
			CXuiSceneBase::PlayUISFX(eSFX_Press);

		}
		else if(DoesMashUpWorldHaveFocus())
		{
			// hiding a mash-up world
			// get the mash-up pack id
			CXuiControl pItem;
			int iIndex;
			iIndex=m_SavesList.GetCurSel(&pItem);

			CXuiCtrl4JList::LIST_ITEM_INFO info = m_pSavesList->GetData(iIndex);
			if((iIndex != JOIN_LOAD_CREATE_BUTTON_INDEX) && (info.iData >= 0))
			{
				LevelGenerationOptions *levelGen = m_generators->at(info.iData);

				if(!levelGen->isTutorial())
				{
					if(levelGen->requiresTexturePack())
					{
						unsigned int uiPackID=levelGen->getRequiredTexturePackId();

						m_bIgnoreInput = true;
						app.HideMashupPackWorld(m_iPad,uiPackID);

						// update the saves list
						m_pSavesList->RemoveAllData();
						m_iSaveInfoC=0;
						GetSaveInfo();
						m_bIgnoreInput = false;
					}
				}
			}

			CXuiSceneBase::PlayUISFX(eSFX_Press);

		}
		break;
	case VK_PAD_LSHOULDER:
		if( m_bInParty )
		{
			m_bShowingPartyGamesOnly = !m_bShowingPartyGamesOnly;
			UpdateGamesList();
			CXuiSceneBase::PlayUISFX(eSFX_Press);
		}
		break;
	}
	
	return hr;
}

HRESULT CScene_MultiGameJoinLoad::OnNavReturn(HXUIOBJ hSceneFrom,BOOL& rfHandled)
{
	
	CXuiSceneBase::ShowLogo( DEFAULT_XUI_MENU_USER, TRUE );
	// start the texture pack timer again
	XuiSetTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME);

	m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); 
	
	// re-enable button presses
	m_bIgnoreInput=false;

	if( m_bMultiplayerAllowed )
	{
		HXUICLASS hClassFullscreenProgress = XuiFindClass( L"CScene_FullscreenProgress" );
		HXUICLASS hClassConnectingProgress = XuiFindClass( L"CScene_ConnectingProgress" );

		// If we are navigating back from a full screen progress scene, then that means a connection attempt failed
		if( XuiIsInstanceOf( hSceneFrom, hClassFullscreenProgress ) || XuiIsInstanceOf( hSceneFrom, hClassConnectingProgress ) )
		{
			UpdateGamesList();
		}
	}
	else
	{
		m_pGamesList->RemoveAllData();
		//m_GamesList.DeleteItems(0, m_GamesList.GetItemCount() );
		m_pGamesList->SetEnable(FALSE);
		//XuiElementSetDisableFocusRecursion( m_pGamesList->m_hObj, TRUE);
		m_NetGamesListTimer.SetShow( TRUE );
		m_LabelNoGames.SetShow( FALSE );
		m_SavesList.InitFocus(m_iPad);
	}

	// are we back here because of a delete of a corrupt save?

	if(app.GetCorruptSaveDeleted())
	{
		// need to re-get the saves list and update the display
		// clear the saves list
		m_pSavesList->RemoveAllData();
		m_iSaveInfoC=0;
		GetSaveInfo();
		app.SetCorruptSaveDeleted(false);
	}

	int iY = -1;
	int iRB=-1;
	if( DoesGamesListHaveFocus() )
	{
		iY = IDS_TOOLTIPS_VIEW_GAMERCARD;
	}
	else if(DoesSavesListHaveFocus())
	{	
		if(ProfileManager.IsSignedInLive( m_iPad ))
		{
			iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE;
		}

		if(StorageManager.GetSaveDisabled())
		{
			iRB=IDS_TOOLTIPS_DELETESAVE;
		}
		else
		{
			// 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename)

			if(StorageManager.EnoughSpaceForAMinSaveGame())
			{
				iRB=IDS_TOOLTIPS_SAVEOPTIONS;
			}
			else
			{
				iRB=IDS_TOOLTIPS_DELETESAVE;

			}
		}
	}
	else if(DoesMashUpWorldHaveFocus())
	{
		// If it's a mash-up pack world, give the Hide option
		iRB=IDS_TOOLTIPS_HIDE;
	}

	int iLB = -1;
	if(m_bInParty)
	{
		if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES;
		else iLB = IDS_TOOLTIPS_PARTY_GAMES;
	}

	if(ProfileManager.IsFullVersion()==false )
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, -1,-1,-1,iLB);
	}
	else if(StorageManager.GetSaveDisabled())
	{
		// clear out the saves list, since the disable save may have happened in the load screen because of a device removal

		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB);
	}
	else
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB);
	}

	return S_OK;
}

HRESULT CScene_MultiGameJoinLoad::OnNotifySelChanged(HXUIOBJ hObjSource, XUINotifySelChanged *pNotifySelChangedData, BOOL& bHandled)
{

	if(m_bReady)
	{
		CXuiSceneBase::PlayUISFX(eSFX_Focus);
	}

	return S_OK;
}


HRESULT CScene_MultiGameJoinLoad::OnTransitionStart( XUIMessageTransition *pTransition, BOOL& bHandled )
{
	//if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ) return S_OK;

	if(pTransition->dwTransAction==XUI_TRANSITION_ACTION_DESTROY ||
		pTransition->dwTransType == XUI_TRANSITION_FROM  || pTransition->dwTransType == XUI_TRANSITION_BACKFROM)
	{
		// 4J Stu - We may have had to unload our font renderer in this scene if one of the save files
		// uses characters not in our font (eg asian chars) so restore our font renderer
		// This will not do anything if our font renderer is already loaded
		app.OverrideFontRenderer(true,true);

		KillTimer(JOIN_LOAD_ONLINE_TIMER_ID);
	}
	else if(pTransition->dwTransType == XUI_TRANSITION_TO || pTransition->dwTransType == XUI_TRANSITION_BACKTO)
	{
		SetTimer(JOIN_LOAD_ONLINE_TIMER_ID,JOIN_LOAD_ONLINE_TIMER_TIME);
		// 4J-PB - Need to check for installed DLC, which might have happened while you were on the info scene
		if(pTransition->dwTransType == XUI_TRANSITION_BACKTO)
		{
			// Can't call this here because if you back out of the load info screen and then go back in and load a game, it will attempt to use the dlc as it's running a mount of the dlc
			
			// block input if we're waiting for DLC to install, and wipe the saves list. The end of dlc mounting custom message will fill the list again
			if(app.StartInstallDLCProcess(m_iPad)==false)
			{
				// not doing a mount, so re-enable input
				m_bIgnoreInput=false;
			}
			else
			{
				m_bIgnoreInput=true;
				m_pSavesList->RemoveAllData();
				m_SavesListTimer.SetShow( TRUE );
			}
		}
	}

	return S_OK;
}

HRESULT CScene_MultiGameJoinLoad::OnFontRendererChange()
{
	// update the tooltips
	// if the saves list has focus, then we should show the Delete Save tooltip
	// if the games list has focus, then we should the the View Gamercard tooltip
	int iRB=-1;	
	int iY = -1;
	if( DoesGamesListHaveFocus() )
	{
		iY = IDS_TOOLTIPS_VIEW_GAMERCARD;
	}	
	else if(DoesSavesListHaveFocus())
	{	
		if(ProfileManager.IsSignedInLive( m_iPad ))
		{
			iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE;
		}
		if(StorageManager.GetSaveDisabled())
		{
			iRB=IDS_TOOLTIPS_DELETESAVE;
		}
		else
		{
			if(StorageManager.EnoughSpaceForAMinSaveGame())
			{
				iRB=IDS_TOOLTIPS_SAVEOPTIONS;
			}
			else
			{
				iRB=IDS_TOOLTIPS_DELETESAVE;
			}
		}
	}
	else if(DoesMashUpWorldHaveFocus())
	{
		// If it's a mash-up pack world, give the Hide option
		iRB=IDS_TOOLTIPS_HIDE;
	}

	int iLB = -1;
	if(m_bInParty)
	{
		if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES;
		else iLB = IDS_TOOLTIPS_PARTY_GAMES;
	}

	if(ProfileManager.IsFullVersion()==false )
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, iY,-1,-1,iLB,-1,-1,true);
	}
	else if(StorageManager.GetSaveDisabled())
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB,-1,true);
	}
	else
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB,-1,true);
	}
	return S_OK;
}

HRESULT CScene_MultiGameJoinLoad::OnNotifySetFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled)
{
	// update the tooltips
	// if the saves list has focus, then we should show the Delete Save tooltip
	// if the games list has focus, then we should the the View Gamercard tooltip
	int iRB=-1;	
	int iY = -1;
	if( DoesGamesListHaveFocus() )
	{
		iY = IDS_TOOLTIPS_VIEW_GAMERCARD;
	}	
	else if(DoesSavesListHaveFocus())
	{
		if(ProfileManager.IsSignedInLive( m_iPad ))
		{
			iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE;
		}
		if(StorageManager.GetSaveDisabled())
		{
			iRB=IDS_TOOLTIPS_DELETESAVE;
		}
		else
		{
			if(StorageManager.EnoughSpaceForAMinSaveGame())
			{
				iRB=IDS_TOOLTIPS_SAVEOPTIONS;
			}
			else
			{
				iRB=IDS_TOOLTIPS_DELETESAVE;
			}
		}
	}
	else if(DoesMashUpWorldHaveFocus())
	{
		// If it's a mash-up pack world, give the Hide option
		iRB=IDS_TOOLTIPS_HIDE;
	}

	int iLB = -1;
	if(m_bInParty)
	{
		if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES;
		else iLB = IDS_TOOLTIPS_PARTY_GAMES;
	}

	if(ProfileManager.IsFullVersion()==false )
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, iY,-1,-1,iLB,-1);
	}
	else if(StorageManager.GetSaveDisabled())
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB);
	}
	else
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB);
	}
	return S_OK;
}

HRESULT CScene_MultiGameJoinLoad::OnNotifyKillFocus(HXUIOBJ hObjSource, XUINotifyFocus *pNotifyFocusData, BOOL& bHandled)
{
	return S_OK;
}

bool CScene_MultiGameJoinLoad::DoesSavesListHaveFocus()
{
	HXUIOBJ hParentObj,hObj=TreeGetFocus();

	if(hObj!=NULL)
	{
		// get the parent and see if it's the saves list
		XuiElementGetParent(hObj,&hParentObj);
		if(hParentObj==m_SavesList.m_hObj)
		{
			// check it's not the first or second element (new world or tutorial)
			if(m_SavesList.GetCurSel()>(m_iDefaultButtonsC-1))
			{
				return true;
			}
		}
	}
	return false;
}

bool CScene_MultiGameJoinLoad::DoesMashUpWorldHaveFocus()
{
	HXUIOBJ hParentObj,hObj=TreeGetFocus();

	if(hObj!=NULL)
	{
		// get the parent and see if it's the saves list
		XuiElementGetParent(hObj,&hParentObj);
		if(hParentObj==m_SavesList.m_hObj)
		{
			// check it's not the first or second element (new world or tutorial)
			if(m_SavesList.GetCurSel()>(m_iDefaultButtonsC-1))
			{
				return false;
			}

			if(m_SavesList.GetCurSel()>(m_iDefaultButtonsC - 1 - m_iMashUpButtonsC))
			{
				return true;
			}
			else return false;
		}
		else return false;
	}
	return false;
}

bool CScene_MultiGameJoinLoad::DoesGamesListHaveFocus()
{
	HXUIOBJ hParentObj,hObj=TreeGetFocus();

	if(hObj!=NULL)
	{
		// get the parent and see if it's the saves list
		XuiElementGetParent(hObj,&hParentObj);
		if(hParentObj==m_pGamesList->m_hObj)
		{
			return true;
		}
	}
	return false;
}

void CScene_MultiGameJoinLoad::UpdateGamesListCallback(LPVOID lpParam)
{
	if(lpParam != NULL)
	{
		CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad *) lpParam;
		// check this there's no save transfer in progress
		if(!pClass->m_bSaveTransferInProgress)
		{
				pClass->UpdateGamesList();	
		}
	}
}

void CScene_MultiGameJoinLoad::UpdateGamesList()
{
	if( m_bIgnoreInput ) return;

	// if we're retrieving save info, don't show the list yet as we will be ignoring press events
	if(m_bRetrievingSaveInfo)
	{
		return;
	}

	DWORD nIndex = -1;
	FriendSessionInfo *pSelectedSession = NULL;
	if(m_pGamesList->TreeHasFocus() && m_pGamesList->GetItemCount() > 0)
	{
		nIndex = m_pGamesList->GetCurSel();
		pSelectedSession = currentSessions.at( nIndex );
	}

	SessionID selectedSessionId;
	if( pSelectedSession != NULL )selectedSessionId = pSelectedSession->sessionId;
	pSelectedSession = NULL;

	for(AUTO_VAR(it, currentSessions.begin()); it < currentSessions.end(); ++it)
	{
		delete (*it);
	}
	currentSessions.clear();
	
	m_NetGamesListTimer.SetShow( FALSE );
		
	// if the saves list has focus, then we should show the Delete Save tooltip
	// if the games list has focus, then we should show the View Gamercard tooltip
	int iRB=-1;	
	int iY = -1;

	if( DoesGamesListHaveFocus() )
	{
		iY = IDS_TOOLTIPS_VIEW_GAMERCARD;
	}
	else if(DoesSavesListHaveFocus())
	{	
		if(ProfileManager.IsSignedInLive( m_iPad ))
		{
			iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE;
		}
		if(StorageManager.GetSaveDisabled())
		{
			iRB=IDS_TOOLTIPS_DELETESAVE;
		}
		else
		{
			if(StorageManager.EnoughSpaceForAMinSaveGame())
			{
				iRB=IDS_TOOLTIPS_SAVEOPTIONS;
			}
			else
			{
				iRB=IDS_TOOLTIPS_DELETESAVE;
			}
		}
	}
	else if(DoesMashUpWorldHaveFocus())
	{
		// If it's a mash-up pack world, give the Hide option
		iRB=IDS_TOOLTIPS_HIDE;
	}

	int iLB = -1;
	if(m_bInParty)
	{
		if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES;
		else iLB = IDS_TOOLTIPS_PARTY_GAMES;
	}

	if(ProfileManager.IsFullVersion()==false )
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, iY,-1,-1,iLB,-1);
	}
	else if(StorageManager.GetSaveDisabled())
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB);
	}
	else
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB);
	}

	currentSessions = *g_NetworkManager.GetSessionList( m_iPad, m_localPlayers, m_bShowingPartyGamesOnly );

	// Update the xui list displayed
	unsigned int xuiListSize = m_pGamesList->GetItemCount();
	unsigned int filteredListSize = (unsigned int)currentSessions.size();

	BOOL gamesListHasFocus = m_pGamesList->TreeHasFocus();

	if(filteredListSize > 0)
	{
		if( !m_pGamesList->IsEnabled() )
		{
			m_pGamesList->SetEnable(TRUE);
			//XuiElementSetDisableFocusRecursion( m_pGamesList->m_hObj, FALSE);
			m_pGamesList->SetCurSel( 0 );
		}
		m_LabelNoGames.SetShow( FALSE );
		m_NetGamesListTimer.SetShow( FALSE );
	}
	else
	{
		m_pGamesList->SetEnable(FALSE);
		//XuiElementSetDisableFocusRecursion(m_pGamesList->m_hObj, TRUE);
		m_NetGamesListTimer.SetShow( FALSE );
		m_LabelNoGames.SetShow( TRUE );

		if( gamesListHasFocus ) m_pGamesList->InitFocus(m_iPad);
	}

	// clear out the games list and re-fill
	m_pGamesList->RemoveAllData();

	if( filteredListSize > 0 )
	{
		// Reset the focus to the selected session if it still exists
		unsigned int sessionIndex = 0;
		m_pGamesList->SetCurSel(0);

		for( AUTO_VAR(it, currentSessions.begin()); it < currentSessions.end(); ++it)
		{
			FriendSessionInfo *sessionInfo = *it;
			HXUIBRUSH hXuiBrush;
			CXuiCtrl4JList::LIST_ITEM_INFO ListInfo;

			ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO));

			ListInfo.pwszText = sessionInfo->displayLabel;
			ListInfo.fEnabled = TRUE;
			ListInfo.iData = sessionIndex;
			m_pGamesList->AddData(ListInfo);
			// display an icon too

			// Is this a default game or a texture pack game?
			if(sessionInfo->data.texturePackParentId!=0)
			{
				// Do we have the texture pack
				Minecraft *pMinecraft = Minecraft::GetInstance();
				TexturePack *tp = pMinecraft->skins->getTexturePackById(sessionInfo->data.texturePackParentId);
				HRESULT hr;

				DWORD dwImageBytes=0;
				PBYTE pbImageData=NULL;

				if(tp==NULL)
				{
					DWORD dwBytes=0;
					PBYTE pbData=NULL;
					app.GetTPD(sessionInfo->data.texturePackParentId,&pbData,&dwBytes);

					// is it in the tpd data ?
					app.GetFileFromTPD(eTPDFileType_Icon,pbData,dwBytes,&pbImageData,&dwImageBytes );
					if(dwImageBytes > 0 && pbImageData)
					{	
						hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush);
						m_pGamesList->UpdateGraphic(sessionIndex,hXuiBrush);
					}
				}
				else
				{
					pbImageData = tp->getPackIcon(dwImageBytes);
					if(dwImageBytes > 0 && pbImageData)
					{			
						hr=XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush);
						m_pGamesList->UpdateGraphic(sessionIndex,hXuiBrush);
					}
				}
			}
			else
			{
				// default texture pack
				XuiCreateTextureBrushFromMemory(m_DefaultMinecraftIconData,m_DefaultMinecraftIconSize,&hXuiBrush);
				m_pGamesList->UpdateGraphic(sessionIndex,hXuiBrush);
			}
			

			if(memcmp( &selectedSessionId, &sessionInfo->sessionId, sizeof(SessionID) ) == 0)
			{
				m_pGamesList->SetCurSel(sessionIndex);
				break;
			}
			++sessionIndex;
		}
	}
}

void CScene_MultiGameJoinLoad::UpdateGamesList(DWORD dwNumResults, IQNetGameSearch *pGameSearch)
{
	// We don't use the QNet callback, but could resurrect this if we ever do normal matchmaking, but updated to work as the function above
#if 0
	const XSESSION_SEARCHRESULT *pSearchResult;
    const XNQOSINFO * pxnqi;

	if(m_searches>0)
		--m_searches;
	
	if(m_searches==0)
	{
		m_NetGamesListTimer.SetShow( FALSE );
		
		// if the saves list has focus, then we should show the Delete Save tooltip
		// if the games list has focus, then we should show the View Gamercard tooltip
		int iRB=-1;	
		int iY = -1;

		if( DoesGamesListHaveFocus() )
		{
			iY = IDS_TOOLTIPS_VIEW_GAMERCARD;
		}
		else if(DoesSavesListHaveFocus())
		{
			iRB=IDS_TOOLTIPS_DELETESAVE;
		}

		int iLB = -1;
		if(m_bInParty)
		{
			if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES
			else iLB = IDS_TOOLTIPS_PARTY_GAMES;
		}

		if(ProfileManager.IsFullVersion()==false )
		{
			ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, iY,-1,-1,iLB,iRB);
		}
		else if(StorageManager.GetSaveDisabled())
		{
			ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB);
		}
		else
		{
			ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE, iY,-1,-1,iLB,iRB);
		}
	}

	if( dwNumResults == 0 )
	{
		if(m_searches==0 && m_GamesList.GetItemCount() == 0)
		{
			m_LabelNoGames.SetShow( TRUE );
		}
		return;
	}

	unsigned int startOffset = m_GamesList.GetItemCount();
	//m_GamesList.InsertItems(startOffset,dwNumResults);	
	//m_GamesList.SetEnable(TRUE);
	//XuiElementSetDisableFocusRecursion( m_GamesList.m_hObj, FALSE);

	// Loop through all the results.
    for( DWORD dwResult = 0; dwResult < pGameSearch->GetNumResults(); dwResult++ )
    {

        pSearchResult = pGameSearch->GetSearchResultAtIndex( dwResult );

		// No room for us, so ignore it
		if(pSearchResult->dwOpenPublicSlots < m_localPlayers)
			continue;

		FriendSessionInfo *sessionInfo = NULL;
		bool foundSession = false;
		for(AUTO_VAR(it, friendsSessions.begin()); it < friendsSessions.end(); ++it)
		{
			sessionInfo = *it;
			if(memcmp( &pSearchResult->info.sessionID, &sessionInfo->sessionId, sizeof(SessionID) ) == 0)
			{
				sessionInfo->searchResult = *pSearchResult;
				sessionInfo->displayLabel = new wchar_t[100];
				foundSession = true;
				break;
			}
		}

		// We received a search result for a session no longer in our list of friends sessions
		if(!foundSession)
			continue;

        // Print some info about this result.
        //printf( "Search result %u:\n", dwResult );
        //printf( "    public slots open = %u, filled = %u\n", pSearchResult->dwOpenPublicSlots, pSearchResult->dwFilledPublicSlots );
        //printf( "    private slots open = %u, filled = %u\n", pSearchResult->dwOpenPrivateSlots, pSearchResult->dwFilledPrivateSlots );

        // See if this result was contacted successfully via QoS probes.
        pxnqi = pGameSearch->GetQosInfoAtIndex( dwResult );
        if( pxnqi->bFlags & XNET_XNQOSINFO_TARGET_CONTACTED )
        {
            // Print the round trip time and the rough estimation of
            // bandwidth.
            app.DebugPrintf( "    RTT min = %u, med = %u\n", pxnqi->wRttMinInMsecs, pxnqi->wRttMedInMsecs );
            app.DebugPrintf( "    bps up = %u, down = %u\n", pxnqi->dwUpBitsPerSec, pxnqi->dwDnBitsPerSec );

			if(pxnqi->cbData > 0)
			{
				sessionInfo->data = *(GameSessionData *)pxnqi->pbData;

				wstring gamerName = convStringToWstring(sessionInfo->data.hostName);
				swprintf(sessionInfo->displayLabel,L"%ls's Game", gamerName.c_str() );
			}
			else
			{
				swprintf(sessionInfo->displayLabel,L"Unknown host Game");
			}

            // If this host wasn't disabled use this one.
            if( !( pxnqi->bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) && sessionInfo->data.netVersion == MINECRAFT_NET_VERSION )
            {
				//printf("This game is valid\n");
				filteredResults.push_back(sessionInfo);
				m_GamesList.InsertItems(startOffset,1);
				m_GamesList.SetText(startOffset,sessionInfo->displayLabel);
				startOffset++;
            }
#ifndef _CONTENT_PACKAGE
			if( sessionInfo->data.netVersion != MINECRAFT_NET_VERSION )
			{
				wprintf(L"%ls version of %d does not match our version of %d\n", sessionInfo->displayLabel, sessionInfo->data.netVersion, MINECRAFT_NET_VERSION);
			}
#endif
        }
    }
	
	if( m_GamesList.GetItemCount() == 0)
	{
		m_LabelNoGames.SetShow( TRUE );
	}
	else
	{
		m_GamesList.SetEnable(TRUE);
		XuiElementSetDisableFocusRecursion( m_GamesList.m_hObj, FALSE);
		if( DoesGamesListHaveFocus() )
		{
			m_GamesList.SetCurSel(0);
		}
	}
#endif
}

/*void CScene_MultiGameJoinLoad::UpdateGamesListLabels()
{
	for( unsigned int i = 0; i < currentSessions.size(); ++i )
	{
		FriendSessionInfo *sessionInfo = currentSessions.at(i);
		m_GamesList.SetText(i,sessionInfo->displayLabel);
		HXUIBRUSH hBrush;
		CXuiCtrl4JList::LIST_ITEM_INFO info = m_pGamesList->GetData(i);

		// display an icon too
		XuiCreateTextureBrushFromMemory(m_DefaultMinecraftIconData,m_DefaultMinecraftIconSize,&hBrush);
		m_pGamesList->UpdateGraphic(i,hBrush);
	}
#if 0
	XUIRect xuiRect;
	HXUIOBJ item = XuiListGetItemControl(m_GamesList,0);

	HXUIOBJ hObj=NULL;
	HXUIOBJ hTextPres=NULL;
 	HRESULT hr=XuiControlGetVisual(item,&hObj);
	hr=XuiElementGetChildById(hObj,L"text_Label",&hTextPres);

	unsigned char displayLabelViewableStartIndex = 0;
	for( unsigned int i = 0; i < currentSessions.size(); ++i )
	{
		FriendSessionInfo *sessionInfo = currentSessions.at(i);

		if(hTextPres != NULL )
		{
			hr=XuiTextPresenterMeasureText(hTextPres, sessionInfo->displayLabel, &xuiRect);

			float fWidth, fHeight;
			XuiElementGetBounds(hTextPres,&fWidth,&fHeight);
			int characters = (fWidth/xuiRect.right) * sessionInfo->displayLabelLength;

			if( characters < sessionInfo->displayLabelLength )
			{
				static wchar_t temp[100];
				ZeroMemory(temp, (100)*sizeof(wchar_t));
				wcsncpy_s( temp, sessionInfo->displayLabel+sessionInfo->displayLabelViewableStartIndex, characters );
				m_GamesList.SetText(i,temp);
				sessionInfo->displayLabelViewableStartIndex++;
				if( sessionInfo->displayLabelViewableStartIndex >= sessionInfo->displayLabelLength ) sessionInfo->displayLabelViewableStartIndex = 0;
			}
		}
	}
#endif
}*/

void CScene_MultiGameJoinLoad::SearchForGameCallback(void *param, DWORD dwNumResults, IQNetGameSearch *pGameSearch)
{
#if 0
	HXUIOBJ hObj = (HXUIOBJ)param;

	void *pObj;
	XuiObjectFromHandle( hObj, &pObj);
	CScene_MultiGameJoinLoad *MultiGameJoinLoad = (CScene_MultiGameJoinLoad *)pObj;

	MultiGameJoinLoad->UpdateGamesList(dwNumResults, pGameSearch);
#endif
}

int CScene_MultiGameJoinLoad::DeviceSelectReturned(void *pParam,bool bContinue)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;
	//HRESULT hr;

	if(bContinue==true)
	{
		// if the saves list has focus, then we should show the Delete Save tooltip
		// if the games list has focus, then we should show the View Gamercard tooltip
		int iRB=-1;	
		int iY = -1;
		if( pClass->DoesGamesListHaveFocus() )
		{
			iY = IDS_TOOLTIPS_VIEW_GAMERCARD;
		}
		else if(pClass->DoesSavesListHaveFocus())
		{	
			if(ProfileManager.IsSignedInLive( pClass->m_iPad ))
			{
				iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE;
			}
			if(StorageManager.GetSaveDisabled())
			{
				iRB=IDS_TOOLTIPS_DELETESAVE;
			}
			else
			{
				if(StorageManager.EnoughSpaceForAMinSaveGame())
				{
					iRB=IDS_TOOLTIPS_SAVEOPTIONS;
				}
				else
				{
					iRB=IDS_TOOLTIPS_DELETESAVE;
				}
			}
		}
		else if(pClass->DoesMashUpWorldHaveFocus())
		{
			// If it's a mash-up pack world, give the Hide option
			iRB=IDS_TOOLTIPS_HIDE;
		}

		int iLB = -1;
		if(pClass->m_bInParty)
		{
			if( pClass->m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES;
			else iLB = IDS_TOOLTIPS_PARTY_GAMES;
		}

		//BOOL bOnlineGame=pClass->m_CheckboxOnline.IsChecked();

		// refresh the saves list (if there is a device selected)

		// clear out the list first

		if(StorageManager.GetSaveDisabled())
		{
			if(StorageManager.GetSaveDeviceSelected(pClass->m_iPad))
			{
				ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,iLB,iRB);
				// saving is disabled, but we should still be able to load from a selected save device
				pClass->GetSaveInfo();
			}
			else
			{		
				ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB,iRB);
				// clear the saves list
				pClass->m_pSavesList->RemoveAllData();

				pClass->m_iSaveInfoC=0;
				//pClass->m_iThumbnailsLoadedC=0;

				pClass->AddDefaultButtons();

				pClass->m_SavesListTimer.SetShow( FALSE );

				pClass->m_pSavesList->SetCurSelVisible(0);
			}
		}
		else
		{
			ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,iLB,iRB);
			pClass->GetSaveInfo();
		}
	}

	// enable input again
	pClass->m_bIgnoreInput=false;

	return 0;
}

HRESULT CScene_MultiGameJoinLoad::OnTimer( XUIMessageTimer *pTimer, BOOL& bHandled )
{
	// 4J-PB - TODO - Don't think we can do this - if a 2nd player signs in here with an offline profile, the signed in LIVE player gets re-logged in, and bMultiplayerAllowed is false briefly 
	switch(pTimer->nId)
	{

	
	case JOIN_LOAD_ONLINE_TIMER_ID:
	{
		XPARTY_USER_LIST partyList;

		if((XPartyGetUserList(  &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1))
		{
			m_bInParty=true;
		}
		else
		{
			m_bInParty=false;
		}

		bool bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad);
		if(bMultiplayerAllowed != m_bMultiplayerAllowed)
		{
			if( bMultiplayerAllowed )
			{
// 					m_CheckboxOnline.SetEnable(TRUE);
// 					m_CheckboxPrivate.SetEnable(TRUE);
			}
			else
			{
				m_bInParty = false;
				m_pGamesList->RemoveAllData();
				//m_GamesList.DeleteItems(0, m_GamesList.GetItemCount() );
				m_pGamesList->SetEnable(FALSE);
				//XuiElementSetDisableFocusRecursion( m_pGamesList->m_hObj, TRUE);
				m_NetGamesListTimer.SetShow( TRUE );
				m_LabelNoGames.SetShow( FALSE );
			}

			int iLB = -1;
			if(m_bInParty)
			{
				if( m_bShowingPartyGamesOnly ) iLB = IDS_TOOLTIPS_ALL_GAMES;
				else iLB = IDS_TOOLTIPS_PARTY_GAMES;
			}
			int iRB=-1;
			int iY=-1;

			if( DoesGamesListHaveFocus() )
			{
			}
			else if(DoesSavesListHaveFocus())
			{	
				if(ProfileManager.IsSignedInLive( m_iPad ))
				{
					iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE;
				}

				if(StorageManager.GetSaveDisabled())
				{
					iRB=IDS_TOOLTIPS_DELETESAVE;
				}
				else
				{
					if(StorageManager.EnoughSpaceForAMinSaveGame())
					{
						iRB=IDS_TOOLTIPS_SAVEOPTIONS;
					}
					else
					{
						iRB=IDS_TOOLTIPS_DELETESAVE;
					}
				}
			}
			else if(DoesMashUpWorldHaveFocus())
			{
				// If it's a mash-up pack world, give the Hide option
				iRB=IDS_TOOLTIPS_HIDE;
			}

			if(ProfileManager.IsFullVersion()==false )
			{
				ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, -1, -1,-1,-1,iLB);
			}
			else if(StorageManager.GetSaveDisabled())
			{
				ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,-1,-1,-1,iLB,iRB);
			}
			else
			{
				ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,iLB,iRB);
			}
			m_bMultiplayerAllowed = bMultiplayerAllowed;
		}
	}
	break;
	case JOIN_LOAD_SEARCH_MINIMUM_TIMER_ID:
	{
		XuiKillTimer( m_hObj, JOIN_LOAD_SEARCH_MINIMUM_TIMER_ID );
		m_NetGamesListTimer.SetShow( FALSE );
		m_LabelNoGames.SetShow( TRUE );
	}
	break;
	case JOIN_LOAD_SCROLL_GAME_NAMES_TIMER_ID:
	{
		// This is called by the gameslist callback function, so isn't needed on a timer
		//UpdateGamesListLabels();
	}
	break;
		case CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID:
			{
				// also check for any new texture packs info being available
				// for each item in the mem list, check it's in the data list

				//CXuiCtrl4JList::LIST_ITEM_INFO ListInfo;
				// for each iConfig, check if the data is available, and add it to the List, then remove it from the viConfig
				
				for(int i=0;i<m_iTexturePacksNotInstalled;i++)
				{
					if(m_iConfigA[i]!=-1)
					{
						DWORD dwBytes=0;
						PBYTE pbData=NULL;
						//app.DebugPrintf("Retrieving iConfig %d from TPD\n",m_iConfigA[i]);

						app.GetTPD(m_iConfigA[i],&pbData,&dwBytes);

						if(dwBytes > 0 && pbData)
						{
							//update the games list
							UpdateGamesList();

							m_iConfigA[i]=-1;
						}
					}
				}
				bool bAllDone=true;
				for(int i=0;i<m_iTexturePacksNotInstalled;i++)
				{
					if(m_iConfigA[i]!=-1) 
					{
						bAllDone = false;
					}
				}

				if(bAllDone)
				{
					// kill this timer
					XuiKillTimer(m_hObj,CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID);
				}
			}
			break;
			}

	return S_OK;
}

/*
int CScene_MultiGameJoinLoad::LoadSaveDataReturned(void *pParam,bool bContinue)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;

	if(bContinue==true)
	{
		bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad());

		// 4J Stu - If we only have one controller connected, then don't show the sign-in UI again
		DWORD connectedControllers = 0;
		for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i)
		{
			if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) ++connectedControllers;
		}

		if(!isClientSide || connectedControllers == 1 || !RenderManager.IsHiDef())
		{
			DWORD dwLocalUsersMask = CGameNetworkManager::GetLocalPlayerMask(ProfileManager.GetPrimaryPad());

			// No guest problems so we don't need to force a sign-in of players here
			StartGameFromSave(pClass, dwLocalUsersMask);
		}
		else
		{
			ProfileManager.RequestSignInUI(false, false, false, true, false,&CScene_MultiGameJoinLoad::StartGame_SignInReturned, pParam,ProfileManager.GetPrimaryPad());
		}
	}
	else
	{
		pClass->m_bIgnoreInput=false;
	}
	return 0;
}
*/

int CScene_MultiGameJoinLoad::StartGame_SignInReturned(void *pParam,bool bContinue, int iPad)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;

	if(bContinue==true)
	{
		// It's possible that the player has not signed in - they can back out
		if(ProfileManager.IsSignedIn(iPad))
		{
			DWORD dwLocalUsersMask = 0;

			for(unsigned int index = 0; index < XUSER_MAX_COUNT; ++index)
			{
				if(ProfileManager.IsSignedIn(index) )
				{
					dwLocalUsersMask |= CGameNetworkManager::GetLocalPlayerMask(index);
				}
			}
			StartGameFromSave(pClass, dwLocalUsersMask);
		}
	}
	else
	{
		pClass->m_bIgnoreInput=false;
	}
	return 0;
}

// 4J Stu - Shared functionality that is the same whether we needed a quadrant sign-in or not
void CScene_MultiGameJoinLoad::StartGameFromSave(CScene_MultiGameJoinLoad* pClass, DWORD dwLocalUsersMask)
{
	/*bool isClientSide = ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && pClass->m_CheckboxOnline.IsChecked() == TRUE;
	//bool isPrivate = pClass->m_CheckboxPrivate.IsChecked() == TRUE;

	SenStatGameEvent(ProfileManager.GetPrimaryPad(),eTelemetryGameEvent_Load,Minecraft::GetInstance()->options->difficulty, isClientSide, ProfileManager.IsFullVersion(), 1,0 );

	g_NetworkManager.HostGame(dwLocalUsersMask,isClientSide,isPrivate,MINECRAFT_NET_MAX_PLAYERS,0);

	LoadingInputParams *loadingParams = new LoadingInputParams();
	loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc;
	loadingParams->lpParam = NULL;

	UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData();
	completionData->bShowBackground=TRUE;
	completionData->bShowLogo=TRUE;
	completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes;
	completionData->iPad = DEFAULT_XUI_MENU_USER;
	loadingParams->completionData = completionData;

	app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams);*/
}

int CScene_MultiGameJoinLoad::DeleteSaveDataReturned(void *pParam,bool bSuccess)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;

	if(bSuccess==true)
	{
		// need to re-get the saves list and update the display
		// clear the saves list
		pClass->m_pSavesList->RemoveAllData();
		pClass->m_iSaveInfoC=0;
		pClass->GetSaveInfo();
	}
	
	pClass->m_bIgnoreInput=false;

	return 0;
}

void CScene_MultiGameJoinLoad::LoadLevelGen(LevelGenerationOptions *levelGen)
{	
	// Load data from disc
	//File saveFile( L"Tutorial\\Tutorial" );
	//LoadSaveFromDisk(&saveFile);

	// clear out the app's terrain features list
	app.ClearTerrainFeaturePosition();
		
	StorageManager.ResetSaveData();
	// Make our next save default to the name of the level
	StorageManager.SetSaveTitle(levelGen->getDefaultSaveName().c_str());
	
	bool isClientSide = false;
	bool isPrivate = false;
	int maxPlayers = MINECRAFT_NET_MAX_PLAYERS;

	if( app.GetTutorialMode() )
	{
		isClientSide = false;
		maxPlayers = 4;
	}

	g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0);

	NetworkGameInitData *param = new NetworkGameInitData();
	param->seed = 0;
	param->saveData = NULL;
	param->settings = app.GetGameHostOption( eGameHostOption_Tutorial );
	param->levelGen = levelGen;

	if(levelGen->requiresTexturePack())
	{
		param->texturePackId = levelGen->getRequiredTexturePackId();
	
		Minecraft *pMinecraft = Minecraft::GetInstance();
		pMinecraft->skins->selectTexturePackById(param->texturePackId);
		//pMinecraft->skins->updateUI();
	}

	LoadingInputParams *loadingParams = new LoadingInputParams();
	loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc;
	loadingParams->lpParam = (LPVOID)param;

	UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData();
	completionData->bShowBackground=TRUE;
	completionData->bShowLogo=TRUE;
	completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes;
	completionData->iPad = DEFAULT_XUI_MENU_USER;
	loadingParams->completionData = completionData;

	app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams);
}

void CScene_MultiGameJoinLoad::LoadSaveFromDisk(File *saveFile)
{	
	// we'll only be coming in here when the tutorial is loaded now

	StorageManager.ResetSaveData();

	// Make our next save default to the name of the level
	StorageManager.SetSaveTitle(saveFile->getName().c_str());

	__int64 fileSize = saveFile->length();
	FileInputStream fis(*saveFile);
	byteArray ba(fileSize);
	fis.read(ba);
	fis.close();
	
	bool isClientSide = false;
	bool isPrivate = false;
	int maxPlayers = MINECRAFT_NET_MAX_PLAYERS;

	if( app.GetTutorialMode() )
	{
		isClientSide = false;
		maxPlayers = 4;
	}
	
	app.SetGameHostOption(eGameHostOption_GameType,GameType::CREATIVE->getId());

	g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0);

	LoadSaveDataThreadParam *saveData = new LoadSaveDataThreadParam(ba.data, ba.length, saveFile->getName());

	NetworkGameInitData *param = new NetworkGameInitData();
	param->seed = 0;
	param->saveData = saveData;
	param->settings = app.GetGameHostOption( eGameHostOption_All );

	LoadingInputParams *loadingParams = new LoadingInputParams();
	loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc;
	loadingParams->lpParam = (LPVOID)param;

	UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData();
	completionData->bShowBackground=TRUE;
	completionData->bShowLogo=TRUE;
	completionData->type = e_ProgressCompletion_CloseAllPlayersUIScenes;
	completionData->iPad = DEFAULT_XUI_MENU_USER;
	loadingParams->completionData = completionData;

	app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams);
}

int CScene_MultiGameJoinLoad::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;
	// results switched for this dialog
	if(result==C4JStorage::EMessage_ResultDecline) 
	{
		if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled())
		{
			pClass->m_bIgnoreInput=false;
		}
		else
		{
			XCONTENT_DATA XContentData;
			StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,XContentData);
			StorageManager.DeleteSaveData(&XContentData,CScene_MultiGameJoinLoad::DeleteSaveDataReturned,pClass);
			pClass->m_SavesListTimer.SetShow( TRUE );
		}
	}
	else
	{
		pClass->m_bIgnoreInput=false;
	}
	return 0;
}

int CScene_MultiGameJoinLoad::SaveTransferDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;
	// results switched for this dialog
	if(result==C4JStorage::EMessage_ResultAccept) 
	{
		// upload the save

		// first load the save
		int iIndex=pClass->m_pSavesList->GetData(pClass->m_pSavesList->GetCurSel()).iIndex-pClass->m_iDefaultButtonsC;
		XCONTENT_DATA ContentData;

		// 4J-PB - ensure we've switched to the right title group id for uploading to
		app.TMSPP_SetTitleGroupID(SAVETRANSFER_GROUP_ID);
		StorageManager.GetSaveCacheFileInfo(iIndex,ContentData);
		C4JStorage::ELoadGameStatus eLoadStatus=StorageManager.LoadSaveData(&ContentData,CScene_MultiGameJoinLoad::LoadSaveDataReturned,pClass);

		pClass->m_bIgnoreInput=false;
	}
	else
	{
		pClass->m_bIgnoreInput=false;
	}
	return 0;
}

int CScene_MultiGameJoinLoad::UploadSaveForXboxOneThreadProc( LPVOID lpParameter )
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad *) lpParameter;
	Minecraft *pMinecraft = Minecraft::GetInstance();	

	pMinecraft->progressRenderer->progressStart(IDS_SAVE_TRANSFER_TITLE);
	pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADING );

	// Delete the marker file
	DeleteFile(pClass, "completemarker");
	if(!WaitForTransferComplete(pClass)) return 0;

	// Upload the save data
	{
		unsigned int uiSaveBytes;
		uiSaveBytes=StorageManager.GetSaveSize();
		pClass->m_pbSaveTransferData=new BYTE [uiSaveBytes];

		StorageManager.GetSaveData(pClass->m_pbSaveTransferData,&uiSaveBytes);

		app.DebugPrintf("Uploading save data (%d bytes)\n", uiSaveBytes);
		UploadFile(pClass, "savedata", pClass->m_pbSaveTransferData, uiSaveBytes);
	}

	if(!WaitForTransferComplete(pClass)) return 0;
	if(pClass->m_bTransferFail)
	{
		// something went wrong, user has been informed
		pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADFAILED );
		return 0;
	}

	// Upload the metadata and thumbnail
	{
		ByteArrayOutputStream baos;
		DataOutputStream dos(&baos);

		LPCWSTR title = StorageManager.GetSaveTitle();
		dos.writeUTF(title);

		char szUniqueMapName[14];
		StorageManager.GetSaveUniqueFilename(szUniqueMapName);
		dos.writeUTF(convStringToWstring(szUniqueMapName));

		{
			// set the save icon
			PBYTE pbImageData=NULL;
			DWORD dwImageBytes=0;
			XCONTENT_DATA XContentData;
			int iIndex=pClass->m_pSavesList->GetData(pClass->m_pSavesList->GetCurSel()).iIndex-pClass->m_iDefaultButtonsC;
			StorageManager.GetSaveCacheFileInfo(iIndex,XContentData);
			StorageManager.GetSaveCacheFileInfo(iIndex,&pbImageData,&dwImageBytes);

			// if there is no thumbnail, retrieve the default one from the file. 
			// Don't delete the image data after creating the xuibrush, since we'll use it in the rename of the save
			if(pbImageData==NULL)
			{
				DWORD dwResult=XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&XContentData,NULL,&dwImageBytes,NULL);
				if(dwResult==ERROR_SUCCESS)
				{
					pClass->m_pbSaveTransferData = new BYTE[dwImageBytes];
					pbImageData = pClass->m_pbSaveTransferData; // Copy pointer so that we can use the same name as the library owned one, but m_pbSaveTransferData will get deleted when done
					XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&XContentData,pbImageData,&dwImageBytes,NULL);
				}
			}

			dos.writeInt(dwImageBytes);

			byteArray ba(pbImageData, dwImageBytes);
			dos.write(ba);
		}

		pClass->m_pbSaveTransferData=new BYTE [baos.size()];
		memcpy(pClass->m_pbSaveTransferData,baos.buf.data,baos.size());

		app.DebugPrintf("Uploading meta data (%d bytes)\n", baos.size());
		UploadFile(pClass, "metadata", pClass->m_pbSaveTransferData, baos.size());
	}

	// Wait for metadata and thumbnail
	if(!WaitForTransferComplete(pClass)) return 0;
	if(pClass->m_bTransferFail)
	{
		// something went wrong, user has been informed
		pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADFAILED );
		return 0;
	}

	// Upload the marker file
	{
		char singleByteData[1] = {1};
		app.DebugPrintf("Uploading marker (%d bytes)\n", 1);
		UploadFile(pClass, "completemarker", &singleByteData, 1);
	}

	// Wait for marker
	if(!WaitForTransferComplete(pClass)) return 0;
	if(pClass->m_bTransferFail)
	{
		// something went wrong, user has been informed
		pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADFAILED );

		return 0;
	}
	// change text for completion confirmation 
	pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_UPLOADCOMPLETE );

	// done
	return 0;
}

void CScene_MultiGameJoinLoad::DeleteFile(CScene_MultiGameJoinLoad *pClass, char *filename)
{
	pClass->m_fProgress=0.0f;
	pClass->m_bTransferComplete=false;

	C4JStorage::ETMSStatus result = StorageManager.TMSPP_DeleteFile(
		ProfileManager.GetPrimaryPad(),
		filename,
		C4JStorage::TMS_FILETYPE_BINARY,
		&CScene_MultiGameJoinLoad::DeleteComplete,
		pClass,
		NULL);

	if(result != C4JStorage::ETMSStatus_DeleteInProgress)
	{
		DeleteComplete(pClass,ProfileManager.GetPrimaryPad(), -1);
	}
}

void CScene_MultiGameJoinLoad::UploadFile(CScene_MultiGameJoinLoad *pClass, char *filename, LPVOID data, DWORD size)
{
	pClass->m_fProgress=0.0f;
	pClass->m_bTransferComplete=false;

	C4JStorage::ETMSStatus result = StorageManager.TMSPP_WriteFileWithProgress(
			ProfileManager.GetPrimaryPad(),
			C4JStorage::eGlobalStorage_TitleUser,
			C4JStorage::TMS_FILETYPE_BINARY,
			C4JStorage::TMS_UGCTYPE_NONE,
			filename,
			(CHAR *)data, 
			size,
			&CScene_MultiGameJoinLoad::TransferComplete,pClass, 0,
			&CScene_MultiGameJoinLoad::Progress,pClass);

#ifdef _DEBUG_MENUS_ENABLED
	if(app.GetWriteSavesToFolderEnabled())
	{
		File targetFileDir(L"GAME:\\FakeTMSPP");
		if(!targetFileDir.exists()) targetFileDir.mkdir();
		string path = string( wstringtofilename( targetFileDir.getPath() ) ).append("\\").append(filename);
		HANDLE hSaveFile = CreateFile( path.c_str(), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL);

		DWORD numberOfBytesWritten = 0;
		WriteFile( hSaveFile,data,size,&numberOfBytesWritten,NULL);
		assert(numberOfBytesWritten == size);

		CloseHandle(hSaveFile);
	}
#endif

	if(result != C4JStorage::ETMSStatus_WriteInProgress)
	{
		TransferComplete(pClass,ProfileManager.GetPrimaryPad(), -1);
	}
}

bool CScene_MultiGameJoinLoad::WaitForTransferComplete( CScene_MultiGameJoinLoad *pClass )
{
	Minecraft *pMinecraft = Minecraft::GetInstance();
	// loop until complete
	while(pClass->m_bTransferComplete==false)
	{
		// check for a cancel
		if(pClass->m_bSaveTransferInProgress==false)
		{
			// cancelled
			return false;
		}
		Sleep(50);		
		// update the progress
		pMinecraft->progressRenderer->progressStagePercentage((unsigned int)(pClass->m_fProgress*100.0f));
	}

	// was there a transfer error?

	return true;
}

int CScene_MultiGameJoinLoad::SaveOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;

	// results switched for this dialog
	// EMessage_ResultAccept means cancel
	if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultThirdOption) 
	{
		if(result==C4JStorage::EMessage_ResultDecline) // rename
		{
			ZeroMemory(pClass->m_wchNewName,sizeof(WCHAR)*XCONTENT_MAX_DISPLAYNAME_LENGTH);
			// bring up a keyboard
			InputManager.RequestKeyboard(IDS_RENAME_WORLD_TITLE,L"",IDS_RENAME_WORLD_TEXT,iPad,pClass->m_wchNewName,XCONTENT_MAX_DISPLAYNAME_LENGTH,&CScene_MultiGameJoinLoad::KeyboardReturned,pClass,C_4JInput::EKeyboardMode_Default,app.GetStringTable());
		}
		else // delete
		{
			// delete the save game
			// Have to ask the player if they are sure they want to delete this game
			UINT uiIDA[2];
			uiIDA[0]=IDS_CONFIRM_CANCEL;
			uiIDA[1]=IDS_CONFIRM_OK;
			StorageManager.RequestMessageBox(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, iPad,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,pClass, app.GetStringTable());
			//pClass->m_bIgnoreInput=false;
		}
	}
	else
	{
		pClass->m_bIgnoreInput=false;
	}
	return 0;
}

int CScene_MultiGameJoinLoad::LoadSaveDataReturned(void *pParam,bool bContinue)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;

	if(bContinue==true)
	{
		pClass->m_bSaveTransferInProgress=true;
		LoadingInputParams *loadingParams = new LoadingInputParams();
		loadingParams->func = &CScene_MultiGameJoinLoad::UploadSaveForXboxOneThreadProc;
		loadingParams->lpParam = (LPVOID)pParam;

		UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData();
		completionData->bShowBackground=TRUE;
		completionData->bShowLogo=TRUE;
		completionData->type = e_ProgressCompletion_NavigateBack;
		completionData->iPad = DEFAULT_XUI_MENU_USER;
		completionData->bRequiresUserAction=TRUE;
		loadingParams->completionData = completionData;

		loadingParams->cancelFunc=&CScene_MultiGameJoinLoad::CancelSaveUploadCallback;
		loadingParams->completeFunc=&CScene_MultiGameJoinLoad::SaveUploadCompleteCallback;
		loadingParams->m_cancelFuncParam=pClass;
		loadingParams->m_completeFuncParam=pClass;
		loadingParams->cancelText=IDS_TOOLTIPS_CANCEL;

		app.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams);
	}
	else
	{
		// switch back to the normal title group id
		app.TMSPP_SetTitleGroupID(GROUP_ID);

		// the save is corrupt!

		pClass->SetShow( TRUE );
		pClass->m_bIgnoreInput=false;

		// give the option to delete the save
		UINT uiIDA[2];
		uiIDA[0]=IDS_CONFIRM_CANCEL;
		uiIDA[1]=IDS_CONFIRM_OK;
		StorageManager.RequestMessageBox(IDS_CORRUPT_OR_DAMAGED_SAVE_TITLE, IDS_CORRUPT_OR_DAMAGED_SAVE_TEXT, uiIDA, 2, 
			pClass->m_iPad,&CScene_MultiGameJoinLoad::DeleteSaveDialogReturned,pClass, app.GetStringTable());
	
	}

	return 0;
}

int CScene_MultiGameJoinLoad::Progress(void *pParam,float fProgress)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;

	app.DebugPrintf("Progress - %f\n",fProgress);
	pClass->m_fProgress=fProgress;
	return 0;
}

int CScene_MultiGameJoinLoad::TransferComplete(void *pParam,int iPad, int iResult)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;

	delete [] pClass->m_pbSaveTransferData;
	pClass->m_pbSaveTransferData = NULL;
	if(iResult!=0)
	{
		// There was a transfer fail
		// Display a dialog
		UINT uiIDA[1];
		uiIDA[0]=IDS_CONFIRM_OK;
		StorageManager.RequestMessageBox(IDS_SAVE_TRANSFER_TITLE, IDS_SAVE_TRANSFER_UPLOADFAILED, uiIDA, 1, ProfileManager.GetPrimaryPad(),NULL,NULL,app.GetStringTable());
		pClass->m_bTransferFail=true;
	}
	else
	{
		pClass->m_bTransferFail=false;
	}
	pClass->m_bTransferComplete=true;
	//pClass->m_bSaveTransferInProgress=false;
	return 0;
}

int CScene_MultiGameJoinLoad::DeleteComplete(void *pParam,int iPad, int iResult)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;
	pClass->m_bTransferComplete=true;
	return 0;
}

int CScene_MultiGameJoinLoad::KeyboardReturned(void *pParam,bool bSet)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;
	HRESULT hr = S_OK;

	// if the user has left the name empty, treat this as backing out
	if((pClass->m_wchNewName[0]!=0) && bSet)
	{
#ifdef _XBOX
		XCONTENT_DATA XContentData;
		StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,XContentData);

		C4JStorage::ELoadGameStatus eLoadStatus=StorageManager.LoadSaveData(&XContentData,CScene_MultiGameJoinLoad::LoadSaveDataForRenameReturned,pClass);

		if(eLoadStatus==C4JStorage::ELoadGame_DeviceRemoved)
		{
			// disable saving 
			StorageManager.SetSaveDisabled(true);
			StorageManager.SetSaveDeviceSelected(ProfileManager.GetPrimaryPad(),false);
			UINT uiIDA[1];
			uiIDA[0]=IDS_OK;
			StorageManager.RequestMessageBox(IDS_STORAGEDEVICEPROBLEM_TITLE, IDS_FAILED_TO_LOADSAVE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),&CScene_MultiGameJoinLoad::DeviceRemovedDialogReturned,pClass);
		}
#else
		// rename the save
		
#endif
	}
	else
	{		
		pClass->m_bIgnoreInput=false;
	}

	return hr;
}

int CScene_MultiGameJoinLoad::LoadSaveDataForRenameReturned(void *pParam,bool bContinue)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;
#ifdef _XBOX
	if(bContinue==true)
	{
		// set the save icon
		PBYTE pbImageData=NULL;
		DWORD dwImageBytes=0;
		HXUIBRUSH hXuiBrush;
		XCONTENT_DATA XContentData;
		StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,XContentData);
		StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,&pbImageData,&dwImageBytes);

		// if there is no thumbnail, retrieve the default one from the file. 
		// Don't delete the image data after creating the xuibrush, since we'll use it in the rename of the save
		if(pbImageData==NULL)
		{
			DWORD dwResult=XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&XContentData,NULL,&dwImageBytes,NULL);
			if(dwResult==ERROR_SUCCESS)
			{
				pbImageData = new BYTE[dwImageBytes];
				XContentGetThumbnail(ProfileManager.GetPrimaryPad(),&XContentData,pbImageData,&dwImageBytes,NULL);
				XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush);
			}
		}
		else
		{
			XuiCreateTextureBrushFromMemory(pbImageData,dwImageBytes,&hXuiBrush);
		}
		// save the data with this icon
		StorageManager.CopySaveDataToNewSave( pbImageData,dwImageBytes,pClass->m_wchNewName,&CScene_MultiGameJoinLoad::CopySaveReturned,pClass);
	}
	else
#endif
	{
		//pClass->SetShow( TRUE );
		pClass->m_bIgnoreInput=false;
	}
	return 0;
}

int CScene_MultiGameJoinLoad::CopySaveReturned(void *pParam,bool bResult)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad*)pParam;
#ifdef _XBOX
	if(bResult)
	{
		// and delete the old save
		XCONTENT_DATA XContentData;
		StorageManager.GetSaveCacheFileInfo(pClass->m_iChangingSaveGameInfoIndex-pClass->m_iDefaultButtonsC,XContentData);
		StorageManager.DeleteSaveData(&XContentData,CScene_MultiGameJoinLoad::DeleteSaveDataReturned,pClass);
		pClass->m_SavesListTimer.SetShow( TRUE );
	}
	else
#endif
	{
		//pClass->SetShow( TRUE );
		pClass->m_bIgnoreInput=false;
	}

	return 0;
}

int CScene_MultiGameJoinLoad::TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result)
{
	CScene_MultiGameJoinLoad *pClass = (CScene_MultiGameJoinLoad *)pParam;

	// Exit with or without saving
	// Decline means install full version of the texture pack in this dialog
	if(result==C4JStorage::EMessage_ResultDecline || result==C4JStorage::EMessage_ResultAccept) 
	{
		// we need to enable background downloading for the DLC
		XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW);

		ULONGLONG ullOfferID_Full;
		ULONGLONG ullIndexA[1];
		app.GetDLCFullOfferIDForPackID(pClass->m_initData->selectedSession->data.texturePackParentId,&ullOfferID_Full);

		if( result==C4JStorage::EMessage_ResultAccept ) // Full version
		{
			ullIndexA[0]=ullOfferID_Full;
			StorageManager.InstallOffer(1,ullIndexA,NULL,NULL);

		}
		else // trial version
		{
			// if there is no trial version, this is a Cancel
			DLC_INFO *pDLCInfo=app.GetDLCInfoForFullOfferID(ullOfferID_Full);
			if(pDLCInfo->ullOfferID_Trial!=0LL)
			{
				ullIndexA[0]=pDLCInfo->ullOfferID_Trial;
				StorageManager.InstallOffer(1,ullIndexA,NULL,NULL);
			}
		}		
	}
	pClass->m_bIgnoreInput=false;
	return 0;
}

HRESULT CScene_MultiGameJoinLoad::OnCustomMessage_DLCInstalled()
{
	// mounted DLC may have changed
	
	if(app.StartInstallDLCProcess(m_iPad)==false)
	{
		// not doing a mount, so re-enable input
		m_bIgnoreInput=false;
	}
	else
	{
		m_bIgnoreInput=true;
		// clear out the saves list and re-fill

		m_pSavesList->RemoveAllData();
		m_SavesListTimer.SetShow( TRUE );
	}
	// this will send a CustomMessage_DLCMountingComplete when done
	return S_OK;
}

HRESULT CScene_MultiGameJoinLoad::OnCustomMessage_DLCMountingComplete()
{	

	VOID *pObj;
	XuiObjectFromHandle( m_SavesList, &pObj );
	m_pSavesList = (CXuiCtrl4JList *)pObj;

	m_iChangingSaveGameInfoIndex = 0;

	m_generators = app.getLevelGenerators();
	m_iDefaultButtonsC = 0;
	m_iMashUpButtonsC = 0;
	XPARTY_USER_LIST partyList;

	if((XPartyGetUserList(  &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1))
	{
		m_bInParty=true;
	}
	else
	{
		m_bInParty=false;
	}

	int iLB = -1;

	int iY=-1;
	if(DoesSavesListHaveFocus())
	{	
		if(ProfileManager.IsSignedInLive( m_iPad ))
		{
			iY=IDS_TOOLTIPS_UPLOAD_SAVE_FOR_XBOXONE;
		}
	}
	if(m_bInParty) iLB = IDS_TOOLTIPS_PARTY_GAMES;
	// check if we're in the trial version
	if(ProfileManager.IsFullVersion()==false)
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1, -1, -1,iLB);

		AddDefaultButtons();

		m_pSavesList->SetCurSelVisible(0);
	}
	else if(StorageManager.GetSaveDisabled())
	{
		if(StorageManager.GetSaveDeviceSelected(m_iPad))
		{
			// saving is disabled, but we should still be able to load from a selected save device

			ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,iLB,IDS_TOOLTIPS_DELETESAVE);

			GetSaveInfo();
		}
		else
		{
			ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_SELECTDEVICE,iY,-1,-1,iLB);

			AddDefaultButtons();
			m_SavesListTimer.SetShow( FALSE );

			m_pSavesList->SetCurSelVisible(0);
		}
	}
	else
	{
		// 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename)
		bool bCanRename = StorageManager.EnoughSpaceForAMinSaveGame();
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK,IDS_TOOLTIPS_CHANGEDEVICE,iY,-1,-1,-1,bCanRename?IDS_TOOLTIPS_SAVEOPTIONS:IDS_TOOLTIPS_DELETESAVE);

		GetSaveInfo();
	}

	m_bIgnoreInput=false;
	app.m_dlcManager.checkForCorruptDLCAndAlert();
	return S_OK;
}

/*
void CScene_MultiGameJoinLoad::UpdateTooltips()
{
	int iA=IDS_TOOLTIPS_SELECT;
	int iB=IDS_TOOLTIPS_BACK;
	int iX=-1;
	int iY=-1
	int iLB = -1;
	XPARTY_USER_LIST partyList;

	if((XPartyGetUserList(  &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1))
	{
		m_bInParty=true;
	}
	else
	{
		m_bInParty=false;
	}

	if(m_bInParty) iLB = IDS_TOOLTIPS_PARTY_GAMES;

	if(ProfileManager.IsFullVersion()==false)
	{
		ui.SetTooltips( DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT,IDS_TOOLTIPS_BACK, -1, -1, -1, -1,iLB);
	}
	else if(StorageManager.GetSaveDisabled())
	{
		if(StorageManager.GetSaveDeviceSelected(m_iPad))
		{
			// saving is disabled, but we should still be able to load from a selected save device
			iX=IDS_TOOLTIPS_CHANGEDEVICE;
			iRB=IDS_TOOLTIPS_DELETESAVE;
		}
		else
		{
			iX=IDS_TOOLTIPS_SELECTDEVICE;
		}
	}
	else
	{
		// 4J-PB - we need to check that there is enough space left to create a copy of the save (for a rename)
		bool bCanRename = StorageManager.EnoughSpaceForAMinSaveGame();

		if(bCanRename) 
		{
			iRB=IDS_TOOLTIPS_SAVEOPTIONS;
		}
		else
		{
			iRB=IDS_TOOLTIPS_DELETESAVE;
		}
	}

	ui.SetTooltips( DEFAULT_XUI_MENU_USER, iA,iB, iX, iY, iLT, iRT,iLB, iRB);
}
*/



#ifdef _XBOX
bool CScene_MultiGameJoinLoad::GetSavesInfoCallback(LPVOID pParam,int iTotalSaveInfoC, C4JStorage::CACHEINFOSTRUCT *InfoA, int iPad, HRESULT hResult)
{
	CScene_MultiGameJoinLoad *pClass=(CScene_MultiGameJoinLoad *)pParam;
	CXuiCtrl4JList::LIST_ITEM_INFO ListInfo;
	PBYTE pbImageData=(PBYTE)InfoA;
	PBYTE pbCurrentImagePtr;
	HXUIBRUSH hXuiBrush;
	HRESULT hr;

	// move the image data pointer to the right place
	if(iTotalSaveInfoC!=0)
	{
		pbImageData+=sizeof(C4JStorage::CACHEINFOSTRUCT)*iTotalSaveInfoC;
	}

	pClass->m_SavesListTimer.SetShow( FALSE );
	pClass->m_SavesList.SetEnable(TRUE);

	pClass->AddDefaultButtons();

	for(int i=0;i<iTotalSaveInfoC;i++)
	{
		ZeroMemory(&ListInfo,sizeof(CXuiCtrl4JList::LIST_ITEM_INFO));
		// Add these to the save list
		if(!(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()))
		{
			// if the save is corrupt, display this instead of the title
			if(InfoA[i].dwImageBytes==0)
			{
				ListInfo.pwszText=app.GetString(IDS_CORRUPT_OR_DAMAGED_SAVE_TITLE);
				ListInfo.bIsDamaged=true;
			}
			else
			{
				ListInfo.pwszText=InfoA[i].wchDisplayName;
				ListInfo.bIsDamaged=false;
			}
			ListInfo.fEnabled=TRUE;
			ListInfo.iData = -1;

			pClass->m_pSavesList->AddData(ListInfo,-1);

			// update the graphic on the list item

			// if there is no thumbnail, this is a corrupt file
			if(InfoA[i].dwImageBytes!=0)
			{
				pbCurrentImagePtr=pbImageData+InfoA[i].dwImageOffset;
				hr=XuiCreateTextureBrushFromMemory(pbCurrentImagePtr,InfoA[i].dwImageBytes,&hXuiBrush);
				pClass->m_pSavesList->UpdateGraphic(i+pClass->m_iDefaultButtonsC,hXuiBrush );
			}	
			else
			{
				// we could put in a damaged save icon here
				const DWORD LOCATOR_SIZE = 256; // Use this to allocate space to hold a ResourceLocator string 
				WCHAR szResourceLocator[ LOCATOR_SIZE ];
				const ULONG_PTR c_ModuleHandle = (ULONG_PTR)GetModuleHandle(NULL);

				swprintf(szResourceLocator, LOCATOR_SIZE, L"section://%X,%ls#%ls",c_ModuleHandle,L"media", L"media/Graphics/MinecraftBrokenIcon.png");

				XuiCreateTextureBrush(szResourceLocator,&hXuiBrush);
				pClass->m_pSavesList->UpdateGraphic(i+pClass->m_iDefaultButtonsC,hXuiBrush );
			}
		}
	}

	pClass->m_iSaveInfoC=iTotalSaveInfoC;

	// If there are some saves, then set the focus to be on the most recent one, which will be the first one after the create and tutorial
	if(iTotalSaveInfoC>0)
	{
		pClass->m_pSavesList->SetCurSelVisible(pClass->m_iDefaultButtonsC);
		pClass->m_bReady=true;
	}

	pClass->m_bRetrievingSaveInfo=false;

	// It's possible that the games list is updated but we haven't displayed it yet as we were still waiting on saves list to load
	// This is to fix a bug where joining a game before the saves list has loaded causes a crash when this callback is called
	// as the scene no longer exists
	pClass->UpdateGamesList();	

	// Fix for #45154 - Frontend: DLC: Content can only be downloaded from the frontend if you have not joined/exited multiplayer
	XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO);

	return false;
}
#else
int CScene_MultiGameJoinLoad::GetSavesInfoCallback(LPVOID lpParam,const bool)
{
	return true;
}
#endif

void CScene_MultiGameJoinLoad::CancelSaveUploadCallback(LPVOID lpParam)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad *) lpParam;

	StorageManager.TMSPP_CancelWriteFileWithProgress(pClass->m_iPad);

	pClass->m_bSaveTransferInProgress=false;

	// change back to the normal title group id
	app.TMSPP_SetTitleGroupID(GROUP_ID);
// 	app.getRemoteStorage()->abort();
// 	pClass->m_eSaveUploadState = eSaveUpload_Idle;

	UINT uiIDA[1] = { IDS_CONFIRM_OK };
	ui.RequestMessageBox(IDS_XBONE_CANCEL_UPLOAD_TITLE, IDS_XBONE_CANCEL_UPLOAD_TEXT, uiIDA, 1, pClass->m_iPad, NULL, NULL, app.GetStringTable());
}

void CScene_MultiGameJoinLoad::SaveUploadCompleteCallback(LPVOID lpParam)
{
	CScene_MultiGameJoinLoad* pClass = (CScene_MultiGameJoinLoad *) lpParam;

	pClass->m_bSaveTransferInProgress=false;
	// change back to the normal title group id
	app.TMSPP_SetTitleGroupID(GROUP_ID);
	// 	app.getRemoteStorage()->abort();
	// 	pClass->m_eSaveUploadState = eSaveUpload_Idle;
}