#include "../../Minecraft.World/Platform/stdafx.h" #include #include #include #include "XUI_Ctrl_4JList.h" #include "XUI_Ctrl_4JIcon.h" #include "XUI_LoadSettings.h" #include "../../Minecraft.Client/Rendering/EntityRenderers/ProgressRenderer.h" #include "XUI_TransferToXboxOne.h" //---------------------------------------------------------------------------------- // Performs initialization tasks - retrieves controls. //---------------------------------------------------------------------------------- HRESULT CScene_TransferToXboxOne::OnInit(XUIMessageInit* pInitData, BOOL& bHandled) { m_iX = -1; m_params = (LoadMenuInitData*)pInitData->pvInitData; m_iPad = m_params->iPad; m_bRetrievingSaveInfo = false; m_bIgnoreInput = false; MapChildControls(); void* pObj; XuiObjectFromHandle(m_SavesSlotList, &pObj); m_pSavesSlotList = (CXuiCtrl4JList*)pObj; m_pbImageData = NULL; m_dwImageBytes = 0; StorageManager.GetSaveCacheFileInfo(m_params->iSaveGameInfoIndex, m_XContentData); StorageManager.GetSaveCacheFileInfo(m_params->iSaveGameInfoIndex, &m_pbImageData, &m_dwImageBytes); m_SavesSlotListTimer.SetShow(TRUE); XuiControlSetText(m_SavesSlotList, app.GetString(IDS_XBONE_SELECTSLOT)); // insert the current save slot names m_MaxSlotC = app.m_uiTransferSlotC; m_pSlotDataA = new SLOTDATA[m_MaxSlotC]; ZeroMemory(m_pSlotDataA, sizeof(SLOTDATA) * m_MaxSlotC); // saves will be called slot1 to slotx // there will be a details file with the names and png of each slot m_pbSlotListFile = NULL; m_uiSlotListFileBytes = 0; if (StorageManager.TMSPP_InFileList(C4JStorage::eGlobalStorage_TitleUser, m_iPad, L"XboxOne/SlotList")) { // there is a slot list file with details of the saves C4JStorage::ETMSStatus status = StorageManager.TMSPP_ReadFile( m_iPad, C4JStorage::eGlobalStorage_TitleUser, C4JStorage::TMS_FILETYPE_BINARY, "XboxOne/SlotList", &CScene_TransferToXboxOne::TMSPPSlotListReturned, this); m_iX = IDS_TOOLTIPS_CLEARSLOTS; } else { CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; ZeroMemory(&ListInfo, sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); // create dummy slots for (int i = 0; i < m_MaxSlotC; i++) { memcpy(m_pSlotDataA[i].wchSaveTitle, app.GetString(IDS_XBONE_EMPTYSLOT), sizeof(WCHAR) * XCONTENT_MAX_DISPLAYNAME_LENGTH); ListInfo.pwszText = app.GetString(IDS_XBONE_EMPTYSLOT); ListInfo.fEnabled = TRUE; ListInfo.iData = -1; m_pSavesSlotList->AddData(ListInfo); } m_SavesSlotListTimer.SetShow(FALSE); } CXuiSceneBase::SetTooltips(DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, m_iX, -1, -1, -1, -1, -1, -1, true); return S_OK; } //---------------------------------------------------------------------------------- // TMSPPSlotListReturned callback //---------------------------------------------------------------------------------- int CScene_TransferToXboxOne::TMSPPWriteReturned(LPVOID pParam, int iPad, int iUserData) { CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne*)pParam; pClass->m_bWaitingForWrite = false; return 0; } //---------------------------------------------------------------------------------- // TMSPPSlotListReturned callback //---------------------------------------------------------------------------------- int CScene_TransferToXboxOne::TMSPPDeleteReturned(LPVOID pParam, int iPad, int iUserData) { CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne*)pParam; pClass->m_SavesSlotListTimer.SetShow(FALSE); pClass->m_bIgnoreInput = false; // update the slots delete pClass->m_pbSlotListFile; pClass->m_pbSlotListFile = NULL; pClass->m_uiSlotListFileBytes = 0; pClass->m_pSavesSlotList->RemoveAllData(); CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; ZeroMemory(&ListInfo, sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); // clear our slot info ZeroMemory(pClass->m_pSlotDataA, sizeof(SLOTDATA) * pClass->m_MaxSlotC); for (int i = 0; i < pClass->m_MaxSlotC; i++) { memcpy(pClass->m_pSlotDataA[i].wchSaveTitle, app.GetString(IDS_XBONE_EMPTYSLOT), sizeof(WCHAR) * XCONTENT_MAX_DISPLAYNAME_LENGTH); ListInfo.pwszText = app.GetString(IDS_XBONE_EMPTYSLOT); ListInfo.fEnabled = TRUE; ListInfo.iData = -1; pClass->m_pSavesSlotList->AddData(ListInfo); } return 0; } //---------------------------------------------------------------------------------- // TMSPPSlotListReturned callback //---------------------------------------------------------------------------------- int CScene_TransferToXboxOne::TMSPPSlotListReturned( LPVOID pParam, int iPad, int iUserData, C4JStorage::PTMSPP_FILEDATA pFileData, LPCSTR szFilename) { CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne*)pParam; unsigned int uiSlotListFileSlots = *((unsigned int*)pFileData->pbData); pClass->m_pbSlotListFile = pFileData->pbData; pClass->m_uiSlotListFileBytes = pFileData->dwSize; // clear our slot info ZeroMemory(pClass->m_pSlotDataA, sizeof(SLOTDATA) * pClass->m_MaxSlotC); // set the empty slot strings for (int i = 0; i < pClass->m_MaxSlotC; i++) { memcpy(pClass->m_pSlotDataA[i].wchSaveTitle, app.GetString(IDS_XBONE_EMPTYSLOT), sizeof(WCHAR) * XCONTENT_MAX_DISPLAYNAME_LENGTH); } // update our slot info with the data from the file - might have less slots unsigned int uiNewSlotsC = (pClass->m_MaxSlotC < uiSlotListFileSlots) ? pClass->m_MaxSlotC : uiSlotListFileSlots; memcpy(pClass->m_pSlotDataA, pClass->m_pbSlotListFile + sizeof(unsigned int), sizeof(SLOTDATA) * uiNewSlotsC); CXuiCtrl4JList::LIST_ITEM_INFO ListInfo; ZeroMemory(&ListInfo, sizeof(CXuiCtrl4JList::LIST_ITEM_INFO)); PBYTE pbImageData = pClass->m_pbSlotListFile + sizeof(unsigned int) + sizeof(SLOTDATA) * uiSlotListFileSlots; // fill out the slot info for (int i = 0; i < pClass->m_MaxSlotC; i++) { if (i < uiNewSlotsC) { ListInfo.pwszText = pClass->m_pSlotDataA[i].wchSaveTitle; ListInfo.fEnabled = TRUE; ListInfo.iData = -1; pClass->m_pSavesSlotList->AddData(ListInfo); if (pClass->m_pSlotDataA[i].uiImageLength != 0) { XuiCreateTextureBrushFromMemory( pbImageData, pClass->m_pSlotDataA[i].uiImageLength, &pClass->m_hXuiBrush); pClass->m_pSavesSlotList->UpdateGraphic(i, pClass->m_hXuiBrush); // increment the image data pointer pbImageData += pClass->m_pSlotDataA[i].uiImageLength; } } else { // make it blank ListInfo.pwszText = app.GetString(IDS_XBONE_EMPTYSLOT); ListInfo.fEnabled = TRUE; ListInfo.iData = -1; pClass->m_pSavesSlotList->AddData(ListInfo); } } pClass->m_SavesSlotListTimer.SetShow(FALSE); return 0; } //---------------------------------------------------------------------------------- // Handler for OnDestroy //---------------------------------------------------------------------------------- HRESULT CScene_TransferToXboxOne::OnDestroy() { return S_OK; } //---------------------------------------------------------------------------------- // Handler for the button press message. //---------------------------------------------------------------------------------- HRESULT CScene_TransferToXboxOne::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_SavesSlotList) { m_bIgnoreInput = true; // update the info in the SlotList file CXuiControl pItem; int iIndex; // get the selected item iIndex = m_SavesSlotList.GetCurSel(&pItem); // check if there is a save there CXuiCtrl4JList::LIST_ITEM_INFO info = m_pSavesSlotList->GetData(iIndex); if (info.pwszImage != NULL) { // we have a save here // Are you sure, etc. } // update the data memcpy(m_pSlotDataA[iIndex].wchSaveTitle, m_XContentData.szDisplayName, sizeof(WCHAR) * XCONTENT_MAX_DISPLAYNAME_LENGTH); m_pSavesSlotList->UpdateText(iIndex, m_pSlotDataA[iIndex].wchSaveTitle); // 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 bool bHostOptionsRead = false; unsigned int uiHostOptions = 0; XuiCreateTextureBrushFromMemory(m_pbImageData, m_dwImageBytes, &m_hXuiBrush); m_pSavesSlotList->UpdateGraphic(iIndex, m_hXuiBrush); m_pSlotDataA[iIndex].uiImageLength = m_dwImageBytes; m_bIgnoreInput = false; // finished so navigate back // app.NavigateBack(XUSER_INDEX_ANY); BuildSlotFile(iIndex, m_pbImageData, m_dwImageBytes); } return S_OK; } HRESULT CScene_TransferToXboxOne::BuildSlotFile(int iIndexBeingUpdated, PBYTE pbImageData, DWORD dwImageBytes) { SLOTDATA* pCurrentSlotData = NULL; PBYTE pbCurrentSlotDataPtr = NULL; // there may be no slot file yet if (m_pbSlotListFile != NULL) { pCurrentSlotData = (SLOTDATA*)(m_pbSlotListFile + sizeof(unsigned int)); pbCurrentSlotDataPtr = m_pbSlotListFile + sizeof(unsigned int) + sizeof(SLOTDATA) * m_MaxSlotC; } m_uiSlotID = iIndexBeingUpdated; // memory required - first int is the number of slots in this file, in case // that changes later unsigned int uiBytesRequired = sizeof(unsigned int) + sizeof(SLOTDATA) * m_MaxSlotC; for (int i = 0; i < m_MaxSlotC; i++) { if (i == iIndexBeingUpdated) { uiBytesRequired += dwImageBytes; } else { uiBytesRequired += m_pSlotDataA[i].uiImageLength; } } PBYTE pbNewSlotFileData = new BYTE[uiBytesRequired]; *((unsigned int*)pbNewSlotFileData) = m_MaxSlotC; PBYTE pbNewSlotFileDataSlots = pbNewSlotFileData + sizeof(unsigned int); PBYTE pbNewSlotImageDataPtr = pbNewSlotFileData + sizeof(unsigned int) + sizeof(SLOTDATA) * m_MaxSlotC; SLOTDATA* pNewSlotData = (SLOTDATA*)pbNewSlotFileDataSlots; // copy the slot info into the new memory, just after the first int memcpy(pbNewSlotFileDataSlots, m_pSlotDataA, sizeof(SLOTDATA) * m_MaxSlotC); for (int i = 0; i < m_MaxSlotC; i++) { if (i == iIndexBeingUpdated) { memcpy(pbNewSlotImageDataPtr, pbImageData, dwImageBytes); pbNewSlotImageDataPtr += dwImageBytes; // update the fields pNewSlotData[i].uiImageLength = dwImageBytes; // save title is already correct } else { if (pbCurrentSlotDataPtr != NULL) { memcpy(pbNewSlotImageDataPtr, pbCurrentSlotDataPtr, pCurrentSlotData[i].uiImageLength); pbNewSlotImageDataPtr += pCurrentSlotData[i].uiImageLength; } // fields are already correct } // move to the next image data in the current slot file if (pbCurrentSlotDataPtr != NULL) { pbCurrentSlotDataPtr += pCurrentSlotData[i].uiImageLength; } } // free the previous file data and the current file data delete m_pbSlotListFile; // reset the pointer until we have the exit from this scene running m_pbSlotListFile = pbNewSlotFileData; m_uiSlotListFileBytes = uiBytesRequired; m_pSlotDataA = (SLOTDATA*)pbNewSlotFileDataSlots; LoadingInputParams* loadingParams = new LoadingInputParams(); loadingParams->func = &CScene_TransferToXboxOne::UploadSaveForXboxOneThreadProc; loadingParams->lpParam = (LPVOID)this; UIFullscreenProgressCompletionData* completionData = new UIFullscreenProgressCompletionData(); completionData->bShowBackground = TRUE; completionData->bShowLogo = TRUE; completionData->iPad = m_iPad; completionData->type = e_ProgressCompletion_NavigateBackToScene; completionData->scene = eUIScene_LoadMenu; loadingParams->completionData = completionData; app.NavigateToScene(m_iPad, eUIScene_FullscreenProgress, loadingParams); return S_OK; } int CScene_TransferToXboxOne::UploadSaveForXboxOneThreadProc( LPVOID lpParameter) { HRESULT hr = S_OK; char szFilename[32]; CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne*)lpParameter; Minecraft* pMinecraft = Minecraft::GetInstance(); unsigned int uiComplete = 0; pClass->m_bWaitingForWrite = true; pMinecraft->progressRenderer->progressStart(IDS_XBONE_UPLOAD_SAVE_TITLE); pMinecraft->progressRenderer->progressStage(IDS_XBONE_UPLOAD_METADATA); // now write the new slot data file to global storage, and then write the // save data C4JStorage::ETMSStatus eStatus = StorageManager.TMSPP_WriteFile( pClass->m_iPad, C4JStorage::eGlobalStorage_TitleUser, C4JStorage::TMS_FILETYPE_BINARY, C4JStorage::TMS_UGCTYPE_NONE, "XboxOne/SlotList", (PCHAR)pClass->m_pbSlotListFile, pClass->m_uiSlotListFileBytes, &CScene_TransferToXboxOne::TMSPPWriteReturned, lpParameter, 0); if (eStatus != C4JStorage::ETMSStatus_WriteInProgress) { // failed pClass->m_bWaitingForWrite = false; } else { // loop waiting for the write to complete uiComplete = 0; while (pClass->m_bWaitingForWrite && (hr == S_OK)) { Sleep(50); uiComplete++; if (uiComplete > 100) uiComplete = 100; pMinecraft->progressRenderer->progressStagePercentage(uiComplete); if (app.GetChangingSessionType()) { // 4J Stu - This causes the fullscreenprogress scene to ignore // the action it was given hr = ERROR_CANCELLED; } } if (hr != S_OK) return -1; // finish the bar for (int i = uiComplete; i < 100; i++) { Sleep(5); pMinecraft->progressRenderer->progressStagePercentage(i); } // now upload the save data pMinecraft->progressRenderer->progressStage(IDS_XBONE_UPLOAD_SAVE); // write the save file, and increment the progress percentage pMinecraft->progressRenderer->progressStagePercentage(25); pClass->m_bSaveDataReceived = false; C4JStorage::ELoadGameStatus eLoadStatus = StorageManager.LoadSaveData( &pClass->m_XContentData, CScene_TransferToXboxOne::LoadSaveDataReturned, lpParameter); // sleep until we have the data while (pClass->m_bSaveDataReceived == false) { Sleep(50); } // write the save to user TMS // break the file up into 256K chunks unsigned int uiChunkSize = 262144; unsigned int uiBytesLeft = pClass->m_uiStorageLength; C4JStorage::ETMSStatus eStatus; // max file size would be 100*256K unsigned int uiPercentageChunk = 100 / (pClass->m_uiStorageLength / uiChunkSize); uiComplete = 0; if (uiPercentageChunk == 0) uiPercentageChunk = 1; for (int i = 0; i < (pClass->m_uiStorageLength / uiChunkSize) + 1; i++) { sprintf(szFilename, "XboxOne/Slot%.2d%.2d", pClass->m_uiSlotID, i); PCHAR pchData = ((PCHAR)pClass->m_pvSaveMem) + i * uiChunkSize; pClass->m_bWaitingForWrite = true; if (uiBytesLeft >= uiChunkSize) { eStatus = StorageManager.TMSPP_WriteFile( pClass->m_iPad, C4JStorage::eGlobalStorage_TitleUser, C4JStorage::TMS_FILETYPE_BINARY, C4JStorage::TMS_UGCTYPE_NONE, szFilename, pchData, uiChunkSize, &CScene_TransferToXboxOne::TMSPPWriteReturned, lpParameter, 0); uiBytesLeft -= uiChunkSize; } else { // last bit of the file to upload is less than uiChunkSize eStatus = StorageManager.TMSPP_WriteFile( pClass->m_iPad, C4JStorage::eGlobalStorage_TitleUser, C4JStorage::TMS_FILETYPE_BINARY, C4JStorage::TMS_UGCTYPE_NONE, szFilename, pchData, uiBytesLeft, &CScene_TransferToXboxOne::TMSPPWriteReturned, lpParameter, 0); } // wait until if (eStatus != C4JStorage::ETMSStatus_WriteInProgress) { // failed pClass->m_bWaitingForWrite = false; } else { // loop waiting for the write to complete while (pClass->m_bWaitingForWrite && (hr == S_OK)) { Sleep(50); } uiComplete += uiPercentageChunk; if (uiComplete > 100) uiComplete = 100; // update the progress pMinecraft->progressRenderer->progressStagePercentage( uiComplete); } } delete pClass->m_pvSaveMem; } return hr; } int CScene_TransferToXboxOne::LoadSaveDataReturned(void* pParam, bool bContinue) { CScene_TransferToXboxOne* pClass = (CScene_TransferToXboxOne*)pParam; if (bContinue == true) { unsigned int uiFileSize = StorageManager.GetSaveSize(); pClass->m_pvSaveMem = new BYTE[uiFileSize]; pClass->m_uiStorageLength = 0; StorageManager.GetSaveData(pClass->m_pvSaveMem, &pClass->m_uiStorageLength); pClass->m_bSaveDataReceived = true; } return 0; } HRESULT CScene_TransferToXboxOne::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: app.NavigateBack(XUSER_INDEX_ANY); rfHandled = TRUE; break; case VK_PAD_X: // wipe the save slots if (m_pbSlotListFile != NULL) { m_SavesSlotListTimer.SetShow(TRUE); m_bIgnoreInput = true; C4JStorage::ETMSStatus eStatus = StorageManager.TMSPP_DeleteFile( m_iPad, "XboxOne/SlotList", C4JStorage::TMS_FILETYPE_BINARY, &CScene_TransferToXboxOne::TMSPPDeleteReturned, this, 0); } break; } return hr; } HRESULT CScene_TransferToXboxOne::OnNotifySelChanged( HXUIOBJ hObjSource, XUINotifySelChanged* pNotifySelChangedData, BOOL& bHandled) { // if(m_bReady) { CXuiSceneBase::PlayUISFX(eSFX_Focus); } return S_OK; } HRESULT CScene_TransferToXboxOne::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); } return S_OK; } HRESULT CScene_TransferToXboxOne::OnFontRendererChange() { // update the tooltips CXuiSceneBase::SetTooltips(DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, m_iX, -1, -1, -1, -1, -1, -1, true); return S_OK; } HRESULT CScene_TransferToXboxOne::OnNotifySetFocus( HXUIOBJ hObjSource, XUINotifyFocus* pNotifyFocusData, BOOL& bHandled) { // update the tooltips CXuiSceneBase::SetTooltips(DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, m_iX, -1, -1, -1, -1, -1, -1, true); return S_OK; } HRESULT CScene_TransferToXboxOne::OnNotifyKillFocus( HXUIOBJ hObjSource, XUINotifyFocus* pNotifyFocusData, BOOL& bHandled) { return S_OK; }