4jcraft/Minecraft.Client/Platform/Common/XUI/XUI_TransferToXboxOne.cpp
2026-03-22 12:40:22 -05:00

573 lines
22 KiB
C++

#include "../../Minecraft.World/Platform/stdafx.h"
#include <xuiresource.h>
#include <xuiapp.h>
#include <assert.h>
#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;
}