diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Graphics/CreateWorldIcon.png b/Minecraft.Client/Common/Media/MediaWindows64/Graphics/CreateWorldIcon.png new file mode 100644 index 00000000..7debd644 Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/Graphics/CreateWorldIcon.png differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Graphics/TutorialIcon.png b/Minecraft.Client/Common/Media/MediaWindows64/Graphics/TutorialIcon.png new file mode 100644 index 00000000..517b4304 Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/Graphics/TutorialIcon.png differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Graphics/lceheadwer.png b/Minecraft.Client/Common/Media/MediaWindows64/Graphics/lceheadwer.png new file mode 100644 index 00000000..abf38ba0 Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/Graphics/lceheadwer.png differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/LoadCreateJoinMenu1080.swf b/Minecraft.Client/Common/Media/MediaWindows64/LoadCreateJoinMenu1080.swf new file mode 100644 index 00000000..71a94a01 Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/LoadCreateJoinMenu1080.swf differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/skinHDDR.swf b/Minecraft.Client/Common/Media/MediaWindows64/skinHDDR.swf new file mode 100644 index 00000000..dc45d45f Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/skinHDDR.swf differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/skinHDGraphics.swf b/Minecraft.Client/Common/Media/MediaWindows64/skinHDGraphics.swf index 7ba08bc8..8ef923d9 100644 Binary files a/Minecraft.Client/Common/Media/MediaWindows64/skinHDGraphics.swf and b/Minecraft.Client/Common/Media/MediaWindows64/skinHDGraphics.swf differ diff --git a/Minecraft.Client/Common/Media/MediaWindows64/skinHDLabelsDR.swf b/Minecraft.Client/Common/Media/MediaWindows64/skinHDLabelsDR.swf new file mode 100644 index 00000000..7cde6464 Binary files /dev/null and b/Minecraft.Client/Common/Media/MediaWindows64/skinHDLabelsDR.swf differ diff --git a/Minecraft.Client/Common/UI/IUIScene_StartGame.cpp b/Minecraft.Client/Common/UI/IUIScene_StartGame.cpp index 735da438..070030b9 100644 --- a/Minecraft.Client/Common/UI/IUIScene_StartGame.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_StartGame.cpp @@ -190,7 +190,8 @@ void IUIScene_StartGame::UpdateTexturePackDescription(int index) //} wchar_t imageName[64]; - swprintf(imageName,64,L"tpack%08x",tp->getId()); + swprintf(imageName,64,L"texturePackIcon%08x",tp->getId()); + registerSubstitutionTexture(imageName, pbImageData, dwImageBytes); m_bitmapTexturePackIcon.setTextureName(imageName); pbImageData = tp->getPackComparison(dwImageBytes); @@ -376,4 +377,4 @@ int IUIScene_StartGame::TexturePackDialogReturned(void *pParam,int iPad,C4JStora #endif pClass->m_bIgnoreInput=false; return 0; -} \ No newline at end of file +} diff --git a/Minecraft.Client/Common/UI/UI.h b/Minecraft.Client/Common/UI/UI.h index 3159ced6..22988ade 100644 --- a/Minecraft.Client/Common/UI/UI.h +++ b/Minecraft.Client/Common/UI/UI.h @@ -70,6 +70,7 @@ #include "UIScene_LoadMenu.h" #include "UIScene_JoinMenu.h" #include "UIScene_LoadOrJoinMenu.h" +#include "UIScene_LoadCreateJoinMenu.h" #include "UIScene_CreateWorldMenu.h" #include "UIScene_LaunchMoreOptionsMenu.h" #include "UIScene_FullscreenProgress.h" @@ -127,4 +128,4 @@ #include "UIScene_EULA.h" #include "UIScene_NewUpdateMessage.h" -extern ConsoleUIController ui; \ No newline at end of file +extern ConsoleUIController ui; diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index d1d0cf4d..6a51fe22 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -609,6 +609,14 @@ void UIController::loadSkins() m_iggyLibraries[eLibrary_Tooltips] = loadSkin(L"skinHDTooltips.swf", L"skinHDTooltips.swf"); m_iggyLibraries[eLibrary_Default] = loadSkin(L"skinHD.swf", L"skinHD.swf"); + // Some 1080p menu ports (such as LoadCreateJoin) may import DR-specific HD + // libraries by distinct names. Load them opportunistically when present so + // those SWFs can resolve their imports without replacing the normal Windows + // skin set. + m_iggyLibraries[eLibraryDR_GraphicsDefault] = loadSkin(L"skinHDGraphicsDR.swf", L"skinHDGraphicsDR.swf"); + m_iggyLibraries[eLibraryDR_Labels] = loadSkin(L"skinHDLabelsDR.swf", L"skinHDLabelsDR.swf"); + m_iggyLibraries[eLibraryDR_Default] = loadSkin(L"skinHDDR.swf", L"skinHDDR.swf"); + #elif defined _DURANGO m_iggyLibraries[eLibrary_Platform] = loadSkin(L"skinHDDurango.swf", L"platformskinHD.swf"); @@ -979,8 +987,16 @@ void UIController::tickInput() { // ButtonList manages focus internally via Flash — // pass mouse coords so it can highlight the right item. + S32 adjustedMouseY = static_cast(sceneMouseY); + if (pScene->getSceneType() == eUIScene_LoadCreateJoinMenu) + { + const S32 visibleRows = 7; + const S32 rowHeight = (visibleRows > 0) ? (ch / visibleRows) : 0; + if (rowHeight > 0) + adjustedMouseY -= rowHeight; + } static_cast(ctrl)->SetTouchFocus( - static_cast(sceneMouseX), static_cast(sceneMouseY), false); + static_cast(sceneMouseX), adjustedMouseY, false); hitControlId = -1; hitArea = INT_MAX; hitCtrl = NULL; diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h index 4a189d4d..eee86757 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -116,6 +116,13 @@ private: eLibraryFallback_HUD, eLibraryFallback_Tooltips, eLibraryFallback_Default, + + // Optional DR-specific HD libraries used by some 1080p menu SWFs. + // Keep these after the normal HD and fallback sets so they don't disturb + // existing references and can be loaded only when the files exist. + eLibraryDR_GraphicsDefault, + eLibraryDR_Labels, + eLibraryDR_Default, #endif eLibrary_Count, diff --git a/Minecraft.Client/Common/UI/UIEnums.h b/Minecraft.Client/Common/UI/UIEnums.h index 72ebc277..794c6e41 100644 --- a/Minecraft.Client/Common/UI/UIEnums.h +++ b/Minecraft.Client/Common/UI/UIEnums.h @@ -85,6 +85,7 @@ enum EUIScene eUIScene_InGamePlayerOptionsMenu, eUIScene_CreativeMenu, eUIScene_LaunchMoreOptionsMenu, + eUIScene_LoadCreateJoinMenu, eUIScene_DLCMainMenu, eUIScene_NewUpdateMessage, eUIScene_EnchantingMenu, diff --git a/Minecraft.Client/Common/UI/UILayer.cpp b/Minecraft.Client/Common/UI/UILayer.cpp index 092124aa..9c69dbef 100644 --- a/Minecraft.Client/Common/UI/UILayer.cpp +++ b/Minecraft.Client/Common/UI/UILayer.cpp @@ -376,6 +376,9 @@ bool UILayer::NavigateToScene(int iPad, EUIScene scene, void *initData) case eUIScene_LoadOrJoinMenu: newScene = new UIScene_LoadOrJoinMenu(iPad, initData, this); break; + case eUIScene_LoadCreateJoinMenu: + newScene = new UIScene_LoadCreateJoinMenu(iPad, initData, this); + break; case eUIScene_LoadMenu: newScene = new UIScene_LoadMenu(iPad, initData, this); break; @@ -912,4 +915,4 @@ UIScene *UILayer::FindScene(EUIScene sceneType) } return nullptr; -} \ No newline at end of file +} diff --git a/Minecraft.Client/Common/UI/UIScene.cpp b/Minecraft.Client/Common/UI/UIScene.cpp index a70b50e9..2473963e 100644 --- a/Minecraft.Client/Common/UI/UIScene.cpp +++ b/Minecraft.Client/Common/UI/UIScene.cpp @@ -352,6 +352,14 @@ void UIScene::loadMovie() // Read movie dimensions from the SWF header (available immediately after // CreateFromMemory, no init tick needed). IggyProperties *properties = IggyPlayerProperties ( swf ); + if(!properties) + { + app.DebugPrintf("ERROR: IggyPlayerProperties returned null for scene '%ls'\n", moviePath.c_str()); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + app.FatalLoadError(); + } m_movieHeight = properties->movie_height_in_pixels; m_movieWidth = properties->movie_width_in_pixels; m_renderWidth = m_movieWidth; @@ -1468,4 +1476,4 @@ size_t UIScene::GetCallbackUniqueId() bool UIScene::isReadyToDelete() { return true; -} \ No newline at end of file +} diff --git a/Minecraft.Client/Common/UI/UIScene_LoadCreateJoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_LoadCreateJoinMenu.cpp new file mode 100644 index 00000000..fb35e1e7 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LoadCreateJoinMenu.cpp @@ -0,0 +1,9440 @@ +#include "stdafx.h" + +#include "UI.h" + +#include "UIScene_LoadCreateJoinMenu.h" + +// By Rockefort O Rockefeler + +#include "..\..\..\Minecraft.World\StringHelpers.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.item.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" + +#include "..\..\..\Minecraft.World\net.minecraft.world.level.chunk.storage.h" + +#include "..\..\..\Minecraft.World\ConsoleSaveFile.h" + +#include "..\..\..\Minecraft.World\ConsoleSaveFileOriginal.h" + +#include "..\..\ProgressRenderer.h" + +#include "..\..\MinecraftServer.h" + +#include "..\..\TexturePackRepository.h" + +#include "..\..\TexturePack.h" + +#include "..\Network\SessionInfo.h" + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + +#include "Common\Network\Sony\SonyHttp.h" + +#include "Common\Network\Sony\SonyRemoteStorage.h" + +#include "DLCTexturePack.h" + +#endif + +#if defined(__ORBIS__) || defined(__PSVITA__) + +#include + +#endif + +#ifdef __PSVITA__ + +#include "message_dialog.h" + +#endif + + + +#ifdef _WINDOWS64 + +#include "..\..\..\Minecraft.World\NbtIo.h" + +#include "..\..\..\Minecraft.World\compression.h" +#include "..\..\Windows64\KeyboardMouseInput.h" +#include "UISplitScreenHelpers.h" + +#include + + + +static bool IsGeneratedSaveTitle(const char *title) + +{ + + if(title == nullptr) return false; + + size_t len = strlen(title); + + if(len != 14) return false; + + for(size_t i = 0; i < len; ++i) + + { + + if(title[i] < '0' || title[i] > '9') return false; + + } + + return true; + +} + + + +static bool TryReadSaveThumbnailMetadata(const SAVE_INFO &saveInfo, unsigned int &uiHostOptions, DWORD &uiTexturePack) +{ + if(saveInfo.thumbnailData == nullptr || saveInfo.metaData.thumbnailSize == 0) + return false; + + bool bHostOptionsRead = false; + char szSeed[50] = {}; + uiHostOptions = 0; + uiTexturePack = 0; + app.GetImageTextData(saveInfo.thumbnailData, saveInfo.metaData.thumbnailSize, reinterpret_cast(&szSeed), uiHostOptions, bHostOptionsRead, uiTexturePack); + return bHostOptionsRead; +} + +static bool IsTutorialSaveMetadata(unsigned int uiHostOptions, DWORD uiTexturePack) +{ + if(uiTexturePack != TexturePackRepository::DEFAULT_TEXTURE_PACK_ID) + return false; + + const unsigned int expectedTutorial = app.GetGameHostOption(uiHostOptions, eGameHostOption_Tutorial); + return uiHostOptions == expectedTutorial; +} + +#ifdef _WINDOWS64 +static bool TryLoadThumbnailFromDisk_Win64(const char *saveFolder, PBYTE *outData, DWORD *outSize) +{ + if(outData) *outData = nullptr; + if(outSize) *outSize = 0; + if(saveFolder == nullptr || saveFolder[0] == '\0') + return false; + + wchar_t wFolder[MAX_SAVEFILENAME_LENGTH]; + ZeroMemory(wFolder, sizeof(wFolder)); + int converted = MultiByteToWideChar(CP_UTF8, 0, saveFolder, -1, wFolder, MAX_SAVEFILENAME_LENGTH); + if(converted <= 0) + { + mbstowcs(wFolder, saveFolder, MAX_SAVEFILENAME_LENGTH - 1); + } + + wstring filePath = wstring(L"Windows64\\GameHDD\\") + wFolder + wstring(L"\\saveThumbnail.png"); + HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); + if(hFile == INVALID_HANDLE_VALUE) + return false; + + DWORD fileSize = GetFileSize(hFile, nullptr); + if(fileSize == INVALID_FILE_SIZE || fileSize == 0) + { + CloseHandle(hFile); + return false; + } + + PBYTE buffer = new BYTE[fileSize]; + DWORD bytesRead = 0; + BOOL ok = ReadFile(hFile, buffer, fileSize, &bytesRead, nullptr); + CloseHandle(hFile); + if(!ok || bytesRead == 0) + { + delete [] buffer; + return false; + } + + if(outData) *outData = buffer; + if(outSize) *outSize = bytesRead; + return true; +} +#endif +static wstring GetPreferredLevelGenSaveTitle(LevelGenerationOptions *levelGen) +{ + if(levelGen == nullptr) + return L""; + + if(levelGen->isTutorial()) + return app.GetString(IDS_TUTORIALSAVENAME); + + LPCWSTR displayName = levelGen->getDisplayName(); + if(displayName != nullptr && displayName[0] != L'\0') + return displayName; + + wstring defaultSaveName = levelGen->getDefaultSaveName(); + if(!defaultSaveName.empty()) + return defaultSaveName; + + LPCWSTR worldName = levelGen->getWorldName(); + if(worldName != nullptr && worldName[0] != L'\0') + return worldName; + + return L""; +} + +static wstring GetPackFallbackName(DWORD uiTexturePack) +{ + if(uiTexturePack == TexturePackRepository::DEFAULT_TEXTURE_PACK_ID) + return L""; + + vector *levelGenerators = app.getLevelGenerators(); + if(levelGenerators == nullptr) + return L""; + + for(LevelGenerationOptions *levelGen : *levelGenerators) + { + if(levelGen == nullptr || levelGen->isTutorial()) + continue; + + if(levelGen->requiresTexturePack() && levelGen->getRequiredTexturePackId() == uiTexturePack) + { + wstring preferredTitle = GetPreferredLevelGenSaveTitle(levelGen); + if(!preferredTitle.empty()) + return preferredTitle; + } + } + + return L""; +} + +static wstring GetGeneratedSaveFallbackName(const SAVE_INFO &saveInfo) +{ + unsigned int uiHostOptions = 0; + DWORD uiTexturePack = TexturePackRepository::DEFAULT_TEXTURE_PACK_ID; + if(TryReadSaveThumbnailMetadata(saveInfo, uiHostOptions, uiTexturePack)) + { + if(IsTutorialSaveMetadata(uiHostOptions, uiTexturePack)) + return app.GetString(IDS_TUTORIALSAVENAME); + + wstring packFallbackName = GetPackFallbackName(uiTexturePack); + if(!packFallbackName.empty()) + return packFallbackName; + } + + return app.GetString(IDS_CREATE_NEW_WORLD); +} +static bool TryLoadUiImageBytes(const wchar_t *fileName, std::vector &outBytes) + +{ + + wchar_t modulePath[MAX_PATH] = {}; + + GetModuleFileNameW(nullptr, modulePath, MAX_PATH); + + wchar_t *lastSlash = wcsrchr(modulePath, L'\\'); + + if(lastSlash) *(lastSlash + 1) = L'\0'; + + + + const wstring exeDir(modulePath); + + const wstring candidates[] = + + { + + exeDir + L"Common\\Media\\MediaWindows64\\Graphics\\" + fileName, + + exeDir + L"Graphics\\" + fileName, + + exeDir + fileName, + + exeDir + L"..\\..\\..\\..\\Minecraft.Client\\Common\\Media\\MediaWindows64\\Graphics\\" + fileName, + exeDir + L"..\\..\\..\\..\\Minecraft.Client\\Windows64Media\\Graphics\\" + fileName, + + wstring(L"Common\\Media\\MediaWindows64\\Graphics\\") + fileName, + + wstring(L"Windows64Media\\Graphics\\") + fileName, + + wstring(L"Graphics\\") + fileName, + + }; + + + + for (const wstring &fullPath : candidates) + + { + + HANDLE hFile = CreateFileW(fullPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (hFile == INVALID_HANDLE_VALUE) + + continue; + + + + DWORD fileSize = GetFileSize(hFile, nullptr); + + if (fileSize == INVALID_FILE_SIZE || fileSize == 0) + + { + + CloseHandle(hFile); + + continue; + + } + + + + outBytes.resize(fileSize); + + DWORD bytesRead = 0; + + const BOOL ok = ReadFile(hFile, outBytes.data(), fileSize, &bytesRead, nullptr); + + CloseHandle(hFile); + + + + if (ok && bytesRead == fileSize) + + return true; + + } + + + + outBytes.clear(); + + return false; + +} + + + +static void TrySetButtonListIcon(UIControl_SaveList &list, int itemIndex, const wchar_t *fileName, const wchar_t *textureName) + +{ + + std::vector bytes; + + if(!TryLoadUiImageBytes(fileName, bytes) || bytes.empty()) + + return; + + + + BYTE *copy = new BYTE[bytes.size()]; + + memcpy(copy, bytes.data(), bytes.size()); + + ui.registerSubstitutionTexture(textureName, copy, static_cast(bytes.size())); + + list.setTextureName(itemIndex, textureName); + +} + + + +static wstring ReadLevelNameFromSaveFile(const wstring& filePath) + +{ + + // Check for a worldname.txt sidecar written by the rename feature first + + size_t slashPos = filePath.rfind(L'\\'); + + if (slashPos != wstring::npos) + + { + + wstring sidecarPath = filePath.substr(0, slashPos + 1) + L"worldname.txt"; + + FILE *fr = nullptr; + + if (_wfopen_s(&fr, sidecarPath.c_str(), L"r") == 0 && fr) + + { + + char buf[128] = {}; + + if (fgets(buf, sizeof(buf), fr)) + + { + + int len = static_cast(strlen(buf)); + + while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r' || buf[len-1] == ' ')) + + buf[--len] = '\0'; + + fclose(fr); + + if (len > 0) + + { + + wchar_t wbuf[128] = {}; + + mbstowcs(wbuf, buf, 127); + + return wstring(wbuf); + + } + + } + + else fclose(fr); + + } + + } + + + + HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); + + if (hFile == INVALID_HANDLE_VALUE) return L""; + + + + DWORD fileSize = GetFileSize(hFile, nullptr); + + if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) { CloseHandle(hFile); return L""; } + + + + unsigned char *rawData = new unsigned char[fileSize]; + + DWORD bytesRead = 0; + + if (!ReadFile(hFile, rawData, fileSize, &bytesRead, nullptr) || bytesRead != fileSize) + + { + + CloseHandle(hFile); + + delete[] rawData; + + return L""; + + } + + CloseHandle(hFile); + + + + unsigned char *saveData = nullptr; + + unsigned int saveSize = 0; + + bool freeSaveData = false; + + + + if (*(unsigned int*)rawData == 0) + + { + + // Compressed format: bytes 0-3=0, bytes 4-7=decompressed size, bytes 8+=compressed data + + unsigned int decompSize = *(unsigned int*)(rawData + 4); + + if (decompSize == 0 || decompSize > 128 * 1024 * 1024) + + { + + delete[] rawData; + + return L""; + + } + + saveData = new unsigned char[decompSize]; + + Compression::getCompression()->Decompress(saveData, &decompSize, rawData + 8, fileSize - 8); + + saveSize = decompSize; + + freeSaveData = true; + + } + + else + + { + + saveData = rawData; + + saveSize = fileSize; + + } + + + + wstring result = L""; + + if (saveSize >= 12) + + { + + unsigned int headerOffset = *(unsigned int*)saveData; + + unsigned int numEntries = *(unsigned int*)(saveData + 4); + + const unsigned int entrySize = sizeof(FileEntrySaveData); + + + + if (headerOffset < saveSize && numEntries > 0 && numEntries < 10000 && + + headerOffset + numEntries * entrySize <= saveSize) + + { + + FileEntrySaveData *table = (FileEntrySaveData *)(saveData + headerOffset); + + for (unsigned int i = 0; i < numEntries; i++) + + { + + if (wcscmp(table[i].filename, L"level.dat") == 0) + + { + + unsigned int off = table[i].startOffset; + + unsigned int len = table[i].length; + + if (off >= 12 && off + len <= saveSize && len > 0 && len < 4 * 1024 * 1024) + + { + + byteArray ba; + + ba.data = (byte*)(saveData + off); + + ba.length = len; + + CompoundTag *root = NbtIo::decompress(ba); + + if (root != nullptr) + + { + + CompoundTag *dataTag = root->getCompound(L"Data"); + + if (dataTag != nullptr) + + result = dataTag->getString(L"LevelName"); + + delete root; + + } + + } + + break; + + } + + } + + } + + } + + + + if (freeSaveData) delete[] saveData; + + delete[] rawData; + + // "world" is the engine default - it means no real name was ever set, + + // so return empty to let the caller fall back to the save filename (timestamp). + + if (result == L"world") result = L""; + + return result; + +} + +#endif + + + + + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + +unsigned long UIScene_LoadCreateJoinMenu::m_ulFileSize=0L; + +wstring UIScene_LoadCreateJoinMenu::m_wstrStageText=L""; + +bool UIScene_LoadCreateJoinMenu::m_bSaveTransferRunning = false; + +#endif + + + + + +#define JOIN_LOAD_ONLINE_TIMER_ID 0 + +#define JOIN_LOAD_ONLINE_TIMER_TIME 100 + + + +#ifdef _XBOX + +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID 3 + +#define CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME 50 + +#endif + + + +#ifdef _XBOX_ONE + +UIScene_LoadCreateJoinMenu::ESaveTransferFiles UIScene_LoadCreateJoinMenu::s_eSaveTransferFile; + +unsigned long UIScene_LoadCreateJoinMenu::s_ulFileSize=0L; + +byteArray UIScene_LoadCreateJoinMenu::s_transferData = byteArray(); + +wstring UIScene_LoadCreateJoinMenu::m_wstrStageText=L""; + + + +#ifdef _DEBUG_MENUS_ENABLED + +C4JStorage::SAVETRANSFER_FILE_DETAILS UIScene_LoadCreateJoinMenu::m_debugTransferDetails; + +#endif + +#endif + + + +int UIScene_LoadCreateJoinMenu::LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes) + +{ + + UIScene_LoadCreateJoinMenu *pClass= static_cast(lpParam); + + + + app.DebugPrintf("Received data for save thumbnail\n"); + +#ifdef _WINDOWS64 + if((pbThumbnail == nullptr || dwThumbnailBytes == 0) && + pClass != nullptr && + pClass->m_iRequestingThumbnailId >= 0 && + pClass->m_iRequestingThumbnailId < pClass->m_iSaveDetailsCount) + { + PBYTE pbCachedThumbnail = nullptr; + DWORD dwCachedThumbnailBytes = 0; + StorageManager.GetSaveCacheFileInfo(pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].saveId, &pbCachedThumbnail, &dwCachedThumbnailBytes); + if(pbCachedThumbnail && dwCachedThumbnailBytes) + { + app.DebugPrintf("LoadCreateJoin thumbnail fallback hit cache size %d\n", dwCachedThumbnailBytes); + pbThumbnail = pbCachedThumbnail; + dwThumbnailBytes = dwCachedThumbnailBytes; + } + } +#endif + + if(pbThumbnail && dwThumbnailBytes) + + { + + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData = new BYTE[dwThumbnailBytes]; + + memcpy(pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData, pbThumbnail, dwThumbnailBytes); + + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].dwThumbnailSize = dwThumbnailBytes; + + } + + else + + { + + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].pbThumbnailData = nullptr; + + pClass->m_saveDetails[pClass->m_iRequestingThumbnailId].dwThumbnailSize = 0; + + app.DebugPrintf("Save thumbnail data is nullptr, or has size 0\n"); + + } + + pClass->m_bSaveThumbnailReady = true; + + + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::LoadSaveCallback(LPVOID lpParam,bool bRes) + +{ + + //UIScene_LoadCreateJoinMenu *pClass= (UIScene_LoadCreateJoinMenu *)lpParam; + + // Get the save data now + + if(bRes) + + { + + app.DebugPrintf("Loaded save OK\n"); + + } + + return 0; + +} + + + +UIScene_LoadCreateJoinMenu::UIScene_LoadCreateJoinMenu(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) + +{ + + constexpr uint64_t MAXIMUM_SAVE_STORAGE = 4LL * 1024LL * 1024LL * 1024LL; + + + + // Setup all the Iggy references we need for this scene + + initialiseMovie(); + + app.SetLiveLinkRequired( true ); + + + + m_iRequestingThumbnailId = 0; + + m_iSaveInfoC=0; + + m_bIgnoreInput = false; + + m_bShowingPartyGamesOnly = false; + + m_bInParty = false; + + m_activeTab = eTab_Load; + m_bPendingSaveSizeBarRefresh = false; + m_bPendingJoinTabAvailabilityRefresh = false; +#ifdef _WINDOWS64 + m_lastHoverMouseX = -1; + m_lastHoverMouseY = -1; + m_mouseHoverTracked = false; + m_hoverBaseIndexLoad = 0; + m_hoverBaseIndexCreate = 0; + m_hoverBaseIndexJoin = 0; +#endif + + m_currentSessions = nullptr; + + m_iState=e_SavesIdle; + + //m_bRetrievingSaveInfo=false; + + + + m_buttonListSaves.init(eControl_SavesList); + + m_buttonListNewGames.init(eControl_NewGamesList); + + m_buttonListGames.init(eControl_GamesList); + + + + m_labelSavesListTitle.init( L"Load" ); + + m_labelCreateListTitle.init( IDS_TOOLTIPS_CREATE ); + + m_labelJoinListTitle.init( L"Join" ); + + m_bHasNoGamesLabel = false; + + m_controlSavesTimer.setVisible( true ); + + m_controlNewGameTimer.setVisible( true ); + + m_controlJoinTimer.setVisible( false ); + + + + + +#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64) + + m_spaceIndicatorSaves.init(L"",eControl_SpaceIndicator,0, MAXIMUM_SAVE_STORAGE); + +#endif + + m_bUpdateSaveSize = false; + + + + m_bAllLoaded = false; + + m_bRetrievingSaveThumbnails = false; + + m_bSaveThumbnailReady = false; + + m_bExitScene=false; + + m_pSaveDetails=nullptr; + + m_bSavesDisplayed=false; + + m_saveDetails = nullptr; + + m_iSaveDetailsCount = 0; + + m_iTexturePacksNotInstalled = 0; + + m_bCopying = false; + + m_bCopyingCancelled = false; + + + +#ifndef _XBOX_ONE + + m_bSaveTransferCancelled=false; + + m_bSaveTransferInProgress=false; + +#endif + + m_eAction = eAction_None; + + + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + + +#ifdef _XBOX_ONE + + // 4J-PB - in order to buy the skin packs & texture packs, we need the signed offer ids for them, which we get in the availability info + + // we need to retrieve this info though, so do it here + + app.AddDLCRequest(e_Marketplace_Content); // content is skin packs, texture packs and mash-up packs + +#endif + + + + + + int iLB = -1; + + + +#ifdef _XBOX + + XPARTY_USER_LIST partyList; + + + + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + + { + + m_bInParty=true; + + } + + else + + { + + m_bInParty=false; + + } + +#endif + + + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO) || defined(_WINDOWS64) + + // Always clear the saves when we enter this menu + + StorageManager.ClearSavesInfo(); + +#endif + + + + // 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 || app.DLCInstallPending()) + + { + + // 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 = true; + + } + + else + + { + + Initialise(); + + } + + + +#ifdef __PSVITA__ + + if(CGameNetworkManager::usingAdhocMode() && SQRNetworkManager_AdHoc_Vita::GetAdhocStatus()) + + { + + g_NetworkManager.startAdhocMatching(); // create the client matching context and clear out the friends list + + } + + + +#endif + + + + UpdateGamesList(); + + + + g_NetworkManager.SetSessionsUpdatedCallback( &UpdateGamesListCallback, this ); + + + + m_initData= new JoinMenuInitData(); + + + + // 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 - Only Xbox will not have trial DLC patched into the game + +#ifdef _XBOX + + // 4J-PB - there may be texture packs we don't have, so use the info from TMS for this + + + + DLC_INFO *pDLCInfo=nullptr; + + + + // 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(); + + + + for(unsigned int i = 0; i < app.GetDLCInfoTexturesOffersCount(); ++i) + + { + + bTexturePackAlreadyListed=false; + +#if defined(__PS3__) || defined(__ORBIS__) + + char *pchDLCName=app.GetDLCInfoTextures(i); + + pDLCInfo=app.GetDLCInfo(pchDLCName); + +#else + + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + +#endif + + for(unsigned int i = 0; i < texturePacksCount; ++i) + + { + + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(i); + + if(pDLCInfo && 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; + +#if defined(__PS3__) || defined(__ORBIS__) + + char *pchDLCName=app.GetDLCInfoTextures(i); + + pDLCInfo=app.GetDLCInfo(pchDLCName); + +#else + + ULONGLONG ull=app.GetDLCInfoTexturesFullOffer(i); + + pDLCInfo=app.GetDLCInfoForFullOfferID(ull); + +#endif + + 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; + + } + + } + + } + + + + addTimer(CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID,CHECKFORAVAILABLETEXTUREPACKS_TIMER_TIME); + +#endif + + + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + + m_eSaveTransferState = eSaveTransfer_Idle; + +#endif + +} + + + + + +UIScene_LoadCreateJoinMenu::~UIScene_LoadCreateJoinMenu() + +{ + + g_NetworkManager.SetSessionsUpdatedCallback( nullptr, nullptr ); + + app.SetLiveLinkRequired( false ); + + + + if (m_currentSessions) + + { + + for (const auto& it : *m_currentSessions) + + delete it; + + delete m_currentSessions; + + m_currentSessions = nullptr; + + } + + + +#if TO_BE_IMPLEMENTED + + // Reset the background downloading, in case we changed it by attempting to download a texture pack + + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + +#endif + + + + if(m_saveDetails) + + { + + for(int i = 0; i < m_iSaveDetailsCount; ++i) + + { + + delete m_saveDetails[i].pbThumbnailData; + + } + + delete [] m_saveDetails; + + } + +} + + + +void UIScene_LoadCreateJoinMenu::updateTooltips() +{ +#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ + if(m_eSaveTransferState!=eSaveTransfer_Idle) + { + return; + } +#endif + + int iRB = -1; + int iY = -1; + int iLB = -1; + int iX = -1; + int iLS = IDS_TOOLTIPS_NAVIGATE; + + if ((m_activeTab == eTab_Load) && (m_pSaveDetails != nullptr) && (m_pSaveDetails->iSaveC > 0) && (m_buttonListSaves.getItemCount() > 0)) + { + if(StorageManager.GetSaveDisabled()) + { + iY = IDS_TOOLTIPS_DELETESAVE; + } + else if(StorageManager.EnoughSpaceForAMinSaveGame()) + { + iY = IDS_TOOLTIPS_SAVEOPTIONS; + } + else + { + iY = IDS_TOOLTIPS_DELETESAVE; + } + } + else if(DoesMashUpWorldHaveFocus()) + { + iY = IDS_TOOLTIPS_HIDE; + } + + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + if(m_iPad == ProfileManager.GetPrimaryPad()) + iY = IDS_TOOLTIPS_GAME_INVITES; +#endif + + if(ProfileManager.IsFullVersion()==false) + { + iRB = -1; + } + else if(StorageManager.GetSaveDisabled()) + { +#ifdef _XBOX + iX = IDS_TOOLTIPS_SELECTDEVICE; +#endif + } + else + { +#if defined _XBOX_ONE + if(ProfileManager.IsSignedInLive(m_iPad)) + { + iX = IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD; + } +#elif defined SONY_REMOTE_STORAGE_DOWNLOAD + bool bSignedInLive = ProfileManager.IsSignedInLive(m_iPad); + if(bSignedInLive) + { + iX = IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD; + } +#else + iX = IDS_TOOLTIPS_CHANGEDEVICE; +#endif + } + + ui.SetTooltips(DEFAULT_XUI_MENU_USER, IDS_TOOLTIPS_SELECT, IDS_TOOLTIPS_BACK, iX, iY, -1, -1, iLB, iRB, iLS, -1, -1, true); +} + +// +void UIScene_LoadCreateJoinMenu::Initialise() + +{ + + m_iSaveListIndex = 0; + + m_iGameListIndex = 0; + +#ifdef _WINDOWS64 + + m_addServerPhase = eAddServer_Idle; + +#endif + + + + m_iDefaultButtonsC = 0; + + m_iMashUpButtonsC=0; + + + + // Check if we're in the trial version + + if(ProfileManager.IsFullVersion()==false) + + { + + + + + + AddDefaultButtons(); + + + +#if TO_BE_IMPLEMENTED + + m_pSavesList->SetCurSelVisible(0); + +#endif + + } + + else if(StorageManager.GetSaveDisabled()) + + { + +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + + GetSaveInfo(); + m_controlSavesTimer.setVisible(true); + +#else + + + +#if TO_BE_IMPLEMENTED + + if(StorageManager.GetSaveDeviceSelected(m_iPad)) + +#endif + + { + + // saving is disabled, but we should still be able to load from a selected save device + + + + + + + + GetSaveInfo(); + m_controlSavesTimer.setVisible(true); + + } + +#if TO_BE_IMPLEMENTED + + else + + { + + AddDefaultButtons(); + + m_controlSavesTimer.setVisible( false ); + + } + +#endif + +#endif // __PS3__ || __ORBIS + + } + + 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(); + + + + GetSaveInfo(); + m_controlSavesTimer.setVisible(true); + + } + + + + m_bIgnoreInput=true; + + app.m_dlcManager.checkForCorruptDLCAndAlert(); + +} + + + +void UIScene_LoadCreateJoinMenu::updateComponents() + +{ + + m_parentLayer->showComponent(m_iPad,eUIComponent_Panorama,true); + + m_parentLayer->showComponent(m_iPad,eUIComponent_Logo,false); + +} + + + +void UIScene_LoadCreateJoinMenu::handleDestroy() + +{ + +#ifdef __PSVITA__ + + app.DebugPrintf("missing InputManager.DestroyKeyboard on Vita !!!!!!\n"); + +#endif + + + + // shut down the keyboard if it is displayed + +#if ( defined __PS3__ || defined __ORBIS__ || defined _DURANGO) + + InputManager.DestroyKeyboard(); + +#endif + +} + + + + + +void UIScene_LoadCreateJoinMenu::handleGainFocus(bool navBack) + +{ + + app.DebugPrintf("[LCJ] handleGainFocus navBack=%d\n", navBack ? 1 : 0); + + app.DebugPrintf("[LCJ] handleGainFocus before UIScene::handleGainFocus\n"); + + UIScene::handleGainFocus(navBack); + + app.DebugPrintf("[LCJ] handleGainFocus after UIScene::handleGainFocus\n"); + + + + app.DebugPrintf("[LCJ] handleGainFocus before updateTooltips\n"); + + updateTooltips(); + + app.DebugPrintf("[LCJ] handleGainFocus after updateTooltips\n"); + + app.DebugPrintf("[LCJ] handleGainFocus before SyncMovieTab\n"); + + SyncMovieTab(); + + app.DebugPrintf("[LCJ] handleGainFocus after SyncMovieTab\n"); + + app.DebugPrintf("[LCJ] handleGainFocus queue deferred bar/tab refresh\n"); + m_bPendingSaveSizeBarRefresh = true; + m_bPendingJoinTabAvailabilityRefresh = true; + + + + app.DebugPrintf("[LCJ] handleGainFocus before addTimer JOIN_LOAD_ONLINE_TIMER_ID\n"); + + addTimer(JOIN_LOAD_ONLINE_TIMER_ID,JOIN_LOAD_ONLINE_TIMER_TIME); + + app.DebugPrintf("[LCJ] handleGainFocus after addTimer JOIN_LOAD_ONLINE_TIMER_ID\n"); + + if(navBack) + + { + + app.SetLiveLinkRequired( true ); + + m_bMultiplayerAllowed = ProfileManager.IsSignedInLive( m_iPad ) && ProfileManager.AllowedToPlayMultiplayer(m_iPad); + + m_bIgnoreInput=false; + + if(app.StartInstallDLCProcess(m_iPad)==false) + + { + + m_bIgnoreInput=false; + + } + + else + + { + + m_bIgnoreInput=true; + + m_buttonListSaves.clearList(); + + m_controlSavesTimer.setVisible(true); + + } + + + + if( m_bMultiplayerAllowed ) + + { + +#if TO_BE_IMPLEMENTED + + HXUICLASS hClassFullscreenProgress = XuiFindClass( L"CScene_FullscreenProgress" ); + + HXUICLASS hClassConnectingProgress = XuiFindClass( L"CScene_ConnectingProgress" ); + + if( XuiIsInstanceOf( hSceneFrom, hClassFullscreenProgress ) || XuiIsInstanceOf( hSceneFrom, hClassConnectingProgress ) ) + + { + + UpdateGamesList(); + + } + +#endif + + } + + else + + { + + m_buttonListGames.clearList(); + + m_controlJoinTimer.setVisible(false); + + if(m_bHasNoGamesLabel) m_labelNoGames.setVisible(false); + +#if TO_BE_IMPLEMENTED + + m_SavesList.InitFocus(m_iPad); + +#endif + + } + + + + if(app.GetCorruptSaveDeleted()) + + { + + m_iState=e_SavesRepopulateAfterDelete; + + app.SetCorruptSaveDeleted(false); + + } + + } + +} + +void UIScene_LoadCreateJoinMenu::handleLoseFocus() + +{ + + // Kill load online timer + + killTimer(JOIN_LOAD_ONLINE_TIMER_ID); + +} + + + +UIControl* UIScene_LoadCreateJoinMenu::GetMainPanel() + +{ + // This movie does not reliably expose a root "MainPanel" value path in all + // forks/builds. Mouse hover hit-testing in UIController depends on the main + // panel for offsets and descendant filtering, so return the currently-active + // tab container instead of the phantom root panel. + switch (m_activeTab) + { + case eTab_Load: + return &m_controlLoadGame; + case eTab_Create: + return &m_controlNewGame; + case eTab_Join: + return &m_controlJoinGame; + default: + return nullptr; + } + +} + +#ifdef _WINDOWS64 +bool UIScene_LoadCreateJoinMenu::ConvertMouseToSceneCoords(float &sceneMouseX, float &sceneMouseY) +{ + if (g_KBMInput.IsMouseGrabbed() || !g_KBMInput.IsKBMActive()) + return false; + + sceneMouseX = static_cast(g_KBMInput.GetMouseX()); + sceneMouseY = static_cast(g_KBMInput.GetMouseY()); + + extern HWND g_hWnd; + RECT rc = {}; + if (!g_hWnd || !GetClientRect(g_hWnd, &rc)) + return false; + + const int winW = rc.right - rc.left; + const int winH = rc.bottom - rc.top; + if (winW <= 0 || winH <= 0) + return false; + + const float screenX = sceneMouseX * (ui.getScreenWidth() / static_cast(winW)); + const float screenY = sceneMouseY * (ui.getScreenHeight() / static_cast(winH)); + + C4JRender::eViewportType vp = GetParentLayer()->getViewport(); + S32 displayW = 0, displayH = 0; + + F32 vpOriginX = 0, vpOriginY = 0, vpW = 0, vpH = 0; + GetViewportRect(ui.getScreenWidth(), ui.getScreenHeight(), vp, vpOriginX, vpOriginY, vpW, vpH); + + S32 fitW = 0, fitH = 0, fitOffsetX = 0, fitOffsetY = 0; + Fit16x9(vpW, vpH, fitW, fitH, fitOffsetX, fitOffsetY); + + const S32 originX = static_cast(vpOriginX) + fitOffsetX; + const S32 originY = static_cast(vpOriginY) + fitOffsetY; + displayW = fitW; + displayH = fitH; + + if (displayW <= 0 || displayH <= 0) + return false; + + sceneMouseX = (screenX - originX) * (static_cast(getRenderWidth()) / static_cast(displayW)); + sceneMouseY = (screenY - originY) * (static_cast(getRenderHeight()) / static_cast(displayH)); + return true; +} + +void UIScene_LoadCreateJoinMenu::GetAbsoluteControlRect(UIControl *pControl, S32 &x, S32 &y, S32 &w, S32 &h) +{ + x = y = w = h = 0; + if (!pControl) + return; + + pControl->UpdateControl(); + x = pControl->getXPos(); + y = pControl->getYPos(); + w = pControl->getWidth(); + h = pControl->getHeight(); + + UIControl *panel = pControl->getParentPanel(); + while (panel) + { + panel->UpdateControl(); + x += panel->getXPos(); + y += panel->getYPos(); + panel = panel->getParentPanel(); + } +} + +void UIScene_LoadCreateJoinMenu::UpdateMouseHoverForActiveTab() +{ + if (!hasFocus(m_iPad) || getMovie() == nullptr || g_KBMInput.IsMouseGrabbed() || !g_KBMInput.IsKBMActive()) + return; + + if (m_bIgnoreInput) + return; + + const int rawMouseX = g_KBMInput.GetMouseX(); + const int rawMouseY = g_KBMInput.GetMouseY(); + if (m_mouseHoverTracked && rawMouseX == m_lastHoverMouseX && rawMouseY == m_lastHoverMouseY) + return; + + m_lastHoverMouseX = rawMouseX; + m_lastHoverMouseY = rawMouseY; + m_mouseHoverTracked = true; + + float sceneMouseX = 0.0f; + float sceneMouseY = 0.0f; + if (!ConvertMouseToSceneCoords(sceneMouseX, sceneMouseY)) + return; + + UIControl_ButtonList *pActiveList = nullptr; + switch (m_activeTab) + { + case eTab_Load: + pActiveList = &m_buttonListSaves; + break; + case eTab_Create: + pActiveList = &m_buttonListNewGames; + break; + case eTab_Join: + pActiveList = &m_buttonListGames; + break; + default: + break; + } + + if (pActiveList == nullptr || pActiveList->getHidden() || !pActiveList->getVisible()) + return; + + S32 cx = 0, cy = 0, cw = 0, ch = 0; + GetAbsoluteControlRect(pActiveList, cx, cy, cw, ch); + if (cw <= 0 || ch <= 0) + return; + + if (sceneMouseX < cx || sceneMouseX > cx + cw || sceneMouseY < cy || sceneMouseY > cy + ch) + return; + + const int itemCount = pActiveList->getItemCount(); + if (itemCount <= 0) + return; + + int visibleRows = 7; + float topInset = 6.0f; + float bottomInset = 22.0f; + switch (m_activeTab) + { + case eTab_Load: + visibleRows = 7; + bottomInset = 22.0f; + break; + case eTab_Create: + visibleRows = 7; + bottomInset = 22.0f; + break; + case eTab_Join: + visibleRows = 7; + bottomInset = 22.0f; + break; + default: + break; + } + const float contentTop = static_cast(cy) + topInset; + const float contentBottom = static_cast(cy + ch) - bottomInset; + if (contentBottom <= contentTop) + return; + + if (sceneMouseY < contentTop || sceneMouseY > contentBottom) + return; + + const float rowHeight = (contentBottom - contentTop) / static_cast(visibleRows); + if (rowHeight <= 0.0f) + return; + + const float hoverOffset = (sceneMouseY - contentTop) / rowHeight; + int hoverRow = static_cast(hoverOffset); + if (hoverRow < 0) + hoverRow = 0; + if (hoverRow >= visibleRows) + hoverRow = visibleRows - 1; + const float hoverRowFraction = hoverOffset - static_cast(hoverRow); + + int *pBaseIndex = nullptr; + switch (m_activeTab) + { + case eTab_Load: + pBaseIndex = &m_hoverBaseIndexLoad; + break; + case eTab_Create: + pBaseIndex = &m_hoverBaseIndexCreate; + break; + case eTab_Join: + pBaseIndex = &m_hoverBaseIndexJoin; + break; + default: + return; + } + + const int maxBaseIndex = (itemCount - visibleRows > 0) ? (itemCount - visibleRows) : 0; + if (*pBaseIndex < 0) + *pBaseIndex = 0; + if (*pBaseIndex > maxBaseIndex) + *pBaseIndex = maxBaseIndex; + + if (itemCount > visibleRows) + { + if (hoverRow == 0 && hoverRowFraction < 0.30f && *pBaseIndex > 0) + { + --(*pBaseIndex); + } + else if (hoverRow == (visibleRows - 1) && hoverRowFraction > 0.70f && *pBaseIndex < maxBaseIndex) + { + ++(*pBaseIndex); + } + } + + const int targetChildIndex = ((*pBaseIndex + hoverRow) < (itemCount - 1)) ? (*pBaseIndex + hoverRow) : (itemCount - 1); + + switch (m_activeTab) + { + case eTab_Load: + if (m_iSaveListIndex == targetChildIndex) + return; + m_iSaveListIndex = targetChildIndex; + m_bUpdateSaveSize = true; + break; + + case eTab_Create: + if (m_iNewGameListIndex == targetChildIndex) + return; + m_iNewGameListIndex = targetChildIndex; + break; + + case eTab_Join: + { +#ifdef _WINDOWS64 + const int targetGameIndex = targetChildIndex - 1; +#else + const int targetGameIndex = targetChildIndex; +#endif + if (m_iGameListIndex == targetGameIndex) + return; + m_iGameListIndex = targetGameIndex; + break; + } + + default: + return; + } + + updateTooltips(); + m_bPendingSaveSizeBarRefresh = true; + m_bPendingJoinTabAvailabilityRefresh = true; +} +#endif +wstring UIScene_LoadCreateJoinMenu::getMoviePath() + +{ + + app.DebugPrintf("[LCJ] getMoviePath -> LoadCreateJoinMenu\n"); + + return L"LoadCreateJoinMenu"; + +} + + + +int UIScene_LoadCreateJoinMenu::GetMovieTabFromFocus() + +{ + + if (DoesGamesListHaveFocus()) + + return 2; + + + + if (m_buttonListNewGames.hasFocus()) + + return 1; + + + + if (m_buttonListSaves.hasFocus()) + + return 0; + + + + return 0; + +} + +void UIScene_LoadCreateJoinMenu::SetMovieTab(int tab) + +{ + + if (getMovie() == nullptr) + + return; + + + + IggyDataValue result; + + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_number; + + value[0].number = static_cast(tab); + + IggyPlayerCallMethodRS(getMovie(), &result, IggyPlayerRootPath(getMovie()), m_funcSetActiveTab, 1, value); + +} + + + +void UIScene_LoadCreateJoinMenu::SyncMovieTab() + +{ + + SetMovieTab(GetMovieTabFromFocus()); + +} + + + +void UIScene_LoadCreateJoinMenu::SetActiveTab(ELoadCreateJoinTab tab, bool setFocus) + +{ + + m_activeTab = tab; + + SetMovieTab((int)tab); + + ApplyTabVisibility(setFocus); + +} + + + +void UIScene_LoadCreateJoinMenu::ApplyTabVisibility(bool setFocus) + +{ + // keep load create and join logic split here or focus gets weird + + const bool showLoad = (m_activeTab == eTab_Load); + + const bool showCreate = (m_activeTab == eTab_Create); + + const bool showJoin = (m_activeTab == eTab_Join); + + + + m_controlLoadGame.setVisible(showLoad); + + m_controlLoadGamePanel.setVisible(showLoad); + + m_buttonListSaves.setVisible(showLoad); + + + + m_controlNewGame.setVisible(showCreate); + + m_controlNewGamePanel.setVisible(showCreate); + + m_buttonListNewGames.setVisible(showCreate); + + + + m_controlJoinGame.setVisible(showJoin); + + m_controlJoinGamePanel.setVisible(showJoin); + + m_buttonListGames.setVisible(showJoin); + + if (m_bHasNoGamesLabel) + m_labelNoGames.setVisible(showJoin && m_buttonListGames.getItemCount() == 0); + + + + if (setFocus) + + { + + if (showLoad) + + SetFocusToElement(eControl_SavesList); + + else if (showCreate) + + SetFocusToElement(eControl_NewGamesList); + + else if (showJoin) + + SetFocusToElement(eControl_GamesList); + + } + + + updateTooltips(); + +} + +void UIScene_LoadCreateJoinMenu::UpdateJoinTabAvailability() + +{ + + if (getMovie() == nullptr) + + return; + + + + app.DebugPrintf("[LCJ] UpdateJoinTabAvailability forced disabled count=%d\n", m_buttonListGames.getItemCount()); + + IggyDataValue result; + + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_boolean; + + value[0].boolval = false; + + IggyPlayerCallMethodRS(getMovie(), &result, IggyPlayerRootPath(getMovie()), m_funcSetMatchesAvailable, 1, value); + + app.DebugPrintf("[LCJ] UpdateJoinTabAvailability exit\n"); + +} + + + +void UIScene_LoadCreateJoinMenu::UpdateSaveSizeBarVisibility() + +{ + + if (getMovie() == nullptr) + + return; + + + + const bool showSaveSizeBar = (m_activeTab == eTab_Load); + + app.DebugPrintf("[LCJ] UpdateSaveSizeBarVisibility enter show=%d\n", showSaveSizeBar ? 1 : 0); + app.DebugPrintf("[LCJ] SaveSizeBar pending=%d activeTab=%d\n", + m_bPendingSaveSizeBarRefresh ? 1 : 0, + static_cast(m_activeTab)); + + + + IggyDataValue result; + + IggyDataValue value[1]; + + value[0].type = IGGY_DATATYPE_boolean; + + value[0].boolval = showSaveSizeBar; + + IggyPlayerCallMethodRS(getMovie(), &result, IggyPlayerRootPath(getMovie()), m_funcShowSaveSizeBar, 1, value); + + app.DebugPrintf("[LCJ] UpdateSaveSizeBarVisibility exit\n"); + +} + +void UIScene_LoadCreateJoinMenu::tick() + +{ + + UIScene::tick(); + +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined _WINDOWS64 || defined __PSVITA__) + + if(m_bExitScene) // navigate forward or back + + { + + if(!m_bRetrievingSaveThumbnails) + + { + + // need to wait for any callback retrieving thumbnail to complete + + navigateBack(); + + } + + } + + // Stop loading thumbnails if we navigate forwards + + if(hasFocus(m_iPad)) + + { + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + + // if the loadOrJoin menu has focus again, we can clear the saveTransfer flag now. Added so we can delay the ehternet disconnect till it's cleaned up + + if(m_eSaveTransferState == eSaveTransfer_Idle) + + m_bSaveTransferRunning = false; + +#endif + +#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64) + + if(m_bUpdateSaveSize) + + { + + if((m_activeTab == eTab_Load) && (m_pSaveDetails != nullptr) && (m_pSaveDetails->iSaveC > 0) && DoesSavesListHaveFocus() && (m_buttonListSaves.getItemCount() > 0) && (m_iSaveListIndex >= 0) && (m_iSaveListIndex < m_buttonListSaves.getItemCount())) + + { + + m_spaceIndicatorSaves.selectSave(m_iSaveListIndex); + + } + + else + + { + + m_spaceIndicatorSaves.selectSave(-1); + + } + + UpdateSaveSizeBarVisibility(); + + m_bUpdateSaveSize = false; + + } + + if(m_bPendingSaveSizeBarRefresh) + { + UpdateSaveSizeBarVisibility(); + m_bPendingSaveSizeBarRefresh = false; + } + + if(m_bPendingJoinTabAvailabilityRefresh) + { + UpdateJoinTabAvailability(); + m_bPendingJoinTabAvailabilityRefresh = false; + } + +#endif + + // Display the saves if we have them + + if(!m_bSavesDisplayed) + + { + + m_pSaveDetails=StorageManager.ReturnSavesInfo(); + + if(m_pSaveDetails!=nullptr) + + { + + //CD - Fix - Adding define for ORBIS/XBOXONE + +#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64) + + m_spaceIndicatorSaves.reset(); + +#endif + + + + AddDefaultButtons(); + + m_bSavesDisplayed=true; + + UpdateGamesList(); + m_bIgnoreInput = false; + + + + if(m_saveDetails!=nullptr) + + { + + for(unsigned int i = 0; i < m_iSaveDetailsCount; ++i) + + { + + if(m_saveDetails[i].pbThumbnailData!=nullptr) + + { + + delete m_saveDetails[i].pbThumbnailData; + + } + + } + + delete m_saveDetails; + + } + + m_saveDetails = new SaveListDetails[m_pSaveDetails->iSaveC]; + + + + m_iSaveDetailsCount = m_pSaveDetails->iSaveC; + +#ifdef _WINDOWS64 + + // Build sorted index array (newest-first by filename timestamp YYYYMMDDHHMMSS) + + int *sortedIdx = new int[m_pSaveDetails->iSaveC]; + + for (int si = 0; si < (int)m_pSaveDetails->iSaveC; ++si) sortedIdx[si] = si; + + for (int si = 1; si < (int)m_pSaveDetails->iSaveC; ++si) + + { + + int key = sortedIdx[si]; + + int sj = si - 1; + + while (sj >= 0 && strcmp(m_pSaveDetails->SaveInfoA[sortedIdx[sj]].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[key].UTF8SaveFilename) < 0) + + { + + sortedIdx[sj + 1] = sortedIdx[sj]; + + --sj; + + } + + sortedIdx[sj + 1] = key; + + } + +#endif + + for(unsigned int i = 0; i < m_pSaveDetails->iSaveC; ++i) + + { + +#if defined(_XBOX_ONE) + + m_spaceIndicatorSaves.addSave(m_pSaveDetails->SaveInfoA[i].totalSize); + +#elif defined(_WINDOWS64) + + int origIdx = sortedIdx[i]; + + wchar_t wFilename[MAX_SAVEFILENAME_LENGTH]; + + ZeroMemory(wFilename, sizeof(wFilename)); + + mbstowcs(wFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1); + + wstring filePath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\saveData.ms"); + + + + HANDLE hFile = CreateFileW(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); + + DWORD fileSize = 0; + + + + if (hFile != INVALID_HANDLE_VALUE) { + + fileSize = GetFileSize(hFile, nullptr); + + if (fileSize < 12 || fileSize == INVALID_FILE_SIZE) fileSize = 0; + + CloseHandle(hFile); + + } + + m_spaceIndicatorSaves.addSave(fileSize); + +#elif defined(__ORBIS__) + + m_spaceIndicatorSaves.addSave(m_pSaveDetails->SaveInfoA[i].blocksUsed * (32 * 1024) ); + +#endif + +#ifdef _DURANGO + + m_buttonListSaves.addItem(m_pSaveDetails->SaveInfoA[i].UTF16SaveTitle, L""); + + + + m_saveDetails[i].saveId = i; + + memcpy(m_saveDetails[i].UTF16SaveName, m_pSaveDetails->SaveInfoA[i].UTF16SaveTitle, 128); + + memcpy(m_saveDetails[i].UTF16SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH); + +#else + +#ifdef _WINDOWS64 + + { + + wstring levelName = ReadLevelNameFromSaveFile(filePath); + + + + if (!levelName.empty()) + + { + + m_buttonListSaves.addItem(levelName, wstring(L"")); + + wcstombs(m_saveDetails[i].UTF8SaveName, levelName.c_str(), 127); + + m_saveDetails[i].UTF8SaveName[127] = '\0'; + + } + + else + + { + + const char *saveTitle = m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveTitle; + + if(IsGeneratedSaveTitle(saveTitle)) + + { + + wstring fallbackName = GetGeneratedSaveFallbackName(m_pSaveDetails->SaveInfoA[origIdx]); + + m_buttonListSaves.addItem(fallbackName, wstring(L"")); + + wcstombs(m_saveDetails[i].UTF8SaveName, fallbackName.c_str(), 127); + + m_saveDetails[i].UTF8SaveName[127] = '\0'; + + } + + else + + { + + m_buttonListSaves.addItem(saveTitle, L""); + + memcpy(m_saveDetails[i].UTF8SaveName, saveTitle, 128); + + } + + } + + m_saveDetails[i].saveId = origIdx; + + memcpy(m_saveDetails[i].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[origIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH); + + if(m_pSaveDetails->SaveInfoA[origIdx].thumbnailData != nullptr && m_pSaveDetails->SaveInfoA[origIdx].metaData.thumbnailSize > 0) + { + m_saveDetails[i].dwThumbnailSize = m_pSaveDetails->SaveInfoA[origIdx].metaData.thumbnailSize; + m_saveDetails[i].pbThumbnailData = new BYTE[m_saveDetails[i].dwThumbnailSize]; + memcpy(m_saveDetails[i].pbThumbnailData, m_pSaveDetails->SaveInfoA[origIdx].thumbnailData, m_saveDetails[i].dwThumbnailSize); + } + + } + +#else + + const char *saveTitle = m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle; + + if(IsGeneratedSaveTitle(saveTitle)) + + { wstring fallbackName = GetGeneratedSaveFallbackName(m_pSaveDetails->SaveInfoA[i]); + + m_buttonListSaves.addItem(fallbackName, wstring(L"")); + + wcstombs(m_saveDetails[i].UTF8SaveName, fallbackName.c_str(), 127); + + m_saveDetails[i].UTF8SaveName[127] = '\0'; + + } + + else + + { + + m_buttonListSaves.addItem(saveTitle, L""); + + memcpy(m_saveDetails[i].UTF8SaveName, saveTitle, 128); + + } + + m_saveDetails[i].saveId = i; + + memcpy(m_saveDetails[i].UTF8SaveFilename, m_pSaveDetails->SaveInfoA[i].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH); + +#endif + +#endif + + } + +#ifdef _WINDOWS64 + + delete[] sortedIdx; + +#endif + + m_controlSavesTimer.setVisible( false ); + updateTooltips(); + + + + // set focus on the first button + + + + } + + } + + + + if(!m_bExitScene && m_bSavesDisplayed && !m_bRetrievingSaveThumbnails && !m_bAllLoaded) + + { + + if( m_iRequestingThumbnailId < m_buttonListSaves.getItemCount()) + + { + +#ifdef _WINDOWS64 + if(m_saveDetails[m_iRequestingThumbnailId].pbThumbnailData != nullptr && m_saveDetails[m_iRequestingThumbnailId].dwThumbnailSize > 0) + { + app.DebugPrintf("Using preloaded save thumbnail\n"); + m_bSaveThumbnailReady = true; + } + else +#endif + { + m_bRetrievingSaveThumbnails = true; + + app.DebugPrintf("Requesting the first thumbnail\n"); + + // set the save to load + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + +#ifdef _WINDOWS64 + + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[m_saveDetails[m_iRequestingThumbnailId].saveId],&LoadSaveDataThumbnailReturned,this); + +#else + + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this); + +#endif + + + + if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail) + + { + + // something went wrong + + m_bRetrievingSaveThumbnails=false; + + m_bAllLoaded = true; + + } + + } + + } + + } + + else if (m_bSavesDisplayed && m_bSaveThumbnailReady) + + { + + m_bSaveThumbnailReady = false; + + + + // check we're not waiting to exit the scene + + if(!m_bExitScene) + + { + + // convert to utf16 + + uint16_t u16Message[MAX_SAVEFILENAME_LENGTH]; + +#ifdef _DURANGO + + // Already utf16 on durango + + memcpy(u16Message, m_saveDetails[m_iRequestingThumbnailId].UTF16SaveFilename, MAX_SAVEFILENAME_LENGTH); + +#elif defined(_WINDOWS64) + + int result = ::MultiByteToWideChar( + + CP_UTF8, // convert from UTF-8 + + MB_ERR_INVALID_CHARS, // error on invalid chars + + m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename, // source UTF-8 string + + MAX_SAVEFILENAME_LENGTH, // total length of source UTF-8 string, + + // in CHAR's (= bytes), including end-of-string \0 + + (wchar_t *)u16Message, // destination buffer + + MAX_SAVEFILENAME_LENGTH // size of destination buffer, in WCHAR's + + ); + +#else + +#ifdef __PS3 + + size_t srcmax,dstmax; + +#else + + uint32_t srcmax,dstmax; + + uint32_t srclen,dstlen; + +#endif + + srcmax=MAX_SAVEFILENAME_LENGTH; + + dstmax=MAX_SAVEFILENAME_LENGTH; + + + +#if defined(__PS3__) + + L10nResult lres= UTF8stoUTF16s((uint8_t *)m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename,&srcmax,u16Message,&dstmax); + +#else + + SceCesUcsContext context; + + sceCesUcsContextInit(&context); + + + + sceCesUtf8StrToUtf16Str(&context, (uint8_t *)m_saveDetails[m_iRequestingThumbnailId].UTF8SaveFilename,srcmax,&srclen,u16Message,dstmax,&dstlen); + +#endif + +#endif + + const int saveId = m_saveDetails[m_iRequestingThumbnailId].saveId; + + if( m_saveDetails[m_iRequestingThumbnailId].pbThumbnailData ) + + { + wchar_t textureName[64]; + swprintf(textureName,64,L"loadsave_large_%08x",saveId); + registerSubstitutionTexture(textureName,m_saveDetails[m_iRequestingThumbnailId].pbThumbnailData,m_saveDetails[m_iRequestingThumbnailId].dwThumbnailSize); + wcscpy((wchar_t *)u16Message, textureName); + + } + + m_buttonListSaves.setTextureName(m_iRequestingThumbnailId, (wchar_t *)u16Message); + + + + ++m_iRequestingThumbnailId; + + if( m_iRequestingThumbnailId < m_buttonListSaves.getItemCount()) + + { + +#ifdef _WINDOWS64 + if(m_saveDetails[m_iRequestingThumbnailId].pbThumbnailData != nullptr && m_saveDetails[m_iRequestingThumbnailId].dwThumbnailSize > 0) + { + app.DebugPrintf("Using preloaded save thumbnail\n"); + m_bRetrievingSaveThumbnails = false; + m_bSaveThumbnailReady = true; + } + else +#endif + { + app.DebugPrintf("Requesting another thumbnail\n"); + + // set the save to load + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + +#ifdef _WINDOWS64 + + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[m_saveDetails[m_iRequestingThumbnailId].saveId],&LoadSaveDataThumbnailReturned,this); + +#else + + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveDataThumbnail(&pSaveDetails->SaveInfoA[(int)m_iRequestingThumbnailId],&LoadSaveDataThumbnailReturned,this); + +#endif + + if(eLoadStatus!=C4JStorage::ESaveGame_GetSaveThumbnail) + + { + + // something went wrong + + m_bRetrievingSaveThumbnails=false; + + m_bAllLoaded = true; + + } + + } + + } + + else + + { + + m_bRetrievingSaveThumbnails = false; + + m_bAllLoaded = true; + + } + + } + + else + + { + + // stop retrieving thumbnails, and exit + + m_bRetrievingSaveThumbnails = false; + + } + + } + + } + + + + switch(m_iState) + + { + + case e_SavesIdle: + + break; + + case e_SavesRepopulate: + + m_bIgnoreInput = false; + + m_iState=e_SavesIdle; + + m_bAllLoaded=false; + + m_bRetrievingSaveThumbnails=false; + + m_iRequestingThumbnailId = 0; + + GetSaveInfo(); + + break; + + case e_SavesRepopulateAfterMashupHide: + + m_bIgnoreInput = false; + + m_iRequestingThumbnailId = 0; + + m_bAllLoaded=false; + + m_bRetrievingSaveThumbnails=false; + + m_bSavesDisplayed=false; + + m_iSaveInfoC=0; + + m_buttonListSaves.clearList(); + + GetSaveInfo(); + + m_iState=e_SavesIdle; + + break; + + case e_SavesRepopulateAfterDelete: + + case e_SavesRepopulateAfterTransferDownload: + + m_bIgnoreInput = false; + + m_iRequestingThumbnailId = 0; + + m_bAllLoaded=false; + + m_bRetrievingSaveThumbnails=false; + + m_bSavesDisplayed=false; + + m_iSaveInfoC=0; + + m_buttonListSaves.clearList(); + + StorageManager.ClearSavesInfo(); + + GetSaveInfo(); + + m_iState=e_SavesIdle; + + break; + + } + +#else + + if(!m_bSavesDisplayed) + + { + + AddDefaultButtons(); + + m_bSavesDisplayed=true; + + m_controlSavesTimer.setVisible( false ); + + } + +#endif + + + +#ifdef _XBOX_ONE + + if(g_NetworkManager.ShouldMessageForFullSession()) + + { + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + ui.RequestErrorMessage( IDS_CONNECTION_FAILED, IDS_IN_PARTY_SESSION_FULL, uiIDA,1,ProfileManager.GetPrimaryPad()); + + } + +#endif + + + + // SAVE TRANSFERS + +#ifdef __ORBIS__ + + // check the status of the PSPlus common dialog + + switch (sceNpCommerceDialogUpdateStatus()) + + { + + case SCE_COMMON_DIALOG_STATUS_FINISHED: + + { + + SceNpCommerceDialogResult Result; + + sceNpCommerceDialogGetResult(&Result); + + sceNpCommerceDialogTerminate(); + + + + if(Result.authorized) + + { + + // they just became a PSPlus member + + ProfileManager.PsPlusUpdate(ProfileManager.GetPrimaryPad(), &Result); + + + + } + + else + + { + + + + } + + + + // 4J-JEV: Fix for PS4 #5148 - [ONLINE] If the user attempts to join a game when they do not have Playstation Plus, the title will lose all functionality. + + m_bIgnoreInput = false; + + } + + break; + + default: + + break; + + } + +#endif + + + +} + + +void UIScene_LoadCreateJoinMenu::GetSaveInfo() + +{ + + unsigned int uiSaveC=0; + + + + // This will return with the number retrieved in uiSaveC + + + + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + + { + +#ifdef __ORBIS__ + + // We need to make sure this is non-null so that we have an idea of free space + + m_pSaveDetails=StorageManager.ReturnSavesInfo(); + + if(m_pSaveDetails==nullptr) + + { + + C4JStorage::ESaveGameState eSGIStatus= StorageManager.GetSavesInfo(m_iPad,nullptr,this,"save"); + + } + +#endif + + + + uiSaveC = 0; + +#ifdef _XBOX + + File savesDir(L"GAME:\\Saves"); + +#else + + File savesDir(L"Saves"); + +#endif + + if( savesDir.exists() ) + + { + + m_saves = savesDir.listFiles(); + + uiSaveC = static_cast(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; + + + + AddDefaultButtons(); + + + + for(unsigned int i=0;iat(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; + + m_buttonListSaves.addItem(name,L""); + + } + + m_bSavesDisplayed = true; + + m_bAllLoaded = true; + + m_bIgnoreInput = false; + + } + + else + + { + + // clear the saves list + + m_bSavesDisplayed = false; // we're blocking the exit from this scene until complete + + m_buttonListSaves.clearList(); + + m_iSaveInfoC=0; + + m_controlSavesTimer.setVisible(true); + + + + m_pSaveDetails=StorageManager.ReturnSavesInfo(); + + if(m_pSaveDetails==nullptr) + + { + + C4JStorage::ESaveGameState eSGIStatus= StorageManager.GetSavesInfo(m_iPad, nullptr,this,"save"); + + } + + + +#if TO_BE_IMPLEMENTED + + if(eSGIStatus==C4JStorage::ESGIStatus_NoSaves) + + { + + uiSaveC=0; + + m_controlSavesTimer.setVisible( false ); + + m_SavesList.SetEnable(TRUE); + + } + +#endif + + } + + + + return; + +} + + + +void UIScene_LoadCreateJoinMenu::AddDefaultButtons() + +{ + // hidden pack entries need a full rebuild or the list can duplicate + // create list uses its own index path do not reuse load index here + // keep all mashup worlds available in create on legacy evolved + app.EnableMashupPackWorlds(m_iPad); + + m_iDefaultButtonsC = 0; + + m_iMashUpButtonsC=0; + + m_iNewGameListIndex = 0; + + m_buttonListNewGames.clearList(); + + m_generators.clear(); + + + + m_buttonListNewGames.addItem(app.GetString(IDS_CREATE_NEW_WORLD)); + +#ifdef _WINDOWS64 + TrySetButtonListIcon( + m_buttonListNewGames, + m_buttonListNewGames.getItemCount() - 1, + L"CreateWorldIcon.png", + L"CreateWorldIcon"); +#endif + + m_iDefaultButtonsC++; + + + + int i = 0; + + + + for ( LevelGenerationOptions *levelGen : *app.getLevelGenerators() ) + + { + + // retrieve the save icon from the texture pack, if there is one + + 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; + + } + + } + + + + // 4J-JEV: For debug. Ignore worlds with no name. + + LPCWSTR wstr = levelGen->getWorldName(); + + m_buttonListNewGames.addItem( wstr ); + + m_generators.push_back(levelGen); + + + +#ifdef _WINDOWS64 + if(levelGen->isTutorial()) + { + TrySetButtonListIcon( + m_buttonListNewGames, + m_buttonListNewGames.getItemCount() - 1, + L"TutorialIcon.png", + L"TutorialIcon"); + } +#endif + + 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); + + + + if(dwImageBytes > 0 && pbImageData) + + { + + wchar_t imageName[64]; + const int slotIndex = m_buttonListNewGames.getItemCount() - 1; + + swprintf(imageName,64,L"tpack_small_slot%08x",slotIndex); + + registerSubstitutionTexture(imageName, pbImageData, dwImageBytes); + + m_buttonListNewGames.setTextureName( m_buttonListNewGames.getItemCount() - 1, imageName ); + + } + + } + + + + ++i; + + } + + m_iDefaultButtonsC += i; + +} + + + +void UIScene_LoadCreateJoinMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled) + +{ + + if(m_bIgnoreInput) return; + + + + // if we're retrieving save info, ignore key presses + + if(!m_bSavesDisplayed) return; + + + + ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released); + + auto openSaveOptions = [&]() -> bool + { + if(!pressed || repeat || !ProfileManager.IsFullVersion() || m_activeTab != eTab_Load) + return false; + + if((m_pSaveDetails != nullptr) && + (m_pSaveDetails->iSaveC > 0) && + (m_buttonListSaves.getItemCount() > 0) && + (m_iSaveListIndex >= 0) && + (m_iSaveListIndex < m_buttonListSaves.getItemCount())) + { + m_bIgnoreInput = true; + + if(StorageManager.EnoughSpaceForAMinSaveGame() && !StorageManager.GetSaveDisabled()) + { + UINT uiIDA[3]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_TITLE_RENAMESAVE; + uiIDA[2]=IDS_TOOLTIPS_DELETESAVE; + ui.RequestAlertMessage(IDS_TOOLTIPS_SAVEOPTIONS, IDS_TEXT_SAVEOPTIONS, uiIDA, 3, iPad,&UIScene_LoadCreateJoinMenu::SaveOptionsDialogReturned,this); + } + else + { + UINT uiIDA[2]; + uiIDA[0]=IDS_CONFIRM_CANCEL; + uiIDA[1]=IDS_CONFIRM_OK; + ui.RequestAlertMessage(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, iPad,&UIScene_LoadCreateJoinMenu::DeleteSaveDialogReturned,this); + } + + ui.PlayUISFX(eSFX_Press); + return true; + } + + return false; + }; + + auto hideFocusedMashupWorld = [&]() -> bool + { + if(!pressed || repeat || !ProfileManager.IsFullVersion() || !DoesMashUpWorldHaveFocus()) + return false; + + const int lGenID = m_iNewGameListIndex - m_iDefaultButtonsC; + if((lGenID < 0) || (lGenID >= static_cast(m_generators.size()))) + return false; + + LevelGenerationOptions *levelGen = m_generators.at(lGenID); + if(!levelGen || levelGen->isTutorial() || !levelGen->requiresTexturePack()) + return false; + + const int oldIndex = m_iNewGameListIndex; + m_bIgnoreInput = true; + app.HideMashupPackWorld(m_iPad, levelGen->getRequiredTexturePackId()); + AddDefaultButtons(); + + if(m_buttonListNewGames.getItemCount() <= 0) + { + m_iNewGameListIndex = 0; + } + else if(oldIndex >= m_buttonListNewGames.getItemCount()) + { + m_iNewGameListIndex = m_buttonListNewGames.getItemCount() - 1; + } + else if(oldIndex >= 0) + { + m_iNewGameListIndex = oldIndex; + } + + if(m_buttonListNewGames.getItemCount() > 0) + { + m_buttonListNewGames.setCurrentSelection(m_iNewGameListIndex); + } + + updateTooltips(); + m_iState = e_SavesIdle; + ui.PlayUISFX(eSFX_Press); + m_bIgnoreInput = false; + return true; + }; + + switch(key) + + { + + case ACTION_MENU_CANCEL: + + if(pressed) + + { + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + + m_bExitScene=true; + +#else + + navigateBack(); + +#endif + + handled = true; + + } + + break; + + case ACTION_MENU_X: + +#if TO_BE_IMPLEMENTED + + // 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); + + ui.PlayUISFX(eSFX_Press); + +#endif + + // Save Transfer + +#ifdef _XBOX_ONE + + if(ProfileManager.IsSignedInLive( m_iPad )) + + { + + UIScene_LoadCreateJoinMenu::s_ulFileSize=0; + + LaunchSaveTransfer(); + + } + +#endif + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + + { + + bool bSignedInLive = ProfileManager.IsSignedInLive(iPad); + + if(bSignedInLive) + + { + + LaunchSaveTransfer(); + + } + + } + +#endif + +#ifdef _WINDOWS64 + + // Right click on a save opens save options. In Create, it hides mash-up worlds. + + if(hideFocusedMashupWorld()) + { + handled = true; + } + else if(openSaveOptions()) + + { + handled = true; + } + +#endif + + break; + + case ACTION_MENU_Y: + +#ifdef _WINDOWS64 + if(hideFocusedMashupWorld()) + { + handled = true; + break; + } + + if(openSaveOptions()) + { + handled = true; + break; + } +#endif + +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + + m_eAction = eAction_ViewInvites; + + if(pressed && iPad == ProfileManager.GetPrimaryPad()) + + { + +#ifdef __ORBIS__ + + // Check if PSN is unavailable because of age restriction + + int npAvailability = ProfileManager.getNPAvailability(iPad); + + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + + { + + UINT uiIDA[1]; + + uiIDA[0] = IDS_OK; + + ui.RequestErrorMessage(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPad); + + + + break; + + } + +#endif + + + + // are we offline? + + if(!ProfileManager.IsSignedInLive(iPad)) + + { + + // get them to sign in to online + + UINT uiIDA[2]; + + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + + ui.RequestAlertMessage(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA, 2, ProfileManager.GetPrimaryPad(), &UIScene_LoadCreateJoinMenu::MustSignInReturnedPSN, this); + + } + + else + + { + +#ifdef __ORBIS__ + + SQRNetworkManager_Orbis::RecvInviteGUI(); + +#elif defined __PSVITA__ + + SQRNetworkManager_Vita::RecvInviteGUI(); + +#else + + int ret = sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); + + app.DebugPrintf("sceNpBasicRecvMessageCustom return %d ( %08x )\n", ret, ret); + +#endif + + } + + } + +#elif defined(_DURANGO) + + if(getControlFocus() == eControl_GamesList && m_buttonListGames.getItemCount() > 0) + + { + + DWORD nIndex = m_buttonListGames.getCurrentSelection(); + + FriendSessionInfo *pSelectedSession = m_currentSessions->at( nIndex ); + + + + PlayerUID uid = pSelectedSession->searchResult.m_playerXuids[0]; + + if( uid != INVALID_XUID ) ProfileManager.ShowProfileCard(ProfileManager.GetLockedProfile(),uid); + + ui.PlayUISFX(eSFX_Press); + + } + +#endif // __PS3__ || __ORBIS__ + + break; + + + + case ACTION_MENU_RIGHT_SCROLL: + + if(pressed && !repeat) + + { + + ui.PlayUISFX(eSFX_Focus); + + if (m_activeTab == eTab_Load) SetActiveTab(eTab_Create, true); + + else if (m_activeTab == eTab_Create) SetActiveTab(eTab_Join, true); + + else SetActiveTab(eTab_Load, true); + + handled = true; + + break; + + } + + + else if(hideFocusedMashupWorld()) + + { + handled = true; + + } + + break; + + + + case ACTION_MENU_LEFT_SCROLL: + + if(pressed && !repeat) + + { + + ui.PlayUISFX(eSFX_Focus); + + if (m_activeTab == eTab_Load) SetActiveTab(eTab_Join, true); + + else if (m_activeTab == eTab_Create) SetActiveTab(eTab_Load, true); + + else SetActiveTab(eTab_Create, true); + + handled = true; + + break; + + } + +#ifdef _XBOX + + if( m_bInParty ) + + { + + m_bShowingPartyGamesOnly = !m_bShowingPartyGamesOnly; + + UpdateGamesList(); + + CXuiSceneBase::PlayUISFX(eSFX_Press); + + } + +#endif + + break; + + + + case ACTION_MENU_LEFT: + + if(pressed && !repeat) + { + ui.PlayUISFX(eSFX_Focus); + if (m_activeTab == eTab_Load) SetActiveTab(eTab_Join, true); + else if (m_activeTab == eTab_Create) SetActiveTab(eTab_Load, true); + else SetActiveTab(eTab_Create, true); + } + + handled = true; + + break; + + case ACTION_MENU_RIGHT: + + if(pressed && !repeat) + { + ui.PlayUISFX(eSFX_Focus); + if (m_activeTab == eTab_Load) SetActiveTab(eTab_Create, true); + else if (m_activeTab == eTab_Create) SetActiveTab(eTab_Join, true); + else SetActiveTab(eTab_Load, true); + } + + handled = true; + + break; + + + + + + case ACTION_MENU_OK: + +#ifdef __ORBIS__ + + case ACTION_MENU_TOUCHPAD_PRESS: + +#endif + + case ACTION_MENU_UP: + + case ACTION_MENU_DOWN: + + case ACTION_MENU_PAGEUP: + + case ACTION_MENU_PAGEDOWN: + + sendInputToMovie(key, repeat, pressed, released); + + handled = true; + + break; + + case ACTION_MENU_OTHER_STICK_UP: + + sendInputToMovie(ACTION_MENU_UP, repeat, pressed, released); + + handled = true; + + break; + + case ACTION_MENU_OTHER_STICK_DOWN: + + sendInputToMovie(ACTION_MENU_DOWN, repeat, pressed, released); + + handled = true; + + break; + + } + +} + + + +int UIScene_LoadCreateJoinMenu::KeyboardCompleteWorldNameCallback(LPVOID lpParam,bool bRes) + +{ + + // 4J HEG - No reason to set value if keyboard was cancelled + + UIScene_LoadCreateJoinMenu *pClass=static_cast(lpParam); + + pClass->m_bIgnoreInput=false; + + if (bRes) + + { + + uint16_t ui16Text[128]; + + ZeroMemory(ui16Text, 128 * sizeof(uint16_t) ); + +#ifdef _WINDOWS64 + + Win64_GetKeyboardText(ui16Text, 128); + +#else + + InputManager.GetText(ui16Text); + +#endif + + + + // check the name is valid + + if(ui16Text[0]!=0) + + { + +#if (defined __PS3__ || defined __ORBIS__ || defined _DURANGO || defined(__PSVITA__)) + + // open the save and overwrite the metadata + + StorageManager.RenameSaveData(pClass->m_iSaveListIndex, ui16Text,&UIScene_LoadCreateJoinMenu::RenameSaveDataReturned,pClass); + +#elif defined(_WINDOWS64) + + { + + int listPos = pClass->m_iSaveListIndex; + + + + // Convert the ui16Text input to a wide string + + wchar_t wNewName[128] = {}; + + for (int k = 0; k < 127 && ui16Text[k]; k++) + + wNewName[k] = static_cast(ui16Text[k]); + + + + // Convert to narrow for storage and in-memory update + + char narrowName[128] = {}; + + wcstombs(narrowName, wNewName, 127); + + + + // Build the sidecar path: Windows64\GameHDD\{folder}\worldname.txt + + wchar_t wFilename[MAX_SAVEFILENAME_LENGTH] = {}; + + mbstowcs(wFilename, pClass->m_saveDetails[listPos].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1); + + wstring sidecarPath = wstring(L"Windows64\\GameHDD\\") + wstring(wFilename) + wstring(L"\\worldname.txt"); + + + + FILE *fw = nullptr; + + if (_wfopen_s(&fw, sidecarPath.c_str(), L"w") == 0 && fw) + + { + + fputs(narrowName, fw); + + fclose(fw); + + } + + + + // Update the in-memory display name so the list reflects it immediately + + strncpy_s(pClass->m_saveDetails[listPos].UTF8SaveName, narrowName, 127); + + pClass->m_saveDetails[listPos].UTF8SaveName[127] = '\0'; + + + + // Reuse the existing callback to trigger the list repopulate + + UIScene_LoadCreateJoinMenu::RenameSaveDataReturned(pClass, true); + + } + +#endif + + } + + else + + { + + pClass->m_bIgnoreInput=false; + + pClass->updateTooltips(); + + } + + } + + else + + { + + pClass->m_bIgnoreInput=false; + + pClass->updateTooltips(); + + } + + + + + + return 0; + +} + +void UIScene_LoadCreateJoinMenu::handleInitFocus(F64 controlId, F64 childId) + +{ + + app.DebugPrintf(app.USER_SR, "UIScene_LoadCreateJoinMenu::handleInitFocus - %d , %d\n", static_cast(controlId), static_cast(childId)); + + SetActiveTab(m_activeTab, false); + m_bPendingSaveSizeBarRefresh = true; + m_bPendingJoinTabAvailabilityRefresh = true; + +} + +void UIScene_LoadCreateJoinMenu::handleFocusChange(F64 controlId, F64 childId) + +{ + + app.DebugPrintf(app.USER_SR, "UIScene_LoadCreateJoinMenu::handleFocusChange - %d , %d\n", static_cast(controlId), static_cast(childId)); + + const int visibleRows = 7; + + + + switch(static_cast(controlId)) + + { + + case eControl_TabLoad: + + m_activeTab = eTab_Load; + + break; + + case eControl_TabCreate: + + m_activeTab = eTab_Create; + + break; + + case eControl_TabJoin: + + m_activeTab = eTab_Join; + + break; + + case eControl_GamesList: + + m_activeTab = eTab_Join; + + m_iGameListIndex = childId; + +#ifdef _WINDOWS64 + + m_iGameListIndex -= 1; + +#endif + + m_buttonListGames.updateChildFocus(static_cast(childId)); + + if (childId < m_hoverBaseIndexJoin) + m_hoverBaseIndexJoin = static_cast(childId); + else if (childId >= m_hoverBaseIndexJoin + visibleRows) + m_hoverBaseIndexJoin = static_cast(childId) - visibleRows + 1; + + { + const int maxJoinBase = (m_buttonListGames.getItemCount() - visibleRows > 0) ? (m_buttonListGames.getItemCount() - visibleRows) : 0; + if (m_hoverBaseIndexJoin < 0) + m_hoverBaseIndexJoin = 0; + else if (m_hoverBaseIndexJoin > maxJoinBase) + m_hoverBaseIndexJoin = maxJoinBase; + } + + break; + + case eControl_SavesList: + + m_activeTab = eTab_Load; + + m_iSaveListIndex = childId; + + m_bUpdateSaveSize = true; + + if (childId < m_hoverBaseIndexLoad) + m_hoverBaseIndexLoad = static_cast(childId); + else if (childId >= m_hoverBaseIndexLoad + visibleRows) + m_hoverBaseIndexLoad = static_cast(childId) - visibleRows + 1; + + { + const int maxLoadBase = (m_buttonListSaves.getItemCount() - visibleRows > 0) ? (m_buttonListSaves.getItemCount() - visibleRows) : 0; + if (m_hoverBaseIndexLoad < 0) + m_hoverBaseIndexLoad = 0; + else if (m_hoverBaseIndexLoad > maxLoadBase) + m_hoverBaseIndexLoad = maxLoadBase; + } + + break; + + case eControl_NewGamesList: + + m_activeTab = eTab_Create; + + m_iNewGameListIndex = childId; + + m_buttonListNewGames.updateChildFocus(static_cast(childId)); + + if (childId < m_hoverBaseIndexCreate) + m_hoverBaseIndexCreate = static_cast(childId); + else if (childId >= m_hoverBaseIndexCreate + visibleRows) + m_hoverBaseIndexCreate = static_cast(childId) - visibleRows + 1; + + { + const int maxCreateBase = (m_buttonListNewGames.getItemCount() - visibleRows > 0) ? (m_buttonListNewGames.getItemCount() - visibleRows) : 0; + if (m_hoverBaseIndexCreate < 0) + m_hoverBaseIndexCreate = 0; + else if (m_hoverBaseIndexCreate > maxCreateBase) + m_hoverBaseIndexCreate = maxCreateBase; + } + + break; + + }; + + updateTooltips(); + + SetActiveTab(m_activeTab, false); + m_bPendingSaveSizeBarRefresh = true; + m_bPendingJoinTabAvailabilityRefresh = true; + +} + +void UIScene_LoadCreateJoinMenu::handlePress(F64 controlId, F64 childId) + +{ + + switch(static_cast(controlId)) + + { + + case eControl_TabLoad: + + ui.PlayUISFX(eSFX_Press); + + SetActiveTab(eTab_Load, true); + + return; + + + + case eControl_TabCreate: + + ui.PlayUISFX(eSFX_Press); + + SetActiveTab(eTab_Create, true); + + return; + + + + case eControl_TabJoin: + + ui.PlayUISFX(eSFX_Press); + + SetActiveTab(eTab_Join, true); + + return; + + + + case eControl_NewGamesList: + + { + + m_bIgnoreInput = true; + + ui.PlayUISFX(eSFX_Press); + + + + if(static_cast(childId) == 0) + + { + app.SetTutorialMode(false); + + m_controlJoinTimer.setVisible(false); + + app.SetCorruptSaveDeleted(false); + + + + CreateWorldMenuInitData *params = new CreateWorldMenuInitData(); + + params->iPad = m_iPad; + + ui.NavigateToScene(m_iPad, eUIScene_CreateWorldMenu, (void *)params); + + } + + else + + { + + int lGenID = static_cast(childId) - 1; + + if(lGenID < static_cast(m_generators.size())) + + { + + LevelGenerationOptions *levelGen = m_generators.at(lGenID); + + app.SetTutorialMode(levelGen->isTutorial()); + + app.SetAutosaveTimerTime(); + + + + if(levelGen->isTutorial()) + + { + + LoadLevelGen(levelGen); + + } + + else + + { + LoadMenuInitData *params = new LoadMenuInitData(); + + params->iPad = m_iPad; + + params->iSaveGameInfoIndex = -1; + + params->levelGen = levelGen; + + params->saveDetails = nullptr; + + ui.NavigateToScene(m_iPad, eUIScene_LoadMenu, params); + + } + + } + + } + + } + + break; + + + + case eControl_SavesList: + + { + + m_bIgnoreInput = true; + + ui.PlayUISFX(eSFX_Press); + + + + int saveIndex = static_cast(childId); + + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + + { + + LoadSaveFromDisk(m_saves->at(saveIndex)); + + } + + else + + { + + LoadMenuInitData *params = new LoadMenuInitData(); + + params->iPad = m_iPad; + + params->iSaveGameInfoIndex = m_saveDetails[saveIndex].saveId; + + params->levelGen = nullptr; + + params->saveDetails = &m_saveDetails[saveIndex]; + + ui.NavigateToScene(m_iPad, eUIScene_LoadMenu, params); + + } + + } + + break; + + + + case eControl_GamesList: + + { + +#ifdef _WINDOWS64 + + if (static_cast(childId) == ADD_SERVER_BUTTON_INDEX) + + { + + ui.PlayUISFX(eSFX_Press); + + BeginAddServer(); + + break; + + } + +#endif + + m_bIgnoreInput = true; + + m_eAction = eAction_JoinGame; + + ui.PlayUISFX(eSFX_Press); + + + + int nIndex = static_cast(childId); + +#ifdef _WINDOWS64 + + nIndex -= 1; + +#endif + + m_iGameListIndex = nIndex; + + CheckAndJoinGame(nIndex); + + } + + break; + + } + +} + +void UIScene_LoadCreateJoinMenu::CheckAndJoinGame(int gameIndex) + +{ + + if( m_buttonListGames.getItemCount() > 0 && gameIndex < m_currentSessions->size() ) + + { + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + + // 4J-PB - is the player allowed to join games? + + bool noUGC=false; + + bool bContentRestricted=false; + + + + // we're online, since we are joining a game + + ProfileManager.GetChatAndContentRestrictions(m_iPad,true,&noUGC,&bContentRestricted,nullptr); + + + +#ifdef __ORBIS__ + + // 4J Stu - On PS4 we don't restrict playing multiplayer based on chat restriction, so remove this check + + noUGC = false; + + + + bool bPlayStationPlus=true; + + int iPadWithNoPlaystationPlus=0; + + bool isSignedInLive = true; + + int iPadNotSignedInLive = -1; + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + + { + + if( InputManager.IsPadConnected(i) || ProfileManager.IsSignedIn(i) ) + + { + + if (isSignedInLive && !ProfileManager.IsSignedInLive(i)) + + { + + // Record the first non signed in live pad + + iPadNotSignedInLive = i; + + } + + + + isSignedInLive = isSignedInLive && ProfileManager.IsSignedInLive(i); + + if(ProfileManager.HasPlayStationPlus(i)==false) + + { + + bPlayStationPlus=false; + + break; + + } + + } + + } + +#endif + +#ifdef __PSVITA__ + + if( CGameNetworkManager::usingAdhocMode() ) + + { + + bContentRestricted = false; + + noUGC = false; + + } + +#endif + + + + if(noUGC) + + { + + // not allowed to join + +#ifndef __PSVITA__ + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + // Not allowed to play online + + ui.RequestAlertMessage(IDS_ONLINE_GAME, IDS_CHAT_RESTRICTION_UGC, uiIDA, 1, m_iPad,nullptr,this); + +#else + + // Not allowed to play online + + ProfileManager.ShowSystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, 0 ); + +#endif + + + + m_bIgnoreInput=false; + + return; + + } + + else if(bContentRestricted) + + { + + ui.RequestContentRestrictedMessageBox(); + + + + m_bIgnoreInput=false; + + return; + + } + +#ifdef __ORBIS__ + + // If this is an online game but not all players are signed in to Live, stop! + + else if (!isSignedInLive) + + { + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + + + // Check if PSN is unavailable because of age restriction + + int npAvailability = ProfileManager.getNPAvailability(iPadNotSignedInLive); + + if (npAvailability == SCE_NP_ERROR_AGE_RESTRICTION) + + { + + m_bIgnoreInput = false; + + // 4J Stu - This is a bit messy and is due to the library incorrectly returning false for IsSignedInLive if the npAvailability isn't SCE_OK + + ui.RequestErrorMessage(IDS_ONLINE_SERVICE_TITLE, IDS_CONTENT_RESTRICTION, uiIDA, 1, iPadNotSignedInLive); + + } + + else + + { + + ui.RequestErrorMessage( IDS_PRO_NOTONLINE_TITLE, IDS_PRO_NOTONLINE_TEXT, uiIDA,1,iPadNotSignedInLive, &UIScene_LoadCreateJoinMenu::MustSignInReturnedPSN, this); + + } + + return; + + } + + else if(bPlayStationPlus==false) + + { + + + + if(ProfileManager.RequestingPlaystationPlus(iPadWithNoPlaystationPlus)) + + { + + // MGH - added this so we don't try and upsell when we don't know if the player has PS Plus yet (if it can't connect to the PS Plus server). + + UINT uiIDA[1]; + + uiIDA[0]=IDS_OK; + + ui.RequestAlertMessage(IDS_ERROR_NETWORK_TITLE, IDS_ERROR_NETWORK, uiIDA, 1, ProfileManager.GetPrimaryPad(), nullptr, nullptr); + + return; + + } + + + + // PS Plus upsell + + // 4J-PB - we're not allowed to show the text Playstation Plus - have to call the upsell all the time! + + // upsell psplus + + int32_t iResult=sceNpCommerceDialogInitialize(); + + + + SceNpCommerceDialogParam param; + + sceNpCommerceDialogParamInitialize(¶m); + + param.mode=SCE_NP_COMMERCE_DIALOG_MODE_PLUS; + + param.features = SCE_NP_PLUS_FEATURE_REALTIME_MULTIPLAY; + + param.userId = ProfileManager.getUserID(iPadWithNoPlaystationPlus); + + + + iResult=sceNpCommerceDialogOpen(¶m); + + + + // UINT uiIDA[2]; + + // uiIDA[0]=IDS_CONFIRM_OK; + + // uiIDA[1]=IDS_PLAYSTATIONPLUS_SIGNUP; + + // ui.RequestMessageBox( IDS_FAILED_TO_CREATE_GAME_TITLE, IDS_NO_PLAYSTATIONPLUS, uiIDA,2,ProfileManager.GetPrimaryPad(),&UIScene_LoadCreateJoinMenu::PSPlusReturned,this, app.GetStringTable(),nullptr,0,false); + + + + m_bIgnoreInput=false; + + return; + + } + + + +#endif + +#endif + + + + m_initData->iPad = 0;; + + m_initData->selectedSession = m_currentSessions->at( gameIndex ); + +#ifdef _WINDOWS64 + + { + + + + int serverDbCount = 0; + + FILE* dbFile = fopen("servers.db", "rb"); + + if (dbFile) + + { + + char magic[4] = {}; + + if (fread(magic, 1, 4, dbFile) == 4 && memcmp(magic, "MCSV", 4) == 0) + + { + + uint32_t version = 0, count = 0; + + fread(&version, sizeof(uint32_t), 1, dbFile); + + fread(&count, sizeof(uint32_t), 1, dbFile); + + if (version == 1) + + serverDbCount = static_cast(count); + + } + + fclose(dbFile); + + } + + int lanCount = static_cast(m_currentSessions->size()) - serverDbCount; + + if (gameIndex >= lanCount && lanCount >= 0) + + m_initData->serverIndex = gameIndex - lanCount; + + else + + m_initData->serverIndex = -1; + + } + +#endif + + + + if(m_initData->selectedSession->data.texturePackParentId!=0) + + { + + int texturePacksCount = Minecraft::GetInstance()->skins->getTexturePackCount(); + + bool bHasTexturePackInstalled=false; + + + + for(int i=0;iskins->getTexturePackByIndex(i); + + if(tp->getDLCParentPackId()==m_initData->selectedSession->data.texturePackParentId) + + { + + bHasTexturePackInstalled=true; + + break; + + } + + } + + + + if(bHasTexturePackInstalled==false) + + { + + + +#ifdef _XBOX + + ULONGLONG ullOfferID_Full; + + app.GetDLCFullOfferIDForPackID(m_initData->selectedSession->data.texturePackParentId,&ullOfferID_Full); + + + + TelemetryManager->RecordUpsellPresented(m_iPad, eSet_UpsellID_Texture_DLC, ullOfferID_Full & 0xFFFFFFFF); + +#endif + + UINT uiIDA[2]; + + + + uiIDA[0]=IDS_TEXTUREPACK_FULLVERSION; + + //uiIDA[1]=IDS_TEXTURE_PACK_TRIALVERSION; + + uiIDA[1]=IDS_CONFIRM_CANCEL; + + + + ui.RequestAlertMessage(IDS_DLC_TEXTUREPACK_NOT_PRESENT_TITLE, IDS_DLC_TEXTUREPACK_NOT_PRESENT, uiIDA, 2, m_iPad,&UIScene_LoadCreateJoinMenu::TexturePackDialogReturned,this); + + + + return; + + } + + + +#ifdef __PSVITA__ + + if(CGameNetworkManager::usingAdhocMode() && !SQRNetworkManager_AdHoc_Vita::GetAdhocStatus()) + + { + + // not connected to adhoc anymore, must have connected back to PSN to buy texture pack so sign in again + + SQRNetworkManager_AdHoc_Vita::AttemptAdhocSignIn(&UIScene_LoadCreateJoinMenu::SignInAdhocReturned, this); + + return; + + } + +#endif + + } + + m_controlJoinTimer.setVisible( false ); + + + +#ifdef _XBOX + + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_AUTO); + +#endif + + + + m_bIgnoreInput=true; + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_JoinMenu,m_initData); + + } + +} + + + +void UIScene_LoadCreateJoinMenu::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 + + const wstring preferredSaveTitle = GetPreferredLevelGenSaveTitle(levelGen); + StorageManager.SetSaveTitle(preferredSaveTitle.empty() ? levelGen->getDefaultSaveName().c_str() : preferredSaveTitle.c_str()); + + + + bool isClientSide = false; + + bool isPrivate = false; + + // TODO int maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + + int maxPlayers = 8; + + + + if( app.GetTutorialMode() ) + + { + + isClientSide = false; + + maxPlayers = 4; + + } + + + + g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0); + + + + NetworkGameInitData *param = new NetworkGameInitData(); + + param->seed = 0; + + param->saveData = nullptr; + + param->settings = app.GetGameHostOption( eGameHostOption_Tutorial ); + + param->levelGen = levelGen; + param->levelName = preferredSaveTitle; + + + + if(levelGen->requiresTexturePack()) + + { + + param->texturePackId = levelGen->getRequiredTexturePackId(); + + + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + pMinecraft->skins->selectTexturePackById(param->texturePackId); + + //pMinecraft->skins->updateUI(); + + } + + + +#ifndef _XBOX + + g_NetworkManager.FakeLocalPlayerJoined(); + +#endif + + + + LoadingInputParams *loadingParams = new LoadingInputParams(); + + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + + loadingParams->lpParam = static_cast(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; + + + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + +} + + + +void UIScene_LoadCreateJoinMenu::UpdateGamesListCallback(LPVOID pParam) + +{ + + if(pParam != nullptr) + + { + + UIScene_LoadCreateJoinMenu *pScene = static_cast(pParam); + + pScene->UpdateGamesList(); + + } + +} + + + +void UIScene_LoadCreateJoinMenu::UpdateGamesList() + +{ + + // If we're ignoring input scene isn't active so do nothing + + if (m_bIgnoreInput) return; + + + + // If a texture pack is loading, or will be loading, then ignore this ( we are going to be destroyed anyway) + + if( Minecraft::GetInstance()->skins->getSelected()->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) ) return; + + + + // if we're retrieving save info, don't show the list yet as we will be ignoring press events + + if(!m_bSavesDisplayed) + + { + + return; + + } + + + + + + FriendSessionInfo *pSelectedSession = nullptr; + + if(DoesGamesListHaveFocus() && m_buttonListGames.getItemCount() > 0) + + { + + unsigned int nIndex = m_buttonListGames.getCurrentSelection(); + +#ifdef _WINDOWS64 + + // Offset past the "Add Server" button + + if (nIndex > 0) + + pSelectedSession = m_currentSessions->at( nIndex - 1 ); + +#else + + pSelectedSession = m_currentSessions->at( nIndex ); + +#endif + + } + + + + SessionID selectedSessionId; + + ZeroMemory(&selectedSessionId,sizeof(SessionID)); + + if( pSelectedSession != nullptr )selectedSessionId = pSelectedSession->sessionId; + + pSelectedSession = nullptr; + + + + m_controlJoinTimer.setVisible( 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; + + int iX=-1; + + + + vector* newSessions = g_NetworkManager.GetSessionList( m_iPad, 1, m_bShowingPartyGamesOnly ); + + + + if (m_currentSessions != NULL && m_currentSessions->size() == newSessions->size()) + + { + + bool same = true; + + for (size_t i = 0; i < newSessions->size(); i++) + + { + + if (memcmp(&(*m_currentSessions)[i]->sessionId, &(*newSessions)[i]->sessionId, sizeof(SessionID)) != 0 || + + wcscmp((*m_currentSessions)[i]->displayLabel ? (*m_currentSessions)[i]->displayLabel : L"", + + (*newSessions)[i]->displayLabel ? (*newSessions)[i]->displayLabel : L"") != 0) + + { + + same = false; + + break; + + } + + } + + if (same) + + { + + for (auto& it : *newSessions) + + delete it; + + delete newSessions; + + return; + + } + + } + + + + if (m_currentSessions) + + { + + for (auto& it : *m_currentSessions) + + delete it; + + delete m_currentSessions; + + } + + m_currentSessions = newSessions; + + + + // Update the xui list displayed + + unsigned int xuiListSize = m_buttonListGames.getItemCount(); + + unsigned int filteredListSize = static_cast(m_currentSessions->size()); + + + + BOOL gamesListHasFocus = DoesGamesListHaveFocus(); + + + + if(filteredListSize > 0) + + { + +#if TO_BE_IMPLEMENTED + + if( !m_pGamesList->IsEnabled() ) + + { + + m_pGamesList->SetEnable(TRUE); + + m_pGamesList->SetCurSel( 0 ); + + } + +#endif + + m_bHasNoGamesLabel = false; + + m_controlJoinTimer.setVisible( false ); + + } + + else + + { + +#if TO_BE_IMPLEMENTED + + m_pGamesList->SetEnable(FALSE); + +#endif + + m_controlJoinTimer.setVisible( false ); + + if(m_bHasNoGamesLabel) m_labelNoGames.setVisible( true ); + + + +#if TO_BE_IMPLEMENTED + + if( gamesListHasFocus ) m_pGamesList->InitFocus(m_iPad); + +#endif + + } + + + + // clear out the games list and re-fill + + m_buttonListGames.clearList(); + + + +#ifdef _WINDOWS64 + + // Always add the "Add Server" button as the first entry in the games list + + m_buttonListGames.addItem(wstring(L"Add Server")); + TrySetButtonListIcon( + m_buttonListGames, + m_buttonListGames.getItemCount() - 1, + L"lceheadwer.png", + L"lceheadwer"); + +#endif + + + + if( filteredListSize > 0 ) + + { + + // Reset the focus to the selected session if it still exists + + unsigned int sessionIndex = 0; + + m_buttonListGames.setCurrentSelection(0); + + + + for( FriendSessionInfo *sessionInfo : *m_currentSessions ) + + { + + wchar_t textureName[64] = L"\0"; + + + + // 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=nullptr; + + + + if(tp==nullptr) + + { + + DWORD dwBytes=0; + + PBYTE pbData=nullptr; + + 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) + + { + + swprintf(textureName,64,L"%ls",sessionInfo->displayLabel); + + registerSubstitutionTexture(textureName,pbImageData,dwImageBytes); + + } + + } + + else + + { + + pbImageData = tp->getPackIcon(dwImageBytes); + + if(dwImageBytes > 0 && pbImageData) + + { + + swprintf(textureName,64,L"%ls",sessionInfo->displayLabel); + + registerSubstitutionTexture(textureName,pbImageData,dwImageBytes); + + } + + } + + } + + else + + { + + // default texture pack + + Minecraft *pMinecraft = Minecraft::GetInstance(); + + TexturePack *tp = pMinecraft->skins->getTexturePackByIndex(0); + + + + DWORD dwImageBytes; + + PBYTE pbImageData = tp->getPackIcon(dwImageBytes); + + + + if(dwImageBytes > 0 && pbImageData) + + { + + swprintf(textureName,64,L"%ls",sessionInfo->displayLabel); + + registerSubstitutionTexture(textureName,pbImageData,dwImageBytes); + + } + + } + + + + m_buttonListGames.addItem( sessionInfo->displayLabel, textureName ); + + + + if(memcmp( &selectedSessionId, &sessionInfo->sessionId, sizeof(SessionID) ) == 0) + + { + +#ifdef _WINDOWS64 + + // Offset past the "Add Server" button + + m_buttonListGames.setCurrentSelection(sessionIndex + 1); + +#else + + m_buttonListGames.setCurrentSelection(sessionIndex); + +#endif + + break; + + } + + ++sessionIndex; + + } + + } + + + + updateTooltips(); + + m_bPendingJoinTabAvailabilityRefresh = true; + +} + + + +void UIScene_LoadCreateJoinMenu::HandleDLCMountingComplete() + +{ + + Initialise(); + +} + + + +bool UIScene_LoadCreateJoinMenu::DoesSavesListHaveFocus() + +{ + + return (m_activeTab == eTab_Load) && m_buttonListSaves.hasFocus(); + +} + +bool UIScene_LoadCreateJoinMenu::DoesMashUpWorldHaveFocus() + +{ + + if(!m_buttonListNewGames.hasFocus()) + + { + + return false; + + } + + + + const int lGenID = m_iNewGameListIndex - m_iDefaultButtonsC; + + if(lGenID < 0 || lGenID >= static_cast(m_generators.size())) + + { + + return false; + + } + + LevelGenerationOptions *levelGen = m_generators.at(lGenID); + + return levelGen && !levelGen->isTutorial() && levelGen->requiresTexturePack(); + +} + +bool UIScene_LoadCreateJoinMenu::DoesGamesListHaveFocus() + +{ + + return (m_activeTab == eTab_Join) && m_buttonListGames.hasFocus(); + +} + + + +bool UIScene_LoadCreateJoinMenu::DoesNewGamesListHaveFocus() + +{ + + return (m_activeTab == eTab_Create) && m_buttonListNewGames.hasFocus(); + +} + + + +void UIScene_LoadCreateJoinMenu::handleTimerComplete(int id) + +{ + + switch(id) + + { + + case JOIN_LOAD_ONLINE_TIMER_ID: + + { + +#ifdef _XBOX + + XPARTY_USER_LIST partyList; + + + + if((XPartyGetUserList( &partyList ) != XPARTY_E_NOT_IN_PARTY ) && (partyList.dwUserCount>1)) + + { + + m_bInParty=true; + + } + + else + + { + + m_bInParty=false; + + } + +#endif + + + + 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_buttonListGames.clearList(); + + m_controlJoinTimer.setVisible( false ); + + m_bHasNoGamesLabel = false; + + } + + + + m_bMultiplayerAllowed = bMultiplayerAllowed; + + } + + } + + break; + + // 4J-PB - Only Xbox will not have trial DLC patched into the game + +#ifdef _XBOX + + case CHECKFORAVAILABLETEXTUREPACKS_TIMER_ID: + + { + + + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + + for(int i=0;ichImageURL); + + + + if(hasRegisteredSubstitutionTexture(textureName)==false) + + { + + PBYTE pbImageData; + + int iImageDataBytes=0; + + SonyHttp::getDataFromURL(pDLCInfo->chImageURL,(void **)&pbImageData,&iImageDataBytes); + + + + if(iImageDataBytes!=0) + + { + + // set the image + + registerSubstitutionTexture(textureName,pbImageData,iImageDataBytes,true); + + m_iConfigA[i]=-1; + + } + + + + } + + } + + } + + } + + + + bool bAllDone=true; + + for(int i=0;igetName().c_str()); + + + + int64_t fileSize = saveFile->length(); + + FileInputStream fis(*saveFile); + + byteArray ba(static_cast(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 ); + + param->savePlatform = savePlatform; + + + +#ifndef _XBOX + + g_NetworkManager.FakeLocalPlayerJoined(); + +#endif + + + + LoadingInputParams *loadingParams = new LoadingInputParams(); + + loadingParams->func = &CGameNetworkManager::RunNetworkGameThreadProc; + + loadingParams->lpParam = static_cast(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; + + + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + +} + + + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + +void UIScene_LoadCreateJoinMenu::LoadSaveFromCloud() + +{ + + + + wchar_t wFileName[128]; + + mbstowcs(wFileName, app.getRemoteStorage()->getLocalFilename(), strlen(app.getRemoteStorage()->getLocalFilename())+1); // plus null + + File cloudFile(wFileName); + + + + + + StorageManager.ResetSaveData(); + + + + // Make our next save default to the name of the level + + wchar_t wSaveName[128]; + + mbstowcs(wSaveName, app.getRemoteStorage()->getSaveNameUTF8(), strlen(app.getRemoteStorage()->getSaveNameUTF8())+1); // plus null + + StorageManager.SetSaveTitle(wSaveName); + + + + int64_t fileSize = cloudFile.length(); + + FileInputStream fis(cloudFile); + + 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_All, app.getRemoteStorage()->getSaveHostOptions() ); + + + + g_NetworkManager.HostGame(0,isClientSide,isPrivate,maxPlayers,0); + + + + LoadSaveDataThreadParam *saveData = new LoadSaveDataThreadParam(ba.data, ba.length, cloudFile.getName()); + + + + NetworkGameInitData *param = new NetworkGameInitData(); + + param->seed = app.getRemoteStorage()->getSaveSeed(); + + param->saveData = saveData; + + param->settings = app.GetGameHostOption( eGameHostOption_All ); + + param->savePlatform = app.getRemoteStorage()->getSavePlatform(); + + param->texturePackId = app.getRemoteStorage()->getSaveTexturePack(); + + + +#ifndef _XBOX + + g_NetworkManager.FakeLocalPlayerJoined(); + +#endif + + + + 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; + + + + ui.NavigateToScene(ProfileManager.GetPrimaryPad(),eUIScene_FullscreenProgress, loadingParams); + +} + + + +#endif //SONY_REMOTE_STORAGE_DOWNLOAD + + + +#ifdef _WINDOWS64 + +static bool Win64_DeleteSaveDirectory(const wchar_t* wPath) + +{ + + wchar_t wSearch[MAX_PATH]; + + swprintf_s(wSearch, MAX_PATH, L"%s\\*", wPath); + + WIN32_FIND_DATAW fd; + + HANDLE hFind = FindFirstFileW(wSearch, &fd); + + if (hFind != INVALID_HANDLE_VALUE) + + { + + do + + { + + if (wcscmp(fd.cFileName, L".") == 0 || wcscmp(fd.cFileName, L"..") == 0) continue; + + wchar_t wChild[MAX_PATH]; + + swprintf_s(wChild, MAX_PATH, L"%s\\%s", wPath, fd.cFileName); + + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + + Win64_DeleteSaveDirectory(wChild); + + else + + DeleteFileW(wChild); + + } while (FindNextFileW(hFind, &fd)); + + FindClose(hFind); + + } + + return RemoveDirectoryW(wPath) != 0; + +} + +#endif // _WINDOWS64 + + + +int UIScene_LoadCreateJoinMenu::DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = static_cast(pParam); + + // results switched for this dialog + + + + // Check that we have a valid save selected (can get a bad index if the save list has been refreshed) + + bool validSelection= pClass->m_iSaveListIndex >= 0 && pClass->m_iSaveListIndex < pClass->m_buttonListSaves.getItemCount(); + + + + if(result==C4JStorage::EMessage_ResultDecline && validSelection) + + { + + if(app.DebugSettingsOn() && app.GetLoadSavesFromFolderEnabled()) + + { + + pClass->m_bIgnoreInput=false; + + } + + else + + { + +#ifdef _WINDOWS64 + + { + + // Use m_saveDetails (sorted display order) so the correct folder is targeted + + int displayIdx = pClass->m_iSaveListIndex; + + bool bSuccess = false; + + if (pClass->m_saveDetails && displayIdx >= 0 && pClass->m_saveDetails[displayIdx].UTF8SaveFilename[0]) + + { + + wchar_t wFilename[MAX_SAVEFILENAME_LENGTH] = {}; + + mbstowcs_s(nullptr, wFilename, MAX_SAVEFILENAME_LENGTH, pClass->m_saveDetails[displayIdx].UTF8SaveFilename, MAX_SAVEFILENAME_LENGTH - 1); + + wchar_t wFolderPath[MAX_PATH] = {}; + + swprintf_s(wFolderPath, MAX_PATH, L"Windows64\\GameHDD\\%s", wFilename); + + bSuccess = Win64_DeleteSaveDirectory(wFolderPath); + + } + + UIScene_LoadCreateJoinMenu::DeleteSaveDataReturned((LPVOID)pClass->GetCallbackUniqueId(), bSuccess); + + } + +#else + + StorageManager.DeleteSaveData(&pClass->m_pSaveDetails->SaveInfoA[pClass->m_iSaveListIndex], UIScene_LoadCreateJoinMenu::DeleteSaveDataReturned, (LPVOID)pClass->GetCallbackUniqueId()); + +#endif + + pClass->m_controlSavesTimer.setVisible( true ); + + } + + } + + else + + { + + pClass->m_bIgnoreInput=false; + + } + + + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::DeleteSaveDataReturned(LPVOID lpParam,bool bRes) + +{ + + ui.EnterCallbackIdCriticalSection(); + + UIScene_LoadCreateJoinMenu* pClass = static_cast(ui.GetSceneFromCallbackId((size_t)lpParam)); + + + + if(pClass) + + { + + if(bRes) + + { + + // wipe the list and repopulate it + + pClass->m_iState=e_SavesRepopulateAfterDelete; + + } + + else pClass->m_bIgnoreInput=false; + + + + pClass->updateTooltips(); + + } + + ui.LeaveCallbackIdCriticalSection(); + + return 0; + +} + + + + + +int UIScene_LoadCreateJoinMenu::RenameSaveDataReturned(LPVOID lpParam,bool bRes) + +{ + + UIScene_LoadCreateJoinMenu* pClass = static_cast(lpParam); + + + + if(bRes) + + { + + pClass->m_iState=e_SavesRepopulate; + + } + + else pClass->m_bIgnoreInput=false; + + + + pClass->updateTooltips(); + + + + return 0; + +} + + + +#ifdef __ORBIS__ + + + + + +void UIScene_LoadCreateJoinMenu::LoadRemoteFileFromDisk(char* remoteFilename) + +{ + + wchar_t wSaveName[128]; + + mbstowcs(wSaveName, remoteFilename, strlen(remoteFilename)+1); // plus null + + + + // processConsoleSave(wSaveName, L"ProcessedSave.bin"); + + + + // File remoteFile(L"ProcessedSave.bin"); + + File remoteFile(wSaveName); + + LoadSaveFromDisk(&remoteFile, SAVE_FILE_PLATFORM_PS3); + +} + +#endif + + + + + +int UIScene_LoadCreateJoinMenu::SaveOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = static_cast(pParam); + + + + // results switched for this dialog + + // EMessage_ResultAccept means cancel + + switch(result) + + { + + case C4JStorage::EMessage_ResultDecline: // rename + + { + + pClass->m_bIgnoreInput=true; + +#ifdef _WINDOWS64 + + { + + wchar_t wSaveName[128]; + + ZeroMemory(wSaveName, 128 * sizeof(wchar_t)); + + mbstowcs_s(nullptr, wSaveName, 128, pClass->m_saveDetails[pClass->m_iSaveListIndex].UTF8SaveName, _TRUNCATE); + + UIKeyboardInitData kbData; + + kbData.title = app.GetString(IDS_RENAME_WORLD_TITLE); + + kbData.defaultText = wSaveName; + + kbData.maxChars = 25; + + kbData.callback = &UIScene_LoadCreateJoinMenu::KeyboardCompleteWorldNameCallback; + + kbData.lpParam = pClass; + + kbData.pcMode = g_KBMInput.IsKBMActive(); + + ui.NavigateToScene(pClass->m_iPad, eUIScene_Keyboard, &kbData); + + } + +#elif defined _DURANGO + + // bring up a keyboard + + InputManager.RequestKeyboard(app.GetString(IDS_RENAME_WORLD_TITLE), (pClass->m_saveDetails[pClass->m_iSaveListIndex]).UTF16SaveName,(DWORD)0,25,&UIScene_LoadCreateJoinMenu::KeyboardCompleteWorldNameCallback,pClass,C_4JInput::EKeyboardMode_Default); + +#else + + // bring up a keyboard + + wchar_t wSaveName[128]; + + //CD - Fix - We must memset the SaveName + + ZeroMemory(wSaveName, 128 * sizeof(wchar_t) ); + + mbstowcs(wSaveName, pClass->m_saveDetails[pClass->m_iSaveListIndex].UTF8SaveName, strlen(pClass->m_saveDetails->UTF8SaveName)+1); // plus null + + LPWSTR ptr = wSaveName; + + InputManager.RequestKeyboard(app.GetString(IDS_RENAME_WORLD_TITLE),wSaveName,(DWORD)0,25,&UIScene_LoadCreateJoinMenu::KeyboardCompleteWorldNameCallback,pClass,C_4JInput::EKeyboardMode_Default); + +#endif + + } + + break; + + + + case C4JStorage::EMessage_ResultThirdOption: // 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; + + ui.RequestAlertMessage(IDS_TOOLTIPS_DELETESAVE, IDS_TEXT_DELETE_SAVE, uiIDA, 2, iPad,&UIScene_LoadCreateJoinMenu::DeleteSaveDialogReturned,pClass); + + } + + break; + + + +#ifdef SONY_REMOTE_STORAGE_UPLOAD + + case C4JStorage::EMessage_ResultFourthOption: // upload to cloud + + { + + UINT uiIDA[2]; + + uiIDA[0]=IDS_CONFIRM_OK; + + uiIDA[1]=IDS_CONFIRM_CANCEL; + + + + ui.RequestAlertMessage(IDS_TOOLTIPS_SAVETRANSFER_UPLOAD, IDS_SAVE_TRANSFER_TEXT, uiIDA, 2, iPad,&UIScene_LoadCreateJoinMenu::SaveTransferDialogReturned,pClass); + + } + + break; + +#endif // SONY_REMOTE_STORAGE_UPLOAD + +#if defined _XBOX_ONE || defined __ORBIS__ + + case C4JStorage::EMessage_ResultFourthOption: // copy save + + { + + UINT uiIDA[2]; + + uiIDA[0]=IDS_CONFIRM_OK; + + uiIDA[1]=IDS_CONFIRM_CANCEL; + + + + ui.RequestAlertMessage(IDS_COPYSAVE, IDS_TEXT_COPY_SAVE, uiIDA, 2, iPad,&UIScene_LoadCreateJoinMenu::CopySaveDialogReturned,pClass); + + } + + break; + +#endif + + + + case C4JStorage::EMessage_Cancelled: + + default: + + { + + // reset the tooltips + + pClass->updateTooltips(); + + pClass->m_bIgnoreInput=false; + + } + + break; + + } + + return 0; + +} + + + + + +#if defined (__PSVITA__) + + + +int UIScene_LoadCreateJoinMenu::SignInAdhocReturned(void *pParam,bool bContinue, int iPad) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)pParam; + + pClass->m_bIgnoreInput = false; + + return 0; + + + +} + + + + + + + +int UIScene_LoadCreateJoinMenu::MustSignInTexturePack(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)pParam; + + + + if(result==C4JStorage::EMessage_ResultAccept) + + { + + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_LoadCreateJoinMenu::MustSignInReturnedTexturePack, pClass); + + } + + else + + { + + pClass->m_bIgnoreInput = false; + + } + + + + return 0; + +} + + + + + +int UIScene_LoadCreateJoinMenu::MustSignInReturnedTexturePack(void *pParam,bool bContinue, int iPad) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)pParam; + + + + int commerceState = app.GetCommerceState(); + + while( commerceState != CConsoleMinecraftApp::eCommerce_State_Offline && + + commerceState != CConsoleMinecraftApp::eCommerce_State_Online && + + commerceState != CConsoleMinecraftApp::eCommerce_State_Error) + + { + + Sleep(10); + + commerceState = app.GetCommerceState(); + + } + + + + if(bContinue==true) + + { + + SONYDLC *pSONYDLCInfo=app.GetSONYDLCInfo(pClass->m_initData->selectedSession->data.texturePackParentId); + + if(pSONYDLCInfo!=nullptr) + + { + + char chName[42]; + + char chKeyName[20]; + + char chSkuID[SCE_NP_COMMERCE2_SKU_ID_LEN]; + + + + memset(chSkuID,0,SCE_NP_COMMERCE2_SKU_ID_LEN); + + // we have to retrieve the skuid from the store info, it can't be hardcoded since Sony may change it. + + // So we assume the first sku for the product is the one we want + + // MGH - keyname in the DLC file is 16 chars long, but there's no space for a nullptr terminating char + + memset(chKeyName, 0, sizeof(chKeyName)); + + strncpy(chKeyName, pSONYDLCInfo->chDLCKeyname, 16); + + + +#ifdef __ORBIS__ + + strcpy(chName, chKeyName); + +#else + + sprintf(chName,"%s-%s",app.GetCommerceCategory(),chKeyName); + +#endif + + app.GetDLCSkuIDFromProductList(chName,chSkuID); + + // 4J-PB - need to check for an empty store + + if(app.CheckForEmptyStore(iPad)==false) + + { + + if(app.DLCAlreadyPurchased(chSkuID)) + + { + + app.DownloadAlreadyPurchased(chSkuID); + + } + + else + + { + + app.Checkout(chSkuID); + + } + + } + + } + + } + + pClass->m_bIgnoreInput = false; + + return 0; + +} + + + +#endif + + + +int UIScene_LoadCreateJoinMenu::TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu *pClass = static_cast(pParam); + + + + // Exit with or without saving + + if(result==C4JStorage::EMessage_ResultAccept) + + { + + // we need to enable background downloading for the DLC + + XBackgroundDownloadSetMode(XBACKGROUND_DOWNLOAD_MODE_ALWAYS_ALLOW); + +#if defined __PSVITA__ || defined __PS3__ || defined __ORBIS__ + + + +#ifdef __PSVITA__ + + if(!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()) && CGameNetworkManager::usingAdhocMode()) + + { + + // get them to sign in to online + + UINT uiIDA[2]; + + uiIDA[0]=IDS_PRO_NOTONLINE_ACCEPT; + + uiIDA[1]=IDS_PRO_NOTONLINE_DECLINE; + + ui.RequestAlertMessage(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 2, ProfileManager.GetPrimaryPad(),&UIScene_LoadCreateJoinMenu::MustSignInTexturePack,pClass); + + return; + + } + +#endif + + + + SONYDLC *pSONYDLCInfo=app.GetSONYDLCInfo(pClass->m_initData->selectedSession->data.texturePackParentId); + + if(pSONYDLCInfo!=nullptr) + + { + + char chName[42]; + + char chKeyName[20]; + + char chSkuID[SCE_NP_COMMERCE2_SKU_ID_LEN]; + + + + memset(chSkuID,0,SCE_NP_COMMERCE2_SKU_ID_LEN); + + // we have to retrieve the skuid from the store info, it can't be hardcoded since Sony may change it. + + // So we assume the first sku for the product is the one we want + + // MGH - keyname in the DLC file is 16 chars long, but there's no space for a nullptr terminating char + + memset(chKeyName, 0, sizeof(chKeyName)); + + strncpy(chKeyName, pSONYDLCInfo->chDLCKeyname, 16); + + + +#ifdef __ORBIS__ + + strcpy(chName, chKeyName); + +#else + + sprintf(chName,"%s-%s",app.GetCommerceCategory(),chKeyName); + +#endif + + app.GetDLCSkuIDFromProductList(chName,chSkuID); + + // 4J-PB - need to check for an empty store + + if(app.CheckForEmptyStore(iPad)==false) + + { + + if(app.DLCAlreadyPurchased(chSkuID)) + + { + + app.DownloadAlreadyPurchased(chSkuID); + + } + + else + + { + + app.Checkout(chSkuID); + + } + + } + + } + +#endif + + + + + +#if defined _XBOX_ONE + + if(ProfileManager.IsSignedIn(iPad)) + + { + + if (ProfileManager.IsSignedInLive(iPad)) + + { + + wstring ProductId; + + app.GetDLCFullOfferIDForPackID(pClass->m_initData->selectedSession->data.texturePackParentId,ProductId); + + + + StorageManager.InstallOffer(1,(WCHAR *)ProductId.c_str(),nullptr,nullptr); + + } + + else + + { + + // 4J-JEV: Fix for XB1: #165863 - XR-074: Compliance: With no active network connection user is unable to convert from Trial to Full texture pack and is not messaged why. + + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + + ui.RequestErrorMessage(IDS_PRO_NOTONLINE_TITLE, IDS_PRO_XBOXLIVE_NOTIFICATION, uiIDA, 1, iPad); + + } + + } + +#endif + + + + } + + pClass->m_bIgnoreInput=false; + + return 0; + +} + + + +#if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ + +int UIScene_LoadCreateJoinMenu::MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)pParam; + + + + if(result==C4JStorage::EMessage_ResultAccept) + + { + +#if defined(__PS3__) + + SQRNetworkManager_PS3::AttemptPSNSignIn(&UIScene_LoadCreateJoinMenu::PSN_SignInReturned, pClass); + +#elif defined __PSVITA__ + + SQRNetworkManager_Vita::AttemptPSNSignIn(&UIScene_LoadCreateJoinMenu::PSN_SignInReturned, pClass); + +#else + + SQRNetworkManager_Orbis::AttemptPSNSignIn(&UIScene_LoadCreateJoinMenu::PSN_SignInReturned, pClass, false, iPad); + +#endif + + } + + else + + { + + pClass->m_bIgnoreInput = false; + + } + + + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::PSN_SignInReturned(void *pParam,bool bContinue, int iPad) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)pParam; + + if(bContinue==true) + + { + + switch(pClass->m_eAction) + + { + + case eAction_ViewInvites: + + // Check if we're signed in to LIVE + + if(ProfileManager.IsSignedInLive(iPad)) + + { + +#if defined(__PS3__) + + int ret = sceNpBasicRecvMessageCustom(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE, SYS_MEMORY_CONTAINER_ID_INVALID); + + app.DebugPrintf("sceNpBasicRecvMessageCustom return %d ( %08x )\n", ret, ret); + +#elif defined __PSVITA__ + + SQRNetworkManager_Vita::RecvInviteGUI(); + +#else + + SQRNetworkManager_Orbis::RecvInviteGUI(); + +#endif + + } + + break; + + case eAction_JoinGame: + + pClass->CheckAndJoinGame(pClass->m_iGameListIndex); + + break; + + } + + } + + else + + { + + pClass->m_bIgnoreInput = false; + + } + + return 0; + +} + +#endif + + + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + + + +void UIScene_LoadCreateJoinMenu::LaunchSaveTransfer() + +{ + + LoadingInputParams *loadingParams = new LoadingInputParams(); + + loadingParams->func = &UIScene_LoadCreateJoinMenu::DownloadSonyCrossSaveThreadProc; + + loadingParams->lpParam = (LPVOID)this; + + + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + + completionData->bShowBackground=TRUE; + + completionData->bShowLogo=TRUE; + + completionData->type = e_ProgressCompletion_NavigateBackToScene; + + completionData->iPad = DEFAULT_XUI_MENU_USER; + + loadingParams->completionData = completionData; + + + + loadingParams->cancelFunc=&UIScene_LoadCreateJoinMenu::CancelSaveTransferCallback; + + loadingParams->m_cancelFuncParam=this; + + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + + + ui.NavigateToScene(m_iPad,eUIScene_FullscreenProgress, loadingParams); + +} + + + + + + + + + +int UIScene_LoadCreateJoinMenu::CreateDummySaveDataCallback(LPVOID lpParam,bool bRes) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParam; + + if(bRes) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_GetSavesInfo; + + } + + else + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + app.DebugPrintf("CreateDummySaveDataCallback failed\n"); + + + + } + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::CrossSaveGetSavesInfoCallback(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, bool bRes) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParam; + + if(bRes) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_GetFileData; + + } + + else + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + app.DebugPrintf("CrossSaveGetSavesInfoCallback failed\n"); + + } + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::LoadCrossSaveDataCallback( void *pParam,bool bIsCorrupt, bool bIsOwner ) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) pParam; + + if(bIsCorrupt == false && bIsOwner) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_CreatingNewSave; + + } + + else + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + app.DebugPrintf("LoadCrossSaveDataCallback failed \n"); + + + + } + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::CrossSaveFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) pParam; + + pClass->m_eSaveTransferState = eSaveTransfer_Idle; + + return 0; + +} + + + + + +int UIScene_LoadCreateJoinMenu::CrossSaveDeleteOnErrorReturned(LPVOID lpParam,bool bRes) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParam; + + pClass->m_eSaveTransferState = eSaveTransfer_ErrorMesssage; + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::RemoteSaveNotFoundCallback(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) pParam; + + pClass->m_eSaveTransferState = eSaveTransfer_Idle; + + return 0; + +} + + + +// MGH - added this global to force the delete of the previous data, for the remote storage saves + +// need to speak to Chris why this is necessary + +bool g_bForceVitaSaveWipe = false; + + + + + +int UIScene_LoadCreateJoinMenu::DownloadSonyCrossSaveThreadProc( LPVOID lpParameter ) + +{ + + m_bSaveTransferRunning = true; + +#ifdef __PS3__ + + StorageManager.SetSaveTransferInProgress(true); + +#endif + + Compression::UseDefaultThreadStorage(); + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParameter; + + pClass->m_saveTransferDownloadCancelled = false; + + m_bSaveTransferRunning = true; + + bool bAbortCalled = false; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + bool bSaveFileCreated = false; + + wchar_t wSaveName[128]; + + + + // get the save file size + + pMinecraft->progressRenderer->progressStagePercentage(0); + + pMinecraft->progressRenderer->progressStart(IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD); + + pMinecraft->progressRenderer->progressStage( IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD ); + + + + ConsoleSaveFile* pSave = nullptr; + + + + pClass->m_eSaveTransferState = eSaveTransfer_GetRemoteSaveInfo; + + + + + + while(pClass->m_eSaveTransferState!=eSaveTransfer_Idle) + + { + + switch(pClass->m_eSaveTransferState) + + { + + case eSaveTransfer_Idle: + + break; + + case eSaveTransfer_GetRemoteSaveInfo: + + app.DebugPrintf("UIScene_LoadCreateJoinMenu getSaveInfo\n"); + + app.getRemoteStorage()->getSaveInfo(); + + pClass->m_eSaveTransferState = eSaveTransfer_GettingRemoteSaveInfo; + + break; + + case eSaveTransfer_GettingRemoteSaveInfo: + + if(pClass->m_saveTransferDownloadCancelled) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + break; + + } + + if(app.getRemoteStorage()->waitingForSaveInfo() == false) + + { + + if(app.getRemoteStorage()->saveIsAvailable()) + + { + + if(app.getRemoteStorage()->saveVersionSupported()) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_CreateDummyFile; + + } + + else + + { + + // must be a newer version of the save in the cloud that we don't support yet + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + ui.RequestAlertMessage(IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD, IDS_SAVE_TRANSFER_WRONG_VERSION, uiIDA, 1, ProfileManager.GetPrimaryPad(),RemoteSaveNotFoundCallback,pClass); + + } + + } + + else + + { + + // no save available, inform the user about the functionality + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + ui.RequestAlertMessage(IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD, IDS_SAVE_TRANSFER_NOT_AVAILABLE_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(),RemoteSaveNotFoundCallback,pClass); + + } + + } + + break; + + case eSaveTransfer_CreateDummyFile: + + { + + StorageManager.ResetSaveData(); + + byte *compData = (byte *)StorageManager.AllocateSaveData( app.getRemoteStorage()->getSaveFilesize() ); + + // Make our next save default to the name of the level + + const char* pNameUTF8 = app.getRemoteStorage()->getSaveNameUTF8(); + + mbstowcs(wSaveName, pNameUTF8, strlen(pNameUTF8)+1); // plus null + + StorageManager.SetSaveTitle(wSaveName); + + PBYTE pbThumbnailData=nullptr; + + DWORD dwThumbnailDataSize=0; + + + + PBYTE pbDataSaveImage=nullptr; + + DWORD dwDataSizeSaveImage=0; + + + + StorageManager.GetDefaultSaveImage(&pbDataSaveImage, &dwDataSizeSaveImage); + + if(app.getRemoteStorage()->getThumbnailData() != nullptr && app.getRemoteStorage()->getThumbnailDataSize() > 0) + { + dwThumbnailDataSize = app.getRemoteStorage()->getThumbnailDataSize(); + pbThumbnailData = new BYTE[dwThumbnailDataSize]; + memcpy(pbThumbnailData, app.getRemoteStorage()->getThumbnailData(), dwThumbnailDataSize); + } + else + { + StorageManager.GetDefaultSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize); + } + + + + BYTE bTextMetadata[88]; + + ZeroMemory(bTextMetadata,88); + + unsigned int hostOptions = app.getRemoteStorage()->getSaveHostOptions(); + +#ifdef __ORBIS__ + + app.SetGameHostOption(hostOptions, eGameHostOption_WorldSize, e_worldSize_Classic); // force the classic world size on, otherwise it's unknown and we can't expand + +#endif + + int iTextMetadataBytes = app.CreateImageTextData(bTextMetadata, app.getRemoteStorage()->getSaveSeed(), true, hostOptions, app.getRemoteStorage()->getSaveTexturePack() ); + + + + // set the icon and save image + + StorageManager.SetSaveImages(pbThumbnailData,dwThumbnailDataSize,pbDataSaveImage,dwDataSizeSaveImage,bTextMetadata,iTextMetadataBytes); + + + + app.getRemoteStorage()->waitForStorageManagerIdle(); + + C4JStorage::ESaveGameState saveState = StorageManager.SaveSaveData( &UIScene_LoadCreateJoinMenu::CreateDummySaveDataCallback, lpParameter ); + + if(saveState == C4JStorage::ESaveGame_Save) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_CreatingDummyFile; + + } + + else + + { + + app.DebugPrintf("Failed to create dummy save file\n"); + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + } + + } + + break; + + case eSaveTransfer_CreatingDummyFile: + + break; + + case eSaveTransfer_GetSavesInfo: + + { + + // we can't cancel here, we need the saves info so we can delete the file + + if(pClass->m_saveTransferDownloadCancelled) + + { + + WCHAR wcTemp[256]; + + swprintf(wcTemp,256, app.GetString(IDS_CANCEL)); // MGH - should change this string to "cancelling download" + + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + } + + + + app.getRemoteStorage()->waitForStorageManagerIdle(); + + app.DebugPrintf("CALL GetSavesInfo B\n"); + + C4JStorage::ESaveGameState eSGIStatus= StorageManager.GetSavesInfo(pClass->m_iPad,&UIScene_LoadCreateJoinMenu::CrossSaveGetSavesInfoCallback,pClass,"save"); + + pClass->m_eSaveTransferState = eSaveTransfer_GettingSavesInfo; + + } + + break; + + case eSaveTransfer_GettingSavesInfo: + + if(pClass->m_saveTransferDownloadCancelled) + + { + + WCHAR wcTemp[256]; + + swprintf(wcTemp,256, app.GetString(IDS_CANCEL)); // MGH - should change this string to "cancelling download" + + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + } + + break; + + + + case eSaveTransfer_GetFileData: + + { + + bSaveFileCreated = true; + + StorageManager.GetSaveUniqueFileDir(pClass->m_downloadedUniqueFilename); + + + + if(pClass->m_saveTransferDownloadCancelled) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + break; + + } + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + + int idx = pClass->m_iSaveListIndex; + + app.getRemoteStorage()->waitForStorageManagerIdle(); + + bool bGettingOK = app.getRemoteStorage()->getSaveData(pClass->m_downloadedUniqueFilename, SaveTransferReturned, pClass); + + if(bGettingOK) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_GettingFileData; + + } + + else + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + app.DebugPrintf("app.getRemoteStorage()->getSaveData failed\n"); + + + + } + + } + + + + case eSaveTransfer_GettingFileData: + + { + + WCHAR wcTemp[256]; + + + + int dataProgress = app.getRemoteStorage()->getDataProgress(); + + pMinecraft->progressRenderer->progressStagePercentage(dataProgress); + + + + //swprintf(wcTemp, 256, L"Downloading data : %d", dataProgress);//app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),0,pClass->m_ulFileSize); + + swprintf(wcTemp,256, app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),dataProgress); + + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + if(pClass->m_saveTransferDownloadCancelled && bAbortCalled == false) + + { + + app.getRemoteStorage()->abort(); + + bAbortCalled = true; + + } + + } + + break; + + case eSaveTransfer_FileDataRetrieved: + + pClass->m_eSaveTransferState = eSaveTransfer_LoadSaveFromDisc; + + break; + + case eSaveTransfer_LoadSaveFromDisc: + + { + + if(pClass->m_saveTransferDownloadCancelled) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + break; + + } + + + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + + int saveInfoIndex = -1; + + for(int i=0;iiSaveC;i++) + + { + + if(strcmp(pSaveDetails->SaveInfoA[i].UTF8SaveFilename, pClass->m_downloadedUniqueFilename) == 0) + + { + + //found it + + saveInfoIndex = i; + + } + + } + + if(saveInfoIndex == -1) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + app.DebugPrintf("CrossSaveGetSavesInfoCallback failed - couldn't find save\n"); + + } + + else + + { + +#ifdef __PS3__ + + // ignore the CRC on PS3 + + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[saveInfoIndex],&LoadCrossSaveDataCallback,pClass, true); + +#else + + C4JStorage::ESaveGameState eLoadStatus=StorageManager.LoadSaveData(&pSaveDetails->SaveInfoA[saveInfoIndex],&LoadCrossSaveDataCallback,pClass); + +#endif + + if(eLoadStatus == C4JStorage::ESaveGame_Load) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_LoadingSaveFromDisc; + + } + + else + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + } + + } + + } + + break; + + case eSaveTransfer_LoadingSaveFromDisc: + + + + break; + + case eSaveTransfer_CreatingNewSave: + + { + + unsigned int fileSize = StorageManager.GetSaveSize(); + + byteArray ba(fileSize); + + StorageManager.GetSaveData(ba.data, &fileSize); + + assert(ba.length == fileSize); + + + + + + StorageManager.ResetSaveData(); + + { + + PBYTE pbThumbnailData=nullptr; + + DWORD dwThumbnailDataSize=0; + + + + PBYTE pbDataSaveImage=nullptr; + + DWORD dwDataSizeSaveImage=0; + + + + StorageManager.GetDefaultSaveImage(&pbDataSaveImage, &dwDataSizeSaveImage); + + if(app.getRemoteStorage()->getThumbnailData() != nullptr && app.getRemoteStorage()->getThumbnailDataSize() > 0) + { + dwThumbnailDataSize = app.getRemoteStorage()->getThumbnailDataSize(); + pbThumbnailData = new BYTE[dwThumbnailDataSize]; + memcpy(pbThumbnailData, app.getRemoteStorage()->getThumbnailData(), dwThumbnailDataSize); + } + else + { + StorageManager.GetDefaultSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize); + } + + + + BYTE bTextMetadata[88]; + + ZeroMemory(bTextMetadata,88); + + unsigned int remoteHostOptions = app.getRemoteStorage()->getSaveHostOptions(); + + app.SetGameHostOption(eGameHostOption_All, remoteHostOptions ); + + int iTextMetadataBytes = app.CreateImageTextData(bTextMetadata, app.getRemoteStorage()->getSaveSeed(), true, remoteHostOptions, app.getRemoteStorage()->getSaveTexturePack() ); + + + + // set the icon and save image + + StorageManager.SetSaveImages(pbThumbnailData,dwThumbnailDataSize,pbDataSaveImage,dwDataSizeSaveImage,bTextMetadata,iTextMetadataBytes); + + } + + + + + +#ifdef SPLIT_SAVES + + ConsoleSaveFileOriginal oldFormatSave( wSaveName, ba.data, ba.length, false, app.getRemoteStorage()->getSavePlatform() ); + + pSave = new ConsoleSaveFileSplit( &oldFormatSave, false, pMinecraft->progressRenderer ); + + + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_SAVING); + + pSave->Flush(false,false); + + pClass->m_eSaveTransferState = eSaveTransfer_Saving; + +#else + + pSave = new ConsoleSaveFileOriginal( wSaveName, ba.data, ba.length, false, app.getRemoteStorage()->getSavePlatform() ); + + pClass->m_eSaveTransferState = eSaveTransfer_Converting; + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_CONVERTING); + +#endif + + delete ba.data; + + } + + break; + + case eSaveTransfer_Converting: + + { + + pSave->ConvertToLocalPlatform(); // check if we need to convert this file from PS3->PS4 + + pClass->m_eSaveTransferState = eSaveTransfer_Saving; + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_SAVING); + + StorageManager.SetSaveTitle(wSaveName); + + StorageManager.SetSaveUniqueFilename(pClass->m_downloadedUniqueFilename); + + + + app.getRemoteStorage()->waitForStorageManagerIdle(); // we need to wait for the save system to be idle here, as Flush doesn't check for it. + + pSave->Flush(false, false); + + } + + break; + + case eSaveTransfer_Saving: + + { + + // On Durango/Orbis, we need to wait for all the asynchronous saving processes to complete before destroying the levels, as that will ultimately delete + + // the directory level storage & therefore the ConsoleSaveSplit instance, which needs to be around until all the sub files have completed saving. + +#if defined(_DURANGO) || defined(__ORBIS__) + + while(StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle ) + + { + + Sleep(10); + + StorageManager.Tick(); + + } + +#endif + + + + delete pSave; + + + + + + pMinecraft->progressRenderer->progressStage(IDS_PROGRESS_SAVING_TO_DISC); + + pClass->m_eSaveTransferState = eSaveTransfer_Succeeded; + + } + + break; + + + + case eSaveTransfer_Succeeded: + + { + + // if we've arrived here, the save has been created successfully + + pClass->m_iState=e_SavesRepopulate; + + pClass->updateTooltips(); + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + app.getRemoteStorage()->waitForStorageManagerIdle(); // wait for everything to complete before we hand control back to the player + + ui.RequestErrorMessage( IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD, IDS_SAVE_TRANSFER_DOWNLOADCOMPLETE, uiIDA,1,ProfileManager.GetPrimaryPad(),CrossSaveFinishedCallback,pClass); + + pClass->m_eSaveTransferState = eSaveTransfer_Finished; + + } + + break; + + + + case eSaveTransfer_Cancelled: // this is no longer used + + { + + assert(0); //pClass->m_eSaveTransferState = eSaveTransfer_Idle; + + } + + break; + + case eSaveTransfer_Error: + + { + + if(bSaveFileCreated) + + { + + if(pClass->m_saveTransferDownloadCancelled) + + { + + WCHAR wcTemp[256]; + + swprintf(wcTemp,256, app.GetString(IDS_CANCEL)); // MGH - should change this string to "cancelling download" + + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + } + + // if the save file has already been created we have to delete it again if there's been an error + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + + int saveInfoIndex = -1; + + for(int i=0;iiSaveC;i++) + + { + + if(strcmp(pSaveDetails->SaveInfoA[i].UTF8SaveFilename, pClass->m_downloadedUniqueFilename) == 0) + + { + + //found it + + saveInfoIndex = i; + + } + + } + + if(saveInfoIndex == -1) + + { + + app.DebugPrintf("eSaveTransfer_Error failed - couldn't find save\n"); + + assert(0); + + pClass->m_eSaveTransferState = eSaveTransfer_ErrorMesssage; + + } + + else + + { + + // delete the save file + + app.getRemoteStorage()->waitForStorageManagerIdle(); + + C4JStorage::ESaveGameState eDeleteStatus = StorageManager.DeleteSaveData(&pSaveDetails->SaveInfoA[saveInfoIndex],UIScene_LoadCreateJoinMenu::CrossSaveDeleteOnErrorReturned,pClass); + + if(eDeleteStatus == C4JStorage::ESaveGame_Delete) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_ErrorDeletingSave; + + } + + else + + { + + app.DebugPrintf("StorageManager.DeleteSaveData failed!!\n"); + + pClass->m_eSaveTransferState = eSaveTransfer_ErrorMesssage; + + } + + } + + } + + else + + { + + pClass->m_eSaveTransferState = eSaveTransfer_ErrorMesssage; + + } + + } + + break; + + + + case eSaveTransfer_ErrorDeletingSave: + + break; + + case eSaveTransfer_ErrorMesssage: + + { + + app.getRemoteStorage()->waitForStorageManagerIdle(); // wait for everything to complete before we hand control back to the player + + if(pClass->m_saveTransferDownloadCancelled) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Idle; + + } + + else + + { + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + UINT errorMessage = IDS_SAVE_TRANSFER_DOWNLOADFAILED; + + if(!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + + { + + errorMessage = IDS_ERROR_NETWORK; // show "A network error has occurred." + +#ifdef __ORBIS__ + + if(!ProfileManager.isSignedInPSN(ProfileManager.GetPrimaryPad())) + + { + + errorMessage = IDS_PRO_NOTONLINE_TEXT; // show "not signed into PSN" + + } + +#endif + +#ifdef __VITA__ + + if(!ProfileManager.IsSignedInPSN(ProfileManager.GetPrimaryPad())) + + { + + errorMessage = IDS_PRO_NOTONLINE_TEXT; // show "not signed into PSN" + + } + +#endif + + + + } + + ui.RequestErrorMessage( IDS_TOOLTIPS_SAVETRANSFER_DOWNLOAD, errorMessage, uiIDA,1,ProfileManager.GetPrimaryPad(),CrossSaveFinishedCallback,pClass); + + pClass->m_eSaveTransferState = eSaveTransfer_Finished; + + } + + if(bSaveFileCreated) // save file has been created, then deleted. + + pClass->m_iState=e_SavesRepopulateAfterDelete; + + else + + pClass->m_iState=e_SavesRepopulate; + + pClass->updateTooltips(); + + } + + break; + + case eSaveTransfer_Finished: + + { + + } + + // waiting to dismiss the dialog + + break; + + } + + Sleep(50); + + } + + m_bSaveTransferRunning = false; + +#ifdef __PS3__ + + StorageManager.SetSaveTransferInProgress(false); + +#endif + + return 0; + + + +} + + + +void UIScene_LoadCreateJoinMenu::SaveTransferReturned(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParam; + + + + if(s == SonyRemoteStorage::e_getDataSucceeded) + + { + + pClass->m_eSaveTransferState = eSaveTransfer_FileDataRetrieved; + + } + + else + + { + + pClass->m_eSaveTransferState = eSaveTransfer_Error; + + app.DebugPrintf("SaveTransferReturned failed with error code : 0x%08x\n", error_code); + + } + + + +} + +ConsoleSaveFile* UIScene_LoadCreateJoinMenu::SonyCrossSaveConvert() + +{ + + return nullptr; + +} + + + +void UIScene_LoadCreateJoinMenu::CancelSaveTransferCallback(LPVOID lpParam) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParam; + + pClass->m_saveTransferDownloadCancelled = true; + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1, -1, -1, -1,-1,-1,-1,-1); // MGH - added - remove the "cancel" tooltip, so the player knows it's underway (really needs a "cancelling" message) + +} + + + +#endif + + + + + + + +#ifdef SONY_REMOTE_STORAGE_UPLOAD + + + +void UIScene_LoadCreateJoinMenu::LaunchSaveUpload() + +{ + + LoadingInputParams *loadingParams = new LoadingInputParams(); + + loadingParams->func = &UIScene_LoadCreateJoinMenu::UploadSonyCrossSaveThreadProc; + + loadingParams->lpParam = (LPVOID)this; + + + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + + completionData->bShowBackground=TRUE; + + completionData->bShowLogo=TRUE; + + completionData->type = e_ProgressCompletion_NavigateBackToScene; + + completionData->iPad = DEFAULT_XUI_MENU_USER; + + loadingParams->completionData = completionData; + + + +// 4J-PB - Waiting for Sony to fix canceling a save upload + + loadingParams->cancelFunc=&UIScene_LoadCreateJoinMenu::CancelSaveUploadCallback; + + loadingParams->m_cancelFuncParam = this; + + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + + + ui.NavigateToScene(m_iPad,eUIScene_FullscreenProgress, loadingParams); + + + +} + + + +int UIScene_LoadCreateJoinMenu::CrossSaveUploadFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) pParam; + + pClass->m_eSaveUploadState = eSaveUpload_Idle; + + + + return 0; + +} + + + + + +int UIScene_LoadCreateJoinMenu::UploadSonyCrossSaveThreadProc( LPVOID lpParameter ) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParameter; + + pClass->m_saveTransferUploadCancelled = false; + + bool bAbortCalled = false; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + + + // get the save file size + + pMinecraft->progressRenderer->progressStagePercentage(0); + + pMinecraft->progressRenderer->progressStart(IDS_TOOLTIPS_SAVETRANSFER_UPLOAD); + + pMinecraft->progressRenderer->progressStage( IDS_TOOLTIPS_SAVETRANSFER_UPLOAD ); + + + + PSAVE_DETAILS pSaveDetails=StorageManager.ReturnSavesInfo(); + + int idx = pClass->m_iSaveListIndex; + + bool bSettingOK = app.getRemoteStorage()->setSaveData(&pSaveDetails->SaveInfoA[idx], SaveUploadReturned, pClass); + + + + if(bSettingOK) + + { + + pClass->m_eSaveUploadState = eSaveUpload_UploadingFileData; + + pMinecraft->progressRenderer->progressStagePercentage(0); + + } + + else + + { + + pClass->m_eSaveUploadState = eSaveUpload_Error; + + } + + + + while(pClass->m_eSaveUploadState!=eSaveUpload_Idle) + + { + + switch(pClass->m_eSaveUploadState) + + { + + case eSaveUpload_Idle: + + break; + + case eSaveUpload_UploadingFileData: + + { + + WCHAR wcTemp[256]; + + int dataProgress = app.getRemoteStorage()->getDataProgress(); + + pMinecraft->progressRenderer->progressStagePercentage(dataProgress); + + + + //swprintf(wcTemp, 256, L"Uploading data : %d", dataProgress);//app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),0,pClass->m_ulFileSize); + + swprintf(wcTemp,256, app.GetString(IDS_SAVETRANSFER_STAGE_PUT_DATA),dataProgress); + + + + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + +// 4J-PB - Waiting for Sony to fix canceling a save upload + + if(pClass->m_saveTransferUploadCancelled && bAbortCalled == false) + + { + + // we only really want to be able to cancel during the download of data, if it's taking a long time + + app.getRemoteStorage()->abort(); + + bAbortCalled = true; + + } + + } + + break; + + case eSaveUpload_FileDataUploaded: + + { + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + ui.RequestErrorMessage( IDS_TOOLTIPS_SAVETRANSFER_UPLOAD, IDS_SAVE_TRANSFER_UPLOADCOMPLETE, uiIDA,1,ProfileManager.GetPrimaryPad(),CrossSaveUploadFinishedCallback,pClass); + + pClass->m_eSaveUploadState = esaveUpload_Finished; + + } + + break; + + case eSaveUpload_Cancelled: // this is no longer used + + assert(0);// pClass->m_eSaveUploadState = eSaveUpload_Idle; + + break; + + case eSaveUpload_Error: + + { + + if(pClass->m_saveTransferUploadCancelled) + + { + + pClass->m_eSaveUploadState = eSaveUpload_Idle; + + } + + else + + { + + UINT uiIDA[1]; + + uiIDA[0]=IDS_CONFIRM_OK; + + ui.RequestErrorMessage( IDS_TOOLTIPS_SAVETRANSFER_UPLOAD, IDS_SAVE_TRANSFER_UPLOADFAILED, uiIDA,1,ProfileManager.GetPrimaryPad(),CrossSaveUploadFinishedCallback,pClass); + + pClass->m_eSaveUploadState = esaveUpload_Finished; + + } + + } + + break; + + case esaveUpload_Finished: + + // waiting for dialog to be dismissed + + break; + + } + + Sleep(50); + + } + + + + return 0; + + + +} + + + +void UIScene_LoadCreateJoinMenu::SaveUploadReturned(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParam; + + + + if(pClass->m_saveTransferUploadCancelled) + + { + + UINT uiIDA[1] = { IDS_CONFIRM_OK }; + + ui.RequestErrorMessage( IDS_CANCEL_UPLOAD_TITLE, IDS_CANCEL_UPLOAD_TEXT, uiIDA, 1, ProfileManager.GetPrimaryPad(), CrossSaveUploadFinishedCallback, pClass ); + + pClass->m_eSaveUploadState=esaveUpload_Finished; + + } + + else + + { + + if(s == SonyRemoteStorage::e_setDataSucceeded) + + pClass->m_eSaveUploadState = eSaveUpload_FileDataUploaded; + + else if ( !pClass->m_saveTransferUploadCancelled ) + + pClass->m_eSaveUploadState = eSaveUpload_Error; + + } + +} + + + +void UIScene_LoadCreateJoinMenu::CancelSaveUploadCallback(LPVOID lpParam) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu *) lpParam; + + pClass->m_saveTransferUploadCancelled = true; + + app.DebugPrintf("m_saveTransferUploadCancelled = true\n"); + + ui.SetTooltips( DEFAULT_XUI_MENU_USER, -1, -1, -1, -1,-1,-1,-1,-1); // MGH - added - remove the "cancel" tooltip, so the player knows it's underway (really needs a "cancelling" message) + + + + pClass->m_bIgnoreInput = true; + +} + + + +int UIScene_LoadCreateJoinMenu::SaveTransferDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)pParam; + + // results switched for this dialog + + if(result==C4JStorage::EMessage_ResultAccept) + + { + + // upload the save + + pClass->LaunchSaveUpload(); + + + + pClass->m_bIgnoreInput=false; + + } + + else + + { + + pClass->m_bIgnoreInput=false; + + } + + return 0; + +} + +#endif // SONY_REMOTE_STORAGE_UPLOAD + + + + + +#if defined _XBOX_ONE + +void UIScene_LoadCreateJoinMenu::LaunchSaveTransfer() + +{ + + SaveTransferStateContainer *stateContainer = new SaveTransferStateContainer(); + + stateContainer->m_iProgress = 0; + + stateContainer->m_bSaveTransferInProgress = false; + + stateContainer->m_bSaveTransferCancelled = false; + + stateContainer->m_iPad = m_iPad; + + stateContainer->m_eSaveTransferState = C4JStorage::eSaveTransfer_Idle; + + stateContainer->m_pClass = this; + + + + LoadingInputParams *loadingParams = new LoadingInputParams(); + + loadingParams->func = &UIScene_LoadCreateJoinMenu::DownloadXbox360SaveThreadProc; + + loadingParams->lpParam = (LPVOID)stateContainer; + + + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + + completionData->bShowBackground=TRUE; + + completionData->bShowLogo=TRUE; + + completionData->type = e_ProgressCompletion_NavigateBackToScene; + + completionData->iPad = DEFAULT_XUI_MENU_USER; + + completionData->bRequiresUserAction=TRUE; + + loadingParams->completionData = completionData; + + + + loadingParams->cancelFunc=&UIScene_LoadCreateJoinMenu::CancelSaveTransferCallback; + + loadingParams->m_cancelFuncParam=stateContainer; + + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + + + ui.NavigateToScene(m_iPad,eUIScene_FullscreenProgress, loadingParams); + +} + + + + + + + +int UIScene_LoadCreateJoinMenu::DownloadXbox360SaveThreadProc( LPVOID lpParameter ) + +{ + + Compression::UseDefaultThreadStorage(); + + + + SaveTransferStateContainer *pStateContainer = (SaveTransferStateContainer *) lpParameter; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + ConsoleSaveFile* pSave = nullptr; + + + + while(StorageManager.SaveTransferClearState()!=C4JStorage::eSaveTransfer_Idle) + + { + + Sleep(5); + + } + + + + pStateContainer->m_bSaveTransferInProgress=true; + + + + UIScene_LoadCreateJoinMenu::s_eSaveTransferFile = eSaveTransferFile_Marker; + + RequestFileSize( pStateContainer, L"completemarker" ); + + + + while((pStateContainer->m_eSaveTransferState!=C4JStorage::eSaveTransfer_Idle) && pStateContainer->m_bSaveTransferInProgress && !pStateContainer->m_bSaveTransferCancelled) + + { + + switch(pStateContainer->m_eSaveTransferState) + + { + + case C4JStorage::eSaveTransfer_Idle: + + break; + + case C4JStorage::eSaveTransfer_FileSizeRetrieved: + + switch(UIScene_LoadCreateJoinMenu::s_eSaveTransferFile) + + { + + case eSaveTransferFile_Marker: + + if(UIScene_LoadCreateJoinMenu::s_ulFileSize == 0) + + { + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_NONE_FOUND); + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + + } + + else + + { + + RequestFileData( pStateContainer, L"completemarker" ); + + } + + break; + + case eSaveTransferFile_Metadata: + + RequestFileData( pStateContainer, L"metadata" ); + + break; + + case eSaveTransferFile_SaveData: + + RequestFileData( pStateContainer, L"savedata" ); + + break; + + }; + + break; + + case C4JStorage::eSaveTransfer_GettingFileData: + + + + break; + + case C4JStorage::eSaveTransfer_FileDataRetrieved: + + switch(UIScene_LoadCreateJoinMenu::s_eSaveTransferFile) + + { + + case eSaveTransferFile_Marker: + + // MGH - the marker file now contains the save file version number + + // if the version is higher than we handle, cancel the download. + + if(UIScene_LoadCreateJoinMenu::s_transferData[0] > SAVE_FILE_VERSION_NUMBER) + + { + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_NONE_FOUND); + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + + } + + else + + { + + UIScene_LoadCreateJoinMenu::s_eSaveTransferFile = eSaveTransferFile_Metadata; + + RequestFileSize( pStateContainer, L"metadata" ); + + } + + break; + + case eSaveTransferFile_Metadata: + + { + + ByteArrayInputStream bais(UIScene_LoadCreateJoinMenu::s_transferData); + + DataInputStream dis(&bais); + + + + wstring saveTitle = dis.readUTF(); + + StorageManager.SetSaveTitle(saveTitle.c_str()); + + + + wstring saveUniqueName = dis.readUTF(); + + + + // 4J Stu - Don't set this any more. We added it so that we could share the ban list data for this save + + // However if the player downloads the same save multiple times, it will overwrite the previous version + + // with that filname, and they could have made changes to it. + + //StorageManager.SetSaveUniqueFilename((wchar_t *)saveUniqueName.c_str()); + + + + int thumbnailSize = dis.readInt(); + + if(thumbnailSize > 0) + + { + + byteArray ba(thumbnailSize); + + dis.readFully(ba); + + + + + + + + // retrieve the seed value from the image metadata, we need to change to host options, then set it back again + + bool bHostOptionsRead = false; + + unsigned int uiHostOptions = 0; + + DWORD dwTexturePack; + + int64_t seedVal; + + + + char szSeed[50]; + + ZeroMemory(szSeed,50); + + app.GetImageTextData(ba.data,ba.length,(unsigned char *)&szSeed,uiHostOptions,bHostOptionsRead,dwTexturePack); + + sscanf_s(szSeed, "%I64d", &seedVal); + + + + app.SetGameHostOption(uiHostOptions, eGameHostOption_WorldSize, e_worldSize_Classic); // force the classic world size on, otherwise it's unknown and we can't expand + + + + + + BYTE bTextMetadata[88]; + + ZeroMemory(bTextMetadata,88); + + + + int iTextMetadataBytes = app.CreateImageTextData(bTextMetadata, seedVal, true, uiHostOptions, dwTexturePack); + + // set the icon and save image + + StorageManager.SetSaveImages(ba.data, ba.length, nullptr, 0, bTextMetadata, iTextMetadataBytes); + + + + delete ba.data; + + } + + + + UIScene_LoadCreateJoinMenu::s_transferData = byteArray(); + + UIScene_LoadCreateJoinMenu::s_eSaveTransferFile = eSaveTransferFile_SaveData; + + RequestFileSize( pStateContainer, L"savedata" ); + + } + + break; + + case eSaveTransferFile_SaveData: + + { + +#ifdef SPLIT_SAVES + + if(!pStateContainer->m_bSaveTransferCancelled) + + { + + ConsoleSaveFileOriginal oldFormatSave( L"Temp name", UIScene_LoadCreateJoinMenu::s_transferData.data, UIScene_LoadCreateJoinMenu::s_transferData.length, false, SAVE_FILE_PLATFORM_X360 ); + + pSave = new ConsoleSaveFileSplit( &oldFormatSave, false, pMinecraft->progressRenderer ); + + + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_SAVING); + + if(!pStateContainer->m_bSaveTransferCancelled) pSave->Flush(false,false); + + } + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Saving; + + + +#else + + pSave = new ConsoleSaveFileOriginal( wSaveName, m_transferData.data, m_transferData.length, false, SAVE_FILE_PLATFORM_X360 ); + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Converting; + +#endif + + delete UIScene_LoadCreateJoinMenu::s_transferData.data; + + UIScene_LoadCreateJoinMenu::s_transferData = byteArray(); + + } + + break; + + }; + + + + pStateContainer->m_iProgress=0; + + break; + + case C4JStorage::eSaveTransfer_Converting: + +#if 0 + + pSave->ConvertToLocalPlatform(); + + + + pMinecraft->progressRenderer->progressStage(IDS_SAVETRANSFER_STAGE_SAVING); + + if(!pStateContainer->m_bSaveTransferCancelled) pSave->Flush(false,false); + + + + pStateContainer->m_iProgress+=1; + + if(pStateContainer->m_iProgress==101) + + { + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Saving; + + pStateContainer->m_iProgress=0; + + break; + + } + + pMinecraft->progressRenderer->progressStagePercentage(pStateContainer->m_iProgress); + +#endif + + break; + + case C4JStorage::eSaveTransfer_Saving: + + // On Durango/Orbis, we need to wait for all the asynchronous saving processes to complete before destroying the levels, as that will ultimately delete + + // the directory level storage & therefore the ConsoleSaveSplit instance, which needs to be around until all the sub files have completed saving. + +#if defined(_DURANGO) || defined(__ORBIS__) + + pMinecraft->progressRenderer->progressStage(IDS_PROGRESS_SAVING_TO_DISC); + + + + while(StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle ) + + { + + Sleep(10); + + + + // 4J Stu - DO NOT tick this here. The main thread should be the only place ticking the StorageManager. You WILL get crashes. + + //StorageManager.Tick(); + + } + +#endif + + + + delete pSave; + + + +#ifdef _XBOX_ONE + + pMinecraft->progressRenderer->progressStage(IDS_SAVE_TRANSFER_DOWNLOAD_AND_CONVERT_COMPLETE); + +#endif + + + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + + + + // wipe the list and repopulate it + + if(!pStateContainer->m_bSaveTransferCancelled) pStateContainer->m_pClass->m_iState=e_SavesRepopulateAfterTransferDownload; + + + + //pClass->m_iProgress+=1; + + //if(pClass->m_iProgress==101) + + //{ + + // pClass->m_iProgress=0; + + // pClass->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + + // pMinecraft->progressRenderer->progressStage( IDS_SAVE_TRANSFER_DOWNLOAD_AND_CONVERT_COMPLETE ); + + + + // break; + + //} + + //pMinecraft->progressRenderer->progressStagePercentage(pClass->m_iProgress); + + + + break; + + } + + Sleep(50); + + } + + + + if(pStateContainer->m_bSaveTransferCancelled) + + { + + WCHAR wcTemp[256]; + + + + pStateContainer->m_bSaveTransferCancelled=false; + + swprintf(wcTemp,app.GetString(IDS_SAVE_TRANSFER_DOWNLOAD_CANCELLED)); + + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + + + } + + + + pStateContainer->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + + pStateContainer->m_bSaveTransferInProgress=false; + + + + delete pStateContainer; + + + + return 0; + +} + + + +void UIScene_LoadCreateJoinMenu::RequestFileSize( SaveTransferStateContainer *pClass, wchar_t *filename ) + +{ + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + + + // get the save file size + + pMinecraft->progressRenderer->progressStart(IDS_SAVETRANSFER_TITLE_GET); + + pMinecraft->progressRenderer->progressStage( IDS_SAVETRANSFER_STAGE_GET_DETAILS ); + + + +#ifdef _DEBUG_MENUS_ENABLED + + if(app.GetLoadSavesFromFolderEnabled()) + + { + + ZeroMemory(&m_debugTransferDetails, sizeof(C4JStorage::SAVETRANSFER_FILE_DETAILS) ); + + + + File targetFile( wstring(L"FakeTMSPP\\").append(filename) ); + + if(targetFile.exists()) m_debugTransferDetails.ulFileLen = targetFile.length(); + + + + SaveTransferReturned(pClass,&m_debugTransferDetails); + + } + + else + +#endif + + { + + do + + { + + pMinecraft->progressRenderer->progressStart(IDS_SAVETRANSFER_TITLE_GET); + + pMinecraft->progressRenderer->progressStage( IDS_SAVETRANSFER_STAGE_GET_DETAILS ); + + Sleep(1); + + pClass->m_eSaveTransferState=StorageManager.SaveTransferGetDetails(pClass->m_iPad,C4JStorage::eGlobalStorage_TitleUser,filename,&UIScene_LoadCreateJoinMenu::SaveTransferReturned,pClass); + + } + + while(pClass->m_eSaveTransferState == C4JStorage::eSaveTransfer_Busy && !pClass->m_bSaveTransferCancelled ); + + } + +} + + + +void UIScene_LoadCreateJoinMenu::RequestFileData( SaveTransferStateContainer *pClass, wchar_t *filename ) + +{ + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + WCHAR wcTemp[256]; + + + + pMinecraft->progressRenderer->progressStagePercentage(0); + + + + swprintf(wcTemp,app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),0,UIScene_LoadCreateJoinMenu::s_ulFileSize); + + m_wstrStageText=wcTemp; + + + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + + +#ifdef _DEBUG_MENUS_ENABLED + + if(app.GetLoadSavesFromFolderEnabled()) + + { + + File targetFile( wstring(L"FakeTMSPP\\").append(filename) ); + + if(targetFile.exists()) + + { + + HANDLE hSaveFile = CreateFile( targetFile.getPath().c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr); + + + + m_debugTransferDetails.pbData = new BYTE[m_debugTransferDetails.ulFileLen]; + + + + DWORD numberOfBytesRead = 0; + + ReadFile( hSaveFile,m_debugTransferDetails.pbData,m_debugTransferDetails.ulFileLen,&numberOfBytesRead,nullptr); + + assert(numberOfBytesRead == m_debugTransferDetails.ulFileLen); + + + + CloseHandle(hSaveFile); + + + + SaveTransferReturned(pClass,&m_debugTransferDetails); + + } + + } + + else + +#endif + + { + + do + + { + + pMinecraft->progressRenderer->progressStart(IDS_SAVETRANSFER_TITLE_GET); + + pMinecraft->progressRenderer->progressStage( -1 ); + + Sleep(1); + + pClass->m_eSaveTransferState=StorageManager.SaveTransferGetData(pClass->m_iPad,C4JStorage::eGlobalStorage_TitleUser,filename,&UIScene_LoadCreateJoinMenu::SaveTransferReturned,&UIScene_LoadCreateJoinMenu::SaveTransferUpdateProgress,pClass,pClass); + + } + + while(pClass->m_eSaveTransferState == C4JStorage::eSaveTransfer_Busy && !pClass->m_bSaveTransferCancelled ); + + } + +} + + + +int UIScene_LoadCreateJoinMenu::SaveTransferReturned(LPVOID lpParam,C4JStorage::SAVETRANSFER_FILE_DETAILS *pSaveTransferDetails) + +{ + + SaveTransferStateContainer* pClass = (SaveTransferStateContainer *) lpParam; + + app.DebugPrintf("Save Transfer - size is %d\n",pSaveTransferDetails->ulFileLen); + + + + // if the file data is null, then assume this is the file size retrieval + + if(pSaveTransferDetails->pbData==nullptr) + + { + + pClass->m_eSaveTransferState=C4JStorage::eSaveTransfer_FileSizeRetrieved; + + UIScene_LoadCreateJoinMenu::s_ulFileSize=pSaveTransferDetails->ulFileLen; + + } + + else + + { + + delete UIScene_LoadCreateJoinMenu::s_transferData.data; + + UIScene_LoadCreateJoinMenu::s_transferData = byteArray(pSaveTransferDetails->pbData, UIScene_LoadCreateJoinMenu::s_ulFileSize); + + pClass->m_eSaveTransferState=C4JStorage::eSaveTransfer_FileDataRetrieved; + + } + + + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::SaveTransferUpdateProgress(LPVOID lpParam,unsigned long ulBytesReceived) + +{ + + WCHAR wcTemp[256]; + + + + SaveTransferStateContainer* pClass = (SaveTransferStateContainer *) lpParam; + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + + + if(pClass->m_bSaveTransferCancelled) // was cancelled + + { + + pMinecraft->progressRenderer->progressStage(IDS_SAVE_TRANSFER_DOWNLOAD_CANCELLING); + + swprintf(wcTemp,app.GetString(IDS_SAVE_TRANSFER_DOWNLOAD_CANCELLING)); + + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + } + + else + + { + + unsigned int uiProgress=(unsigned int)(((float)ulBytesReceived/float(UIScene_LoadCreateJoinMenu::s_ulFileSize))*100.0f); + + + + pMinecraft->progressRenderer->progressStagePercentage(uiProgress); + + swprintf(wcTemp,app.GetString(IDS_SAVETRANSFER_STAGE_GET_DATA),((float)(ulBytesReceived))/1024000.0f,((float)UIScene_LoadCreateJoinMenu::s_ulFileSize)/1024000.0f); + + m_wstrStageText=wcTemp; + + pMinecraft->progressRenderer->progressStage( m_wstrStageText ); + + } + + + + return 0; + +} + + + +void UIScene_LoadCreateJoinMenu::CancelSaveTransferCallback(LPVOID lpParam) + +{ + + SaveTransferStateContainer* pClass = (SaveTransferStateContainer *) lpParam; + + + + if(!pClass->m_bSaveTransferCancelled) + + { + + StorageManager.CancelSaveTransfer(UIScene_LoadCreateJoinMenu::CancelSaveTransferCompleteCallback,pClass); + + + + pClass->m_bSaveTransferCancelled=true; + + } + + //pClass->m_bSaveTransferInProgress=false; + +} + + + +int UIScene_LoadCreateJoinMenu::CancelSaveTransferCompleteCallback(LPVOID lpParam) + +{ + + SaveTransferStateContainer* pClass = (SaveTransferStateContainer *) lpParam; + + // change the state to idle to get the download thread to terminate + + pClass->m_eSaveTransferState=C4JStorage::eSaveTransfer_Idle; + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::NeedSyncMessageReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu *pClass = (UIScene_LoadCreateJoinMenu *)pParam; + + LoadMenuInitData *params = (LoadMenuInitData *)pParam; + + + + if( result == C4JStorage::EMessage_ResultAccept ) + + { + + // navigate to the settings scene + + ui.NavigateToScene(pClass->m_iPad, eUIScene_LoadMenu, pClass->m_loadMenuInitData); + + } + + else + + { + + delete pClass->m_loadMenuInitData; + + pClass->m_bIgnoreInput = false; + + } + + + + return 0; + +} + + + + + +#endif + + + + + +#ifdef _XBOX_ONE + +void UIScene_LoadCreateJoinMenu::HandleDLCLicenseChange() + +{ + + // may have installed Halloween on this menu + + app.StartInstallDLCProcess(m_iPad); + +} + +#endif + + + +#if defined _XBOX_ONE || defined __ORBIS__ + +int UIScene_LoadCreateJoinMenu::CopySaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)pParam; + + + + if(result==C4JStorage::EMessage_ResultAccept) + + { + + + + LoadingInputParams *loadingParams = new LoadingInputParams(); + + void *uniqueId = (LPVOID)pClass->GetCallbackUniqueId(); + + loadingParams->func = &UIScene_LoadCreateJoinMenu::CopySaveThreadProc; + + loadingParams->lpParam = uniqueId; + + loadingParams->waitForThreadToDelete = true; + + + + UIFullscreenProgressCompletionData *completionData = new UIFullscreenProgressCompletionData(); + + completionData->bShowBackground=TRUE; + + completionData->bShowLogo=TRUE; + + completionData->type = e_ProgressCompletion_NavigateBackToScene; + + completionData->iPad = DEFAULT_XUI_MENU_USER; + + loadingParams->completionData = completionData; + + + + loadingParams->cancelFunc=&UIScene_LoadCreateJoinMenu::CancelCopySaveCallback; + + loadingParams->m_cancelFuncParam=uniqueId; + + loadingParams->cancelText=IDS_TOOLTIPS_CANCEL; + + + + ui.NavigateToScene(iPad,eUIScene_FullscreenProgress, loadingParams); + + } + + else + + { + + pClass->m_bIgnoreInput=false; + + } + + + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::CopySaveThreadProc( LPVOID lpParameter ) + +{ + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + pMinecraft->progressRenderer->progressStart(IDS_PROGRESS_COPYING_SAVE); + + pMinecraft->progressRenderer->progressStage( -1 ); + + + + ui.EnterCallbackIdCriticalSection(); + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParameter); + + if( pClass ) + + { + + pClass->m_bCopying = true; + + pClass->m_bCopyingCancelled = false; + + ui.LeaveCallbackIdCriticalSection(); + + // Copy save data takes two callbacks - one for completion, and one for progress. The progress callback also lets us cancel the operation, if we return false. + + StorageManager.CopySaveData(&pClass->m_pSaveDetails->SaveInfoA[pClass->m_iSaveListIndex],UIScene_LoadCreateJoinMenu::CopySaveDataReturned,UIScene_LoadCreateJoinMenu::CopySaveDataProgress,lpParameter); + + + + bool bContinue = true; + + do + + { + + Sleep(100); + + ui.EnterCallbackIdCriticalSection(); + + pClass = (UIScene_LoadCreateJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParameter); + + if( pClass ) + + { + + bContinue = pClass->m_bCopying; + + } + + else + + { + + bContinue = false; + + } + + ui.LeaveCallbackIdCriticalSection(); + + } while( bContinue ); + + } + + else + + { + + ui.LeaveCallbackIdCriticalSection(); + + } + + + + return 0; + +} + + + +int UIScene_LoadCreateJoinMenu::CopySaveDataReturned(LPVOID lpParam, bool success, C4JStorage::ESaveGameState stat) + +{ + + ui.EnterCallbackIdCriticalSection(); + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParam); + + + + if(pClass) + + { + + if(success) + + { + + pClass->m_bCopying = false; + + // wipe the list and repopulate it + + pClass->m_iState=e_SavesRepopulateAfterDelete; + + ui.LeaveCallbackIdCriticalSection(); + + } + + else + + { + +#ifdef __ORBIS__ + + UINT uiIDA[1]; + + // you cancelled the save on exit after choosing exit and save? You go back to the Exit choices then. + + uiIDA[0]=IDS_OK; + + + + if( stat == C4JStorage::ESaveGame_CopyCompleteFailLocalStorage ) + + { + + ui.LeaveCallbackIdCriticalSection(); + + ui.RequestErrorMessage(IDS_COPYSAVE_FAILED_TITLE, IDS_COPYSAVE_FAILED_LOCAL, uiIDA, 1, ProfileManager.GetPrimaryPad(), CopySaveErrorDialogFinishedCallback, lpParam); + + } + + else if( stat == C4JStorage::ESaveGame_CopyCompleteFailQuota ) + + { + + ui.LeaveCallbackIdCriticalSection(); + + ui.RequestErrorMessage(IDS_COPYSAVE_FAILED_TITLE, IDS_COPYSAVE_FAILED_QUOTA, uiIDA, 1, ProfileManager.GetPrimaryPad(), CopySaveErrorDialogFinishedCallback, lpParam); + + } + + else + + { + + pClass->m_bCopying = false; + + ui.LeaveCallbackIdCriticalSection(); + + } + +#else + + pClass->m_bCopying = false; + + ui.LeaveCallbackIdCriticalSection(); + +#endif + + } + + } + + else + + { + + ui.LeaveCallbackIdCriticalSection(); + + } + + return 0; + +} + + + +bool UIScene_LoadCreateJoinMenu::CopySaveDataProgress(LPVOID lpParam, int percent) + +{ + + bool bContinue = false; + + ui.EnterCallbackIdCriticalSection(); + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParam); + + if( pClass ) + + { + + bContinue = !pClass->m_bCopyingCancelled; + + } + + ui.LeaveCallbackIdCriticalSection(); + + Minecraft *pMinecraft=Minecraft::GetInstance(); + + pMinecraft->progressRenderer->progressStagePercentage(percent); + + + + return bContinue; + +} + + + +void UIScene_LoadCreateJoinMenu::CancelCopySaveCallback(LPVOID lpParam) + +{ + + ui.EnterCallbackIdCriticalSection(); + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)ui.GetSceneFromCallbackId((size_t)lpParam); + + if( pClass ) + + { + + pClass->m_bCopyingCancelled = true; + + } + + ui.LeaveCallbackIdCriticalSection(); + +} + + + +int UIScene_LoadCreateJoinMenu::CopySaveErrorDialogFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result) + +{ + + ui.EnterCallbackIdCriticalSection(); + + UIScene_LoadCreateJoinMenu* pClass = (UIScene_LoadCreateJoinMenu*)ui.GetSceneFromCallbackId((size_t)pParam); + + if( pClass ) + + { + + pClass->m_bCopying = false; + + } + + ui.LeaveCallbackIdCriticalSection(); + + + + return 0; + +} + + + +#endif // _XBOX_ONE + +#ifdef _WINDOWS64 + +// adding servers bellow + + + +void UIScene_LoadCreateJoinMenu::BeginAddServer() + +{ + + m_addServerPhase = eAddServer_IP; + + m_addServerIP.clear(); + + m_addServerPort.clear(); + + + + UIKeyboardInitData kbData; + + kbData.title = L"Server Address"; + + kbData.defaultText = L""; + + kbData.maxChars = 128; + + kbData.callback = &UIScene_LoadCreateJoinMenu::AddServerKeyboardCallback; + + kbData.lpParam = this; + + kbData.pcMode = g_KBMInput.IsKBMActive(); + + ui.NavigateToScene(m_iPad, eUIScene_Keyboard, &kbData); + +} + + + +int UIScene_LoadCreateJoinMenu::AddServerKeyboardCallback(LPVOID lpParam, bool bRes) + +{ + + UIScene_LoadCreateJoinMenu *pClass = static_cast(lpParam); + + + + if (!bRes) + + { + + pClass->m_addServerPhase = eAddServer_Idle; + + pClass->m_bIgnoreInput = false; + + return 0; + + } + + + + uint16_t ui16Text[256]; + + ZeroMemory(ui16Text, sizeof(ui16Text)); + + Win64_GetKeyboardText(ui16Text, 256); + + + + wchar_t wBuf[256] = {}; + + for (int k = 0; k < 255 && ui16Text[k]; k++) + + wBuf[k] = static_cast(ui16Text[k]); + + + + if (wBuf[0] == 0) + + { + + pClass->m_addServerPhase = eAddServer_Idle; + + pClass->m_bIgnoreInput = false; + + return 0; + + } + + + + switch (pClass->m_addServerPhase) + + { + + case eAddServer_IP: + + { + + pClass->m_addServerIP = wBuf; + + pClass->m_addServerPhase = eAddServer_Port; + + + + UIKeyboardInitData kbData; + + kbData.title = L"Server Port"; + + kbData.defaultText = L"25565"; + + kbData.maxChars = 6; + + kbData.callback = &UIScene_LoadCreateJoinMenu::AddServerKeyboardCallback; + + kbData.lpParam = pClass; + + kbData.pcMode = g_KBMInput.IsKBMActive(); + + ui.NavigateToScene(pClass->m_iPad, eUIScene_Keyboard, &kbData); + + break; + + } + + case eAddServer_Port: + + { + + pClass->m_addServerPort = wBuf; + + pClass->m_addServerPhase = eAddServer_Name; + + + + UIKeyboardInitData kbData; + + kbData.title = L"Server Name"; + + kbData.defaultText = L"Minecraft Server"; + + kbData.maxChars = 64; + + kbData.callback = &UIScene_LoadCreateJoinMenu::AddServerKeyboardCallback; + + kbData.lpParam = pClass; + + kbData.pcMode = g_KBMInput.IsKBMActive(); + + ui.NavigateToScene(pClass->m_iPad, eUIScene_Keyboard, &kbData); + + break; + + } + + case eAddServer_Name: + + { + + wstring name = wBuf; + + pClass->AppendServerToFile(pClass->m_addServerIP, pClass->m_addServerPort, name); + + pClass->m_addServerPhase = eAddServer_Idle; + + pClass->m_bIgnoreInput = false; + + + + g_NetworkManager.ForceFriendsSessionRefresh(); + + break; + + } + + default: + + pClass->m_addServerPhase = eAddServer_Idle; + + pClass->m_bIgnoreInput = false; + + break; + + } + + + + return 0; + +} + + + +void UIScene_LoadCreateJoinMenu::AppendServerToFile(const wstring& ip, const wstring& port, const wstring& name) + +{ + + char narrowIP[256] = {}; + + char narrowPort[16] = {}; + + char narrowName[256] = {}; + + wcstombs(narrowIP, ip.c_str(), sizeof(narrowIP) - 1); + + wcstombs(narrowPort, port.c_str(), sizeof(narrowPort) - 1); + + wcstombs(narrowName, name.c_str(), sizeof(narrowName) - 1); + + + + uint16_t portNum = static_cast(atoi(narrowPort)); + + + + struct ServerEntry { std::string ip; uint16_t port; std::string name; }; + + std::vector entries; + + + + FILE* file = fopen("servers.db", "rb"); + + if (file) + + { + + char magic[4] = {}; + + if (fread(magic, 1, 4, file) == 4 && memcmp(magic, "MCSV", 4) == 0) + + { + + uint32_t version = 0, count = 0; + + fread(&version, sizeof(uint32_t), 1, file); + + fread(&count, sizeof(uint32_t), 1, file); + + if (version == 1) + + { + + for (uint32_t s = 0; s < count; s++) + + { + + uint16_t ipLen = 0, p = 0, nameLen = 0; + + if (fread(&ipLen, sizeof(uint16_t), 1, file) != 1) break; + + if (ipLen == 0 || ipLen > 256) break; + + char ipBuf[257] = {}; + + if (fread(ipBuf, 1, ipLen, file) != ipLen) break; + + if (fread(&p, sizeof(uint16_t), 1, file) != 1) break; + + if (fread(&nameLen, sizeof(uint16_t), 1, file) != 1) break; + + if (nameLen > 256) break; + + char nameBuf[257] = {}; + + if (nameLen > 0 && fread(nameBuf, 1, nameLen, file) != nameLen) break; + + entries.push_back({std::string(ipBuf), p, std::string(nameBuf)}); + + } + + } + + } + + fclose(file); + + } + + + + entries.push_back({std::string(narrowIP), portNum, std::string(narrowName)}); + + + + file = fopen("servers.db", "wb"); + + if (file) + + { + + fwrite("MCSV", 1, 4, file); + + uint32_t version = 1; + + uint32_t count = static_cast(entries.size()); + + fwrite(&version, sizeof(uint32_t), 1, file); + + fwrite(&count, sizeof(uint32_t), 1, file); + + + + for (size_t i = 0; i < entries.size(); i++) + + { + + uint16_t ipLen = static_cast(entries[i].ip.length()); + + fwrite(&ipLen, sizeof(uint16_t), 1, file); + + fwrite(entries[i].ip.c_str(), 1, ipLen, file); + + fwrite(&entries[i].port, sizeof(uint16_t), 1, file); + + uint16_t nameLen = static_cast(entries[i].name.length()); + + fwrite(&nameLen, sizeof(uint16_t), 1, file); + + fwrite(entries[i].name.c_str(), 1, nameLen, file); + + } + + fclose(file); + + } + +} + +#endif // _WINDOWS64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Minecraft.Client/Common/UI/UIScene_LoadCreateJoinMenu.h b/Minecraft.Client/Common/UI/UIScene_LoadCreateJoinMenu.h new file mode 100644 index 00000000..f35d00d1 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIScene_LoadCreateJoinMenu.h @@ -0,0 +1,413 @@ +#pragma once + +#include "UIScene.h" + +class LevelGenerationOptions; +class C4JThread; + + +#if defined __PS3__ || defined __ORBIS__ || defined(__PSVITA__) +#define SONY_REMOTE_STORAGE_DOWNLOAD +#endif +#if defined __PS3__ || __PSVITA__ +#define SONY_REMOTE_STORAGE_UPLOAD +#endif + + +class UIScene_LoadCreateJoinMenu : public UIScene +{ +private: + enum EControls + { + eControl_SavesList, + eControl_NewGamesList, + eControl_GamesList, + eControl_TabLoad, + eControl_TabCreate, + eControl_TabJoin, +#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64) + eControl_SpaceIndicator, +#endif + }; + + enum ELoadCreateJoinTab + { + eTab_Load = 0, + eTab_Create, + eTab_Join, + }; + + enum EState + { + e_SavesIdle, + e_SavesRepopulate, + e_SavesRepopulateAfterMashupHide, + e_SavesRepopulateAfterDelete, + e_SavesRepopulateAfterTransferDownload, + }; + + enum eActions + { + eAction_None=0, + eAction_ViewInvites, + eAction_JoinGame, + }; + eActions m_eAction; + + static const int JOIN_LOAD_CREATE_BUTTON_INDEX = 0; + + SaveListDetails *m_saveDetails; + int m_iSaveDetailsCount; + +protected: + UIControl m_controlMainPanel; + UIControl m_controlLoadGame; + UIControl m_controlLoadGamePanel; + UIControl m_controlNewGame; + UIControl m_controlNewGamePanel; + UIControl m_controlJoinGame; + UIControl m_controlJoinGamePanel; + UIControl_SaveList m_buttonListSaves; + UIControl_SaveList m_buttonListNewGames; + UIControl_SaveList m_buttonListGames; + UIControl_Button m_tabLoad, m_tabCreate, m_tabJoin; + UIControl_Label m_labelSavesListTitle, m_labelCreateListTitle, m_labelJoinListTitle, m_labelNoGames; + UIControl m_controlSavesTimer, m_controlNewGameTimer, m_controlJoinTimer; +#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64) + UIControl_SpaceIndicatorBar m_spaceIndicatorSaves; +#endif + + IggyName m_funcSetActiveTab, m_funcSetMatchesAvailable, m_funcShowSaveSizeBar; + bool m_hasMainPanel; + bool m_hasNewGameList; + bool m_hasNoGamesLabel; + bool m_hasTabButtons; + ELoadCreateJoinTab m_activeTab; + IggyValuePath m_loadGamePath; + IggyValuePath m_newGamePath; + IggyValuePath m_joinGamePath; + bool m_hasLoadGamePath; + bool m_hasNewGamePath; + bool m_hasJoinGamePath; + +private: + UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) + UI_MAP_ELEMENT( m_controlMainPanel, "MainPanel") + UI_MAP_ELEMENT( m_controlLoadGame, "LoadGame") + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlLoadGame ) + UI_MAP_ELEMENT( m_controlLoadGamePanel, "LoadGameListRecessPanel") + UI_MAP_ELEMENT( m_buttonListSaves, "LoadGameList") + UI_MAP_ELEMENT( m_controlSavesTimer, "LoadGameTimer") + UI_MAP_ELEMENT( m_tabLoad, "TouchTabLoad") +#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(_WINDOWS64) + UI_MAP_ELEMENT( m_spaceIndicatorSaves, "SaveSizeBar") +#endif + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT( m_controlNewGame, "NewGame") + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlNewGame ) + UI_MAP_ELEMENT( m_controlNewGamePanel, "NewGameListRecessPanel") + UI_MAP_ELEMENT( m_buttonListNewGames, "NewGameList") + UI_MAP_ELEMENT( m_controlNewGameTimer, "NewGameTimer") + UI_MAP_ELEMENT( m_tabCreate, "TouchTabCreate") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT( m_controlJoinGame, "JoinGame") + UI_BEGIN_MAP_CHILD_ELEMENTS( m_controlJoinGame ) + UI_MAP_ELEMENT( m_controlJoinGamePanel, "GamesPanel") + UI_MAP_ELEMENT( m_buttonListGames, "JoinGameGamesList") + UI_MAP_ELEMENT( m_controlJoinTimer, "JoinGamesTimer") + UI_MAP_ELEMENT( m_tabJoin, "TouchTabJoin") + UI_END_MAP_CHILD_ELEMENTS() + + UI_MAP_ELEMENT( m_labelSavesListTitle, "LoadGameTabTitle") + UI_MAP_ELEMENT( m_labelCreateListTitle, "NewGameTabTitle") + UI_MAP_ELEMENT( m_labelJoinListTitle, "JoinGameTabTitle") + UI_MAP_ELEMENT( m_labelNoGames, "NoGames") + UI_MAP_NAME( m_funcSetActiveTab, L"SetActiveTab") + UI_MAP_NAME( m_funcSetMatchesAvailable, L"SetMatchesAvailable") + UI_MAP_NAME( m_funcShowSaveSizeBar, L"ShowSaveSizeBar") + UI_END_MAP_ELEMENTS_AND_NAMES() + + int m_iDefaultButtonsC; + int m_iMashUpButtonsC; + int m_iState; + + vector *m_currentSessions; + vector m_generators; + vector *m_saves; + + bool m_bIgnoreInput; + bool m_bAllLoaded; + bool m_bRetrievingSaveThumbnails; + bool m_bSaveThumbnailReady; + bool m_bShowingPartyGamesOnly; + bool m_bInParty; + JoinMenuInitData *m_initData; + bool m_bMultiplayerAllowed; + int m_iTexturePacksNotInstalled; + int m_iRequestingThumbnailId; + SAVE_DETAILS *m_pSaveDetails; + bool m_bSavesDisplayed; + bool m_bExitScene; + bool m_bCopying; + bool m_bCopyingCancelled; + int m_iSaveInfoC; + int m_iSaveListIndex; + int m_iGameListIndex; + //int *m_iConfigA; // track the texture packs that we don't have installed +#ifndef _XBOX_ONE + bool m_bSaveTransferInProgress; + bool m_bSaveTransferCancelled; +#endif + bool m_bUpdateSaveSize; + bool m_bPendingSaveSizeBarRefresh; + bool m_bPendingJoinTabAvailabilityRefresh; +bool m_bHasNoGamesLabel; +int m_iNewGameListIndex; +#ifdef _WINDOWS64 + int m_lastHoverMouseX; + int m_lastHoverMouseY; + bool m_mouseHoverTracked; + int m_hoverBaseIndexLoad; + int m_hoverBaseIndexCreate; + int m_hoverBaseIndexJoin; +#endif + +public: + UIScene_LoadCreateJoinMenu(int iPad, void *initData, UILayer *parentLayer); + virtual ~UIScene_LoadCreateJoinMenu(); + + virtual void updateTooltips(); + virtual void updateComponents(); + virtual UIControl* GetMainPanel(); + + virtual void handleDestroy(); + virtual void handleLoseFocus(); + virtual void handleGainFocus(bool navBack); + virtual void handleTimerComplete(int id); + // INPUT + virtual void handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled); + virtual void handleFocusChange(F64 controlId, F64 childId); + virtual void handleInitFocus(F64 controlId, F64 childId); + + virtual EUIScene getSceneType() { return eUIScene_LoadCreateJoinMenu; } + + static void UpdateGamesListCallback(LPVOID pParam); +#ifdef _XBOX_ONE + void HandleDLCLicenseChange(); +#endif + virtual void tick(); + +private: + void Initialise(); + void GetSaveInfo(); + void UpdateGamesList(); + void AddDefaultButtons(); + bool DoesSavesListHaveFocus(); + bool DoesMashUpWorldHaveFocus(); + bool DoesGamesListHaveFocus(); + bool DoesNewGamesListHaveFocus(); + int GetMovieTabFromFocus(); + void SyncMovieTab(); + void SetMovieTab(int tab); + void SetActiveTab(ELoadCreateJoinTab tab, bool setFocus); + void ApplyTabVisibility(bool setFocus); + void UpdateJoinTabAvailability(); + void UpdateSaveSizeBarVisibility(); +#ifdef _WINDOWS64 + void UpdateMouseHoverForActiveTab(); + bool ConvertMouseToSceneCoords(float &sceneMouseX, float &sceneMouseY); + void GetAbsoluteControlRect(UIControl *pControl, S32 &x, S32 &y, S32 &w, S32 &h); +#endif + +protected: + // TODO: This should be pure virtual in this class + virtual wstring getMoviePath(); + +public: + static int LoadSaveDataThumbnailReturned(LPVOID lpParam,PBYTE pbThumbnail,DWORD dwThumbnailBytes); + static int LoadSaveCallback(LPVOID lpParam,bool bRes); + static int DeleteSaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int SaveOptionsDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int TexturePackDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DeleteSaveDataReturned(LPVOID lpParam,bool bRes); + static int RenameSaveDataReturned(LPVOID lpParam,bool bRes); + static int KeyboardCompleteWorldNameCallback(LPVOID lpParam,bool bRes); +#ifdef __PSVITA__ + static int MustSignInTexturePack(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int MustSignInReturnedTexturePack(void *pParam,bool bContinue, int iPad); + static int SignInAdhocReturned(void *pParam,bool bContinue, int iPad); +#endif + +protected: + void handlePress(F64 controlId, F64 childId); + void LoadLevelGen(LevelGenerationOptions *levelGen); + void LoadSaveFromDisk(File *saveFile, ESavePlatform savePlatform = SAVE_FILE_PLATFORM_LOCAL); +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + void LoadSaveFromCloud(); +#endif +public: + virtual void HandleDLCMountingComplete(); + +#ifdef __ORBIS__ + void LoadRemoteFileFromDisk(char* remoteFilename); +#endif + +private: + void CheckAndJoinGame(int gameIndex); + +#ifdef _WINDOWS64 + static const int ADD_SERVER_BUTTON_INDEX = 0; + enum eAddServerPhase { eAddServer_Idle, eAddServer_IP, eAddServer_Port, eAddServer_Name }; + eAddServerPhase m_addServerPhase; + wstring m_addServerIP; + wstring m_addServerPort; + void BeginAddServer(); + void AppendServerToFile(const wstring& ip, const wstring& port, const wstring& name); + static int AddServerKeyboardCallback(LPVOID lpParam, bool bRes); +#endif + +#if defined(__PS3__) || defined(__PSVITA__) || defined(__ORBIS__) + static int MustSignInReturnedPSN(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int PSN_SignInReturned(void *pParam,bool bContinue, int iPad); + static void remoteStorageGetSaveCallback(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code); +#endif + +#ifdef __ORBIS__ + //static int PSPlusReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif +#ifdef _XBOX_ONE + typedef struct _SaveTransferStateContainer + { + int m_iProgress; + bool m_bSaveTransferInProgress; + bool m_bSaveTransferCancelled; + int m_iPad; + C4JStorage::eSaveTransferState m_eSaveTransferState; + UIScene_LoadCreateJoinMenu *m_pClass; + } SaveTransferStateContainer; + enum ESaveTransferFiles + { + eSaveTransferFile_Marker, + eSaveTransferFile_Metadata, + eSaveTransferFile_SaveData, + }; + static ESaveTransferFiles s_eSaveTransferFile; + static unsigned long s_ulFileSize; + static byteArray s_transferData; + static wstring m_wstrStageText; + LoadMenuInitData *m_loadMenuInitData; + +#ifdef _DEBUG_MENUS_ENABLED + static C4JStorage::SAVETRANSFER_FILE_DETAILS m_debugTransferDetails; +#endif + + void LaunchSaveTransfer(); + static int DownloadXbox360SaveThreadProc( LPVOID lpParameter ); + static void RequestFileSize( SaveTransferStateContainer *pClass, wchar_t *filename ); + static void RequestFileData( SaveTransferStateContainer *pClass, wchar_t *filename ); + static int SaveTransferReturned(LPVOID lpParam,C4JStorage::SAVETRANSFER_FILE_DETAILS *pSaveTransferDetails); + static int SaveTransferUpdateProgress(LPVOID lpParam,unsigned long ulBytesReceived); + static void CancelSaveTransferCallback(LPVOID lpParam); + static int NeedSyncMessageReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int CancelSaveTransferCompleteCallback(LPVOID lpParam); + +#endif + + + +#ifdef SONY_REMOTE_STORAGE_DOWNLOAD + enum eSaveTransferState + { + eSaveTransfer_Idle, + eSaveTransfer_Busy, + eSaveTransfer_GetRemoteSaveInfo, + eSaveTransfer_GettingRemoteSaveInfo, + eSaveTransfer_CreateDummyFile, + eSaveTransfer_CreatingDummyFile, + eSaveTransfer_GettingFileSize, + eSaveTransfer_FileSizeRetrieved, + eSaveTransfer_GetFileData, + eSaveTransfer_GettingFileData, + eSaveTransfer_FileDataRetrieved, + eSaveTransfer_GetSavesInfo, + eSaveTransfer_GettingSavesInfo, + eSaveTransfer_LoadSaveFromDisc, + eSaveTransfer_LoadingSaveFromDisc, + eSaveTransfer_CreatingNewSave, + eSaveTransfer_Converting, + eSaveTransfer_Saving, + eSaveTransfer_Succeeded, + eSaveTransfer_Cancelled, + eSaveTransfer_Error, + eSaveTransfer_ErrorDeletingSave, + eSaveTransfer_ErrorMesssage, + eSaveTransfer_Finished, + + }; + eSaveTransferState m_eSaveTransferState; + static unsigned long m_ulFileSize; + static wstring m_wstrStageText; + static bool m_bSaveTransferRunning; + int m_iProgress; + char m_downloadedUniqueFilename[64];//SCE_SAVE_DATA_DIRNAME_DATA_MAXSIZE]; + bool m_saveTransferDownloadCancelled; + void LaunchSaveTransfer(); + static int CreateDummySaveDataCallback(LPVOID lpParam,bool bRes); + static int CrossSaveGetSavesInfoCallback(LPVOID lpParam, SAVE_DETAILS *pSaveDetails,bool bRes); + static int LoadCrossSaveDataCallback(void *pParam,bool bIsCorrupt, bool bIsOwner); + static int CrossSaveFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int CrossSaveDeleteOnErrorReturned(LPVOID lpParam,bool bRes); + static int RemoteSaveNotFoundCallback(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int DownloadSonyCrossSaveThreadProc( LPVOID lpParameter ); + static void SaveTransferReturned(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code); + static ConsoleSaveFile* SonyCrossSaveConvert(); + + static void CancelSaveTransferCallback(LPVOID lpParam); +public: + static bool isSaveTransferRunning() { return m_bSaveTransferRunning; } +private: +#endif + +#ifdef SONY_REMOTE_STORAGE_UPLOAD + enum eSaveUploadState + { + eSaveUpload_Idle, + eSaveUpload_UploadingFileData, + eSaveUpload_FileDataUploaded, + eSaveUpload_Cancelled, + eSaveUpload_Error, + esaveUpload_Finished + }; + + eSaveUploadState m_eSaveUploadState; + bool m_saveTransferUploadCancelled; + + void LaunchSaveUpload(); + static int UploadSonyCrossSaveThreadProc( LPVOID lpParameter ); + static void SaveUploadReturned(LPVOID lpParam, SonyRemoteStorage::Status s, int error_code); + static void CancelSaveUploadCallback(LPVOID lpParam); + static int SaveTransferDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int CrossSaveUploadFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif + +#if defined _XBOX_ONE || defined __ORBIS__ + static int CopySaveDialogReturned(void *pParam,int iPad,C4JStorage::EMessageResult result); + static int CopySaveThreadProc( LPVOID lpParameter ); + static int CopySaveDataReturned( LPVOID lpParameter, bool success, C4JStorage::ESaveGameState state ); + static bool CopySaveDataProgress(LPVOID lpParam, int percent); + static void CancelCopySaveCallback(LPVOID lpParam); + static int CopySaveErrorDialogFinishedCallback(void *pParam,int iPad,C4JStorage::EMessageResult result); +#endif +}; + + + + + + + + + + diff --git a/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp b/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp index c382740f..de95eb43 100644 --- a/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_MainMenu.cpp @@ -801,7 +801,7 @@ int UIScene_MainMenu::CreateLoad_SignInReturned(void *pParam, bool bContinue, in #ifdef _XBOX_ONE ui.ShowPlayerDisplayname(true); #endif - proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); + proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadCreateJoinMenu); } else { @@ -864,7 +864,7 @@ int UIScene_MainMenu::CreateLoad_SignInReturned(void *pParam, bool bContinue, in #ifdef _XBOX_ONE ui.ShowPlayerDisplayname(true); #endif - proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); + proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadCreateJoinMenu); #endif } } @@ -878,7 +878,7 @@ int UIScene_MainMenu::CreateLoad_SignInReturned(void *pParam, bool bContinue, in #ifdef _XBOX_ONE ui.ShowPlayerDisplayname(true); #endif - proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); + proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadCreateJoinMenu); #endif } } @@ -1438,7 +1438,7 @@ void UIScene_MainMenu::RunPlayGame(int iPad) #ifdef _XBOX_ONE ui.ShowPlayerDisplayname(true); #endif - proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); + proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadCreateJoinMenu); #endif } else @@ -1510,7 +1510,7 @@ void UIScene_MainMenu::RunPlayGame(int iPad) #ifdef _XBOX_ONE ui.ShowPlayerDisplayname(true); #endif - proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadOrJoinMenu); + proceedToScene(ProfileManager.GetPrimaryPad(), eUIScene_LoadCreateJoinMenu); #endif } } diff --git a/Minecraft.Client/Platform_Libs/Dev/Storage/4J_Storage.cpp b/Minecraft.Client/Platform_Libs/Dev/Storage/4J_Storage.cpp new file mode 100644 index 00000000..12fba123 --- /dev/null +++ b/Minecraft.Client/Platform_Libs/Dev/Storage/4J_Storage.cpp @@ -0,0 +1,357 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "../../../Windows64/4JLibs/inc/4J_Storage.h" +#include "STO_Main.h" + +C4JStorage StorageManager; +XMARKETPLACE_CONTENTOFFER_INFO InternalContentOfferInfo; + +C4JStorage::C4JStorage() {} + +void C4JStorage::Tick(void) +{ + InternalStorageManager.Tick(); +} + +C4JStorage::EMessageResult C4JStorage::RequestMessageBox(UINT uiTitle, UINT uiText, UINT *uiOptionA, UINT uiOptionC, DWORD dwPad, + int (*Func)(LPVOID, int, const C4JStorage::EMessageResult), LPVOID lpParam, + C4JStringTable *pStringTable, WCHAR *pwchFormatString, DWORD dwFocusButton) +{ + return EMessage_Undefined; +} + +C4JStorage::EMessageResult C4JStorage::GetMessageBoxResult() +{ + return EMessage_Undefined; +} + +bool C4JStorage::SetSaveDevice(int (*Func)(LPVOID, const bool), LPVOID lpParam, bool bForceResetOfSaveDevice) +{ + return true; +} + +void C4JStorage::Init(unsigned int uiSaveVersion, LPCWSTR pwchDefaultSaveName, char *pszSavePackName, int iMinimumSaveSize, + int (*Func)(LPVOID, const ESavingMessage, int), LPVOID lpParam, LPCSTR szGroupID) +{ + InternalStorageManager.Init(Func, lpParam, szGroupID); +} + +void C4JStorage::ResetSaveData() +{ + InternalStorageManager.m_SaveGame.ResetSaveData(); +} + +void C4JStorage::SetDefaultSaveNameForKeyboardDisplay(LPCWSTR pwchDefaultSaveName) +{ + ; +} + +void C4JStorage::SetSaveTitle(LPCWSTR pwchDefaultSaveName) +{ + InternalStorageManager.m_SaveGame.SetSaveTitle(pwchDefaultSaveName); +} + +LPCWSTR C4JStorage::GetSaveTitle() +{ + return InternalStorageManager.m_SaveGame.GetSaveTitle(); +} + +bool C4JStorage::GetSaveUniqueNumber(INT *piVal) +{ + return InternalStorageManager.m_SaveGame.GetSaveUniqueNumber(piVal); +} + +bool C4JStorage::GetSaveUniqueFilename(char *pszName) +{ + return InternalStorageManager.m_SaveGame.GetSaveUniqueFilename(pszName); +} + +void C4JStorage::SetSaveUniqueFilename(char *szFilename) +{ + InternalStorageManager.m_SaveGame.SetSaveUniqueFilename(szFilename); +} + +void C4JStorage::SetState(ESaveGameControlState eControlState, int (*Func)(LPVOID, const bool), LPVOID lpParam) +{ + ; +} + +void C4JStorage::SetSaveDisabled(bool bDisable) +{ + InternalStorageManager.m_SaveGame.SetSaveDisabled(bDisable); +} + +bool C4JStorage::GetSaveDisabled(void) +{ + return InternalStorageManager.m_SaveGame.GetSaveDisabled(); +} + +unsigned int C4JStorage::GetSaveSize() +{ + return InternalStorageManager.m_SaveGame.GetSaveSize(); +} + +void C4JStorage::GetSaveData(void *pvData, unsigned int *puiBytes) +{ + InternalStorageManager.m_SaveGame.GetSaveData(pvData, puiBytes); +} + +PVOID C4JStorage::AllocateSaveData(unsigned int uiBytes) +{ + return InternalStorageManager.m_SaveGame.AllocateSaveData(uiBytes); +} + +void C4JStorage::SetSaveImages(PBYTE pbThumbnail, DWORD dwThumbnailBytes, PBYTE pbImage, DWORD dwImageBytes, PBYTE pbTextData, DWORD dwTextDataBytes) +{ + InternalStorageManager.m_SaveGame.SetSaveImages(pbThumbnail, dwThumbnailBytes, pbImage, dwImageBytes, pbTextData, dwTextDataBytes); +} + +void C4JStorage::SetDefaultImages(PBYTE pbOptionsImage, DWORD dwOptionsImageBytes, PBYTE pbSaveImage, DWORD dwSaveImageBytes, PBYTE pbSaveThumbnail, DWORD dwSaveThumbnailBytes) +{ + InternalStorageManager.m_SaveGame.SetDefaultImages(pbOptionsImage, dwOptionsImageBytes, pbSaveImage, dwSaveImageBytes, pbSaveThumbnail, dwSaveThumbnailBytes); +} + +void C4JStorage::GetDefaultSaveImage(PBYTE *ppbSaveImage, DWORD *pdwSaveImageBytes) +{ + InternalStorageManager.m_SaveGame.GetDefaultSaveImage(ppbSaveImage, pdwSaveImageBytes); +} + +void C4JStorage::GetDefaultSaveThumbnail(PBYTE *ppbSaveThumbnail, DWORD *pdwSaveThumbnailBytes) +{ + InternalStorageManager.m_SaveGame.GetDefaultSaveThumbnail(ppbSaveThumbnail, pdwSaveThumbnailBytes); +} + +C4JStorage::ESaveGameState C4JStorage::SaveSaveData(int (*Func)(LPVOID, const bool), LPVOID lpParam) +{ + return InternalStorageManager.m_SaveGame.SaveSaveData(Func, lpParam); +} + +void C4JStorage::CopySaveDataToNewSave(PBYTE pbThumbnail, DWORD cbThumbnail, WCHAR *wchNewName, int (*Func)(LPVOID lpParam, bool), LPVOID lpParam) +{ + InternalStorageManager.m_SaveGame.CopySaveDataToNewSave(pbThumbnail, cbThumbnail, wchNewName, Func, lpParam); +} + +void C4JStorage::SetSaveDeviceSelected(unsigned int uiPad, bool bSelected) +{ + ; +} + +bool C4JStorage::GetSaveDeviceSelected(unsigned int iPad) +{ + return true; +} + +C4JStorage::ESaveGameState C4JStorage::DoesSaveExist(bool *pbExists) +{ + return InternalStorageManager.m_SaveGame.DoesSaveExist(pbExists); +} + +bool C4JStorage::EnoughSpaceForAMinSaveGame() +{ + return true; +} + +void C4JStorage::SetMaxSaves(int iMaxC) +{ + (void)iMaxC; +} + +void C4JStorage::SetIncompleteSaveCallback(void (*Func)(LPVOID, const ESaveIncompleteType, int blocksRequired), LPVOID param) +{ + (void)Func; + (void)param; +} + +void C4JStorage::ContinueIncompleteOperation() +{ +} + +C4JStorage::ESaveGameState C4JStorage::GetSaveState() +{ + return C4JStorage::ESaveGame_Idle; +} + +void C4JStorage::SetSaveMessageVPosition(float fY) +{ + ; +} + +C4JStorage::ESaveGameState C4JStorage::GetSavesInfo(int iPad, int (*Func)(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, const bool), LPVOID lpParam, + char *pszSavePackName) +{ + return InternalStorageManager.m_SaveGame.GetSavesInfo(iPad, Func, lpParam, pszSavePackName); +} + +PSAVE_DETAILS C4JStorage::ReturnSavesInfo() +{ + return InternalStorageManager.m_SaveGame.ReturnSavesInfo(); +} + +void C4JStorage::ClearSavesInfo() +{ + InternalStorageManager.m_SaveGame.ClearSavesInfo(); +} + +C4JStorage::ESaveGameState C4JStorage::LoadSaveDataThumbnail(PSAVE_INFO pSaveInfo, + int (*Func)(LPVOID lpParam, PBYTE pbThumbnail, DWORD dwThumbnailBytes), LPVOID lpParam) +{ + return InternalStorageManager.m_SaveGame.LoadSaveDataThumbnail(pSaveInfo, Func, lpParam); +} + +void C4JStorage::GetSaveCacheFileInfo(DWORD dwFile, XCONTENT_DATA &xContentData) +{ + ; +} + +void C4JStorage::GetSaveCacheFileInfo(DWORD dwFile, PBYTE *ppbImageData, DWORD *pdwImageBytes) +{ + ; +} + +C4JStorage::ESaveGameState C4JStorage::LoadSaveData(PSAVE_INFO pSaveInfo, int (*Func)(LPVOID lpParam, const bool, const bool), LPVOID lpParam) +{ + return InternalStorageManager.m_SaveGame.LoadSaveData(pSaveInfo, Func, lpParam); +} + +C4JStorage::ESaveGameState C4JStorage::DeleteSaveData(PSAVE_INFO pSaveInfo, int (*Func)(LPVOID lpParam, const bool), LPVOID lpParam) +{ + return InternalStorageManager.m_SaveGame.DeleteSaveData(pSaveInfo, Func, lpParam); +} + +C4JStorage::ESaveGameState C4JStorage::RenameSaveData(int iRenameIndex, uint16_t *pui16NewName, int (*Func)(LPVOID lpParam, const bool), LPVOID lpParam) +{ + return InternalStorageManager.m_SaveGame.RenameSaveData(iRenameIndex, pui16NewName, Func, lpParam); +} + +void C4JStorage::RegisterMarketplaceCountsCallback(int (*Func)(LPVOID lpParam, C4JStorage::DLC_TMS_DETAILS *, int), LPVOID lpParam) +{ + ; +} + +void C4JStorage::SetDLCPackageRoot(char *pszDLCRoot) +{ + InternalStorageManager.m_DLC.SetPackageRoot(pszDLCRoot); +} + +C4JStorage::EDLCStatus C4JStorage::GetDLCOffers(int iPad, int (*Func)(LPVOID, int, DWORD, int), LPVOID lpParam, DWORD dwOfferTypesBitmask) +{ + return EDLC_Idle; +} + +DWORD C4JStorage::CancelGetDLCOffers() +{ + return 0; +} + +void C4JStorage::ClearDLCOffers() +{ + ; +} + +XMARKETPLACE_CONTENTOFFER_INFO &C4JStorage::GetOffer(DWORD dw) +{ + return InternalContentOfferInfo; +} + +int C4JStorage::GetOfferCount() +{ + return 0; +} + +DWORD C4JStorage::InstallOffer(int iOfferIDC, uint64_t *ullOfferIDA, int (*Func)(LPVOID, int, int), LPVOID lpParam, bool bTrial) +{ + return 0; +} + +DWORD C4JStorage::GetAvailableDLCCount(int iPad) +{ + return InternalStorageManager.m_DLC.GetAvailableDLCCount(iPad); +} + +C4JStorage::EDLCStatus C4JStorage::GetInstalledDLC(int iPad, int (*Func)(LPVOID, int, int), LPVOID lpParam) +{ + return InternalStorageManager.m_DLC.GetInstalledDLC(iPad, Func, lpParam); +} + +XCONTENT_DATA &C4JStorage::GetDLC(DWORD dw) +{ + return InternalStorageManager.m_DLC.GetDLC(dw); +} + +DWORD C4JStorage::MountInstalledDLC(int iPad, DWORD dwDLC, int (*Func)(LPVOID, int, DWORD, DWORD), LPVOID lpParam, LPCSTR szMountDrive) +{ + return InternalStorageManager.m_DLC.MountInstalledDLC(iPad, dwDLC, Func, lpParam, szMountDrive); +} + +DWORD C4JStorage::UnmountInstalledDLC(LPCSTR szMountDrive) +{ + return InternalStorageManager.m_DLC.UnmountInstalledDLC(szMountDrive); +} + +void C4JStorage::GetMountedDLCFileList(const char *szMountDrive, std::vector &fileList) +{ + InternalStorageManager.m_DLC.GetMountedDLCFileList(szMountDrive, fileList); +} + +std::string C4JStorage::GetMountedPath(std::string szMount) +{ + return InternalStorageManager.m_DLC.GetMountedPath(szMount); +} + +C4JStorage::ETMSStatus C4JStorage::ReadTMSFile(int iQuadrant, eGlobalStorage eStorageFacility, C4JStorage::eTMS_FileType eFileType, + WCHAR *pwchFilename, BYTE **ppBuffer, DWORD *pdwBufferSize, + int (*Func)(LPVOID, WCHAR *, int, bool, int), LPVOID lpParam, int iAction) +{ + return ETMSStatus_Idle; +} + +bool C4JStorage::WriteTMSFile(int iQuadrant, eGlobalStorage eStorageFacility, WCHAR *pwchFilename, BYTE *pBuffer, DWORD dwBufferSize) +{ + return true; +} + +bool C4JStorage::DeleteTMSFile(int iQuadrant, eGlobalStorage eStorageFacility, WCHAR *pwchFilename) +{ + return true; +} + +void C4JStorage::StoreTMSPathName(WCHAR *pwchName) +{ + ; +} + +C4JStorage::ETMSStatus C4JStorage::TMSPP_ReadFile(int iPad, C4JStorage::eGlobalStorage eStorageFacility, C4JStorage::eTMS_FILETYPEVAL eFileTypeVal, + LPCSTR szFilename, int (*Func)(LPVOID, int, int, PTMSPP_FILEDATA, LPCSTR), LPVOID lpParam, + int iUserData) +{ + return ETMSStatus_Idle; +} + +unsigned int C4JStorage::CRC(unsigned char *buf, int len) +{ + return 0; +} + + diff --git a/Minecraft.Client/Platform_Libs/Dev/Storage/STO_DLC.cpp b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_DLC.cpp new file mode 100644 index 00000000..e939ebcf --- /dev/null +++ b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_DLC.cpp @@ -0,0 +1,344 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "STO_DLC.h" +#include "STO_Main.h" + +#ifdef __linux__ +#include +#include +#include +#include +#endif + +XCONTENT_DATA &CDLC::GetDLC(DWORD dw) +{ + return m_vInstalledDLCs[dw]; +} + +CDLC::CDLC(void) : m_vInstalledDLCs(), m_szMountPath(), m_vDLCDriveMappings() +{ + m_iHasNewInstalledDLCs = false; + dword0 = 0; + dwordC0 = 0; + m_iHasNewMountedDLCs = false; // @Patoke fix + + ZeroMemory(m_szDLCProductCode, sizeof(m_szDLCProductCode)); + ZeroMemory(m_szProductUpgradeKey, sizeof(m_szProductUpgradeKey)); +} + +C4JStorage::EDLCStatus CDLC::GetOffers(int iPad, int (*Func)(LPVOID, int, DWORD, int), LPVOID lpParam, DWORD dwOfferTypesBitmask) +{ + return C4JStorage::EDLC_NoOffers; +} + +void CDLC::ClearOffers() +{ + ; +} + +C4JStorage::EDLCStatus CDLC::GetInstalledDLC(int iPad, int (*Func)(LPVOID, int, int), LPVOID lpParam) +{ + if (m_iHasNewInstalledDLCs) + { + return C4JStorage::EDLC_Pending; + } + + m_pInstalledDLCFunc = Func; + m_pInstalledDLCParam = lpParam; + m_iHasNewInstalledDLCs = true; + +#ifdef __linux__ + const char *dlcDir = NULL; + struct stat stDir; + if (stat("Windows64Media/DLC", &stDir) == 0 && S_ISDIR(stDir.st_mode)) + { + dlcDir = "Windows64Media/DLC"; + } + else if (stat("Windows64/DLC", &stDir) == 0 && S_ISDIR(stDir.st_mode)) + { + dlcDir = "Windows64/DLC"; + } + + if (!dlcDir) + { + InternalStorageManager.DebugPrintf("No DLC directory, can't have any DLC installed\n"); + return C4JStorage::EDLC_Error; + } + + DIR *dir = opendir(dlcDir); + if (dir) + { + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_name[0] == '.') + continue; + char fullPath[512]; + snprintf(fullPath, sizeof(fullPath), "%s/%s", dlcDir, entry->d_name); + struct stat stEntry; + if (stat(fullPath, &stEntry) == 0 && S_ISDIR(stEntry.st_mode)) + { + XCONTENT_DATA data; + snprintf(data.szFileName, sizeof(data.szFileName), "%s/%s", dlcDir, entry->d_name); + swprintf(data.szDisplayName, 256, L"%s", entry->d_name); + data.DeviceID = 0; + data.dwContentType = 0; + AddInstalled(&data); + } + } + closedir(dir); + } +#else + bool ret = false; + DWORD atts = GetFileAttributesA("Windows64Media/DLC"); + if (atts == -1) + { + atts = GetFileAttributesA("Windows64/DLC"); + ret = true; + } + + bool validDir = atts != -1 && (atts & FILE_ATTRIBUTE_DIRECTORY); + if (!validDir) + { + InternalStorageManager.DebugPrintf("No DLC directory, can't have any DLC installed\n"); + return C4JStorage::EDLC_Error; + } + + _WIN32_FIND_DATAA hFind; + HANDLE hFindFile; + if (ret) + { + hFindFile = FindFirstFileA("Windows64/DLC/*", &hFind); + } + else + { + hFindFile = FindFirstFileA("Windows64Media/DLC/*", &hFind); + } + + if (hFindFile != (HANDLE)-1LL) + { + do + { + atts = hFind.dwFileAttributes; + + bool isArt = hFind.dwFileAttributes != -1 && (hFind.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + if (isArt && hFind.cFileName[0] != '.') + { + XCONTENT_DATA data; + + if (ret) + { + sprintf(data.szFileName, "Windows64/DLC/%s", hFind.cFileName); + } + else + { + sprintf(data.szFileName, "Windows64Media/DLC/%s", hFind.cFileName); + } + + swprintf(data.szDisplayName, 256, L"%s", hFind.cFileName); + int displayNameLen = wcslen(data.szDisplayName); + + data.DeviceID = 0; + data.dwContentType = 0; + + AddInstalled(&data); + } + } while (FindNextFileA(hFindFile, &hFind)); + FindClose(hFindFile); + } +#endif + + return C4JStorage::EDLC_Idle; +} + +DWORD CDLC::MountInstalledDLC(int iPad, DWORD dwDLC, int (*Func)(LPVOID, int, DWORD, DWORD), LPVOID lpParam, LPCSTR szMountDrive) +{ + this->m_pMountedDLCFunc = Func; + this->m_pMountedDLCParam = lpParam; + + if (szMountDrive) + { + m_szMountPath = szMountDrive; + } + else + { + m_szMountPath = this->m_szPackageRoot; + } + + this->m_uiCurrentMappedDLC = dwDLC; + + char *dlcdirPath = m_vInstalledDLCs[m_uiCurrentMappedDLC].szFileName; + m_vDLCDriveMappings.push_back(DriveMapping(m_szMountPath, dlcdirPath)); + + dword94 = 0xFFFFFFFF; + m_iHasNewMountedDLCs = true; + + return 997; +} + +DWORD CDLC::UnmountInstalledDLC(LPCSTR szMountDrive) +{ + LPCSTR szDrive = nullptr; + + if (szMountDrive) + { + szDrive = szMountDrive; + } + else + { + szDrive = this->m_szPackageRoot; + } + + for (int i = 0; i < this->m_vDLCDriveMappings.size(); i++) + { + if (m_vDLCDriveMappings[i].m_szDirectoryPath == szDrive) + { + m_vDLCDriveMappings.erase(m_vDLCDriveMappings.begin() + i); + + return 0; + } + } + return 0; +} + +void CDLC::GetMountedDLCFileList(const char *szMountDrive, std::vector &fileList) +{ +#ifdef __linux__ + const char *basePath = m_vInstalledDLCs[m_uiCurrentMappedDLC].szFileName; + DIR *dir = opendir(basePath); + if (dir) + { + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_name[0] == '.') + continue; + char fullPath[256]; + snprintf(fullPath, sizeof(fullPath), "%s/%s", basePath, entry->d_name); + struct stat st; + if (stat(fullPath, &st) == 0 && !S_ISDIR(st.st_mode)) + { + fileList.push_back(fullPath); + } + } + closedir(dir); + } +#else + char *dlcdirPath = new char[256]; + sprintf(dlcdirPath, "%s/*", m_vInstalledDLCs[m_uiCurrentMappedDLC].szFileName); + + _WIN32_FIND_DATAA atts; + HANDLE hFind = FindFirstFileA(dlcdirPath, &atts); + if (hFind != (HANDLE)-1LL) + { + do + { + if (atts.dwFileAttributes == -1 || (atts.dwFileAttributes & 0x10) != 0x10) + { + char dir[256]; + sprintf(dir, "%s/%s", m_vInstalledDLCs[m_uiCurrentMappedDLC].szFileName, atts.cFileName); + + fileList.push_back(dir); + } + } while (FindNextFileA(hFind, &atts)); + FindClose(hFind); + } + delete[] dlcdirPath; +#endif +} + +std::string CDLC::GetMountedPath(std::string szMount) +{ + for (int ch = 0; ch < szMount.size(); ++ch) + { + if (szMount[ch] == '/' || szMount[ch] == '\\') + { + return ""; + } + + if (szMount[ch] == ':') + { + std::string driveName = szMount.substr(0, ch); + for (int i = 0; i < m_vDLCDriveMappings.size(); ++i) + { + if (m_vDLCDriveMappings[i].m_szDirectoryPath == driveName) + { + std::string newPath = m_vDLCDriveMappings[i].m_szMountPath; + + newPath.append(szMount.substr(ch + 1, -1)); + + return newPath; + } + } + break; + } + } + + return ""; +} + +void CDLC::SetDLCProductCode(const char *szProductCode) +{ + strcpy(m_szDLCProductCode, szProductCode); +} + +void CDLC::SetProductUpgradeKey(const char *szProductCode) +{ + strcpy(m_szProductUpgradeKey, szProductCode); +} + +int CDLC::GetAvailableDLCCount(int iPad) +{ + return 0; +} + +void CDLC::SetPackageRoot(char *pszDLCRoot) +{ + strcpy(this->m_szPackageRoot, pszDLCRoot); +} + +void CDLC::Tick(void) +{ + if (m_iHasNewInstalledDLCs) + { + m_iHasNewInstalledDLCs = false; + m_pInstalledDLCFunc(m_pInstalledDLCParam, m_vInstalledDLCs.size(), 0); + } + if (m_iHasNewMountedDLCs) + { + m_iHasNewMountedDLCs = false; + m_pMountedDLCFunc(m_pMountedDLCParam, 0, 0, dword94); + } +} + +void CDLC::AddInstalled(XCONTENT_DATA *data) +{ + m_vInstalledDLCs.push_back(*data); +} + +DWORD CDLC::CancelOffers(void) +{ + return 0; +} diff --git a/Minecraft.Client/Platform_Libs/Dev/Storage/STO_DLC.h b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_DLC.h new file mode 100644 index 00000000..89603611 --- /dev/null +++ b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_DLC.h @@ -0,0 +1,81 @@ +#pragma once +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "../../../Windows64/4JLibs/inc/4J_Storage.h" + +class CDLC +{ +public: + struct DriveMapping + { + DriveMapping(std::string szDirectoryPath, std::string szMountPath) : m_szDirectoryPath(szDirectoryPath), m_szMountPath(szMountPath) + { + ; + } + + std::string m_szDirectoryPath; + std::string m_szMountPath; + }; + + XCONTENT_DATA &GetDLC(DWORD dw); + CDLC(void); + + C4JStorage::EDLCStatus GetOffers(int iPad, int (*Func)(LPVOID, int, DWORD, int), LPVOID lpParam, + DWORD dwOfferTypesBitmask = XMARKETPLACE_OFFERING_TYPE_CONTENT); + void ClearOffers(); + C4JStorage::EDLCStatus GetInstalledDLC(int iPad, int (*Func)(LPVOID, int, int), LPVOID lpParam); + DWORD MountInstalledDLC(int iPad, DWORD dwDLC, int (*Func)(LPVOID, int, DWORD, DWORD), LPVOID lpParam, LPCSTR szMountDrive = NULL); + DWORD UnmountInstalledDLC(LPCSTR szMountDrive = NULL); + void GetMountedDLCFileList(const char *szMountDrive, std::vector &fileList); + std::string GetMountedPath(std::string szMount); + + void SetDLCProductCode(const char *szProductCode); + void SetProductUpgradeKey(const char *szProductCode); + int GetAvailableDLCCount(int iPad); + void SetPackageRoot(char *pszDLCRoot); + + void Tick(void); + void AddInstalled(XCONTENT_DATA *data); + DWORD CancelOffers(void); + + DWORD dword0; + int (*m_pInstalledDLCFunc)(LPVOID, int, int); + LPVOID m_pInstalledDLCParam; + BYTE gap18[16]; + int m_iHasNewInstalledDLCs; + std::vector m_vInstalledDLCs; + BYTE gap48[4]; + DWORD m_iHasNewMountedDLCs; + int (*m_pMountedDLCFunc)(LPVOID, int, DWORD, DWORD); + LPVOID m_pMountedDLCParam; + std::string m_szMountPath; + DWORD m_uiCurrentMappedDLC; + DWORD dword94; + char m_szPackageRoot[40]; + DWORD dwordC0; + std::vector m_vDLCDriveMappings; + char m_szDLCProductCode[16]; + char m_szProductUpgradeKey[60]; +}; diff --git a/Minecraft.Client/Platform_Libs/Dev/Storage/STO_Main.cpp b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_Main.cpp new file mode 100644 index 00000000..7f4e8124 --- /dev/null +++ b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_Main.cpp @@ -0,0 +1,94 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "STO_Main.h" +#include "../../../Windows64/4JLibs/inc/4J_Storage.h" + +CStorage InternalStorageManager; + +CStorage::CStorage(void) +{ + m_SaveGame = CSaveGame(); + m_DLC = CDLC(); +} + +void CStorage::Init(int (*Func)(LPVOID, const C4JStorage::ESavingMessage, int), LPVOID lpParam, LPCSTR szGroupID) {} + +void CStorage::Tick(void) +{ + m_DLC.Tick(); +} + +unsigned int CStorage::CRC(unsigned char *buf, int len) +{ + return ~UpdateCRC(0xFFFFFFFF, buf, len); +} + +void CStorage::MakeCRCTable(void) +{ + for (int c = 0; c < 256; ++c) + { + unsigned int k = c; + for (int n = 0; n < 8; ++n) + { + if ((k & 1) != 0) + { + k = (k >> 1) ^ 0xEDB88320; + } + else + { + k >>= 1; + } + } + m_CRCTable[c] = k; + } + m_bHasCRCTable = true; +} + +unsigned int CStorage::UpdateCRC(unsigned int crc, unsigned __int8 *buf, int len) +{ + if (!m_bHasCRCTable) + { + MakeCRCTable(); + } + + for (int c = 0; c < len; ++c) + { + crc = (crc >> 8) ^ m_CRCTable[(unsigned __int8)(buf[c] ^ crc)]; + } + + return crc; +} + +void CStorage::DebugPrintf(const char *szFormat, ...) +{ + char buf[1024]; + + va_list va; + va_start(va, szFormat); + + vsnprintf(buf, 1024, szFormat, va); + + va_end(va); +} diff --git a/Minecraft.Client/Platform_Libs/Dev/Storage/STO_Main.h b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_Main.h new file mode 100644 index 00000000..95803eee --- /dev/null +++ b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_Main.h @@ -0,0 +1,49 @@ +#pragma once +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "STO_DLC.h" +#include "STO_SaveGame.h" + +class CStorage +{ +public: + CStorage(void); + + void Init(int (*Func)(LPVOID, const C4JStorage::ESavingMessage, int), LPVOID lpParam, LPCSTR szGroupID); + void Tick(void); + unsigned int CRC(unsigned char *buf, int len); + void MakeCRCTable(void); + unsigned int UpdateCRC(unsigned int crc, unsigned __int8 *buf, int len); + void DebugPrintf(const char *szFormat, ...); + + BYTE gap0[8]; + CSaveGame m_SaveGame; + CDLC m_DLC; + BYTE gap278[0x10]; + DWORD m_CRCTable[256]; + bool m_bHasCRCTable; +}; + +extern CStorage InternalStorageManager; \ No newline at end of file diff --git a/Minecraft.Client/Platform_Libs/Dev/Storage/STO_SaveGame.cpp b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_SaveGame.cpp new file mode 100644 index 00000000..7646a2d1 --- /dev/null +++ b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_SaveGame.cpp @@ -0,0 +1,1227 @@ +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "STO_SaveGame.h" +#include + +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +static unsigned long s_pngCrcTable[256]; +static bool s_pngCrcTableReady = false; + +static void BuildPngCrcTable() +{ + for (unsigned int n = 0; n < 256; n++) + { + unsigned long c = n; + for (int k = 0; k < 8; k++) + c = (c & 1) ? (0xEDB88320L ^ (c >> 1)) : (c >> 1); + s_pngCrcTable[n] = c; + } + s_pngCrcTableReady = true; +} + +static unsigned long PngCrc32(const unsigned char *buf, unsigned int len) +{ + if (!s_pngCrcTableReady) BuildPngCrcTable(); + unsigned long c = 0xFFFFFFFFL; + for (unsigned int i = 0; i < len; i++) + c = s_pngCrcTable[(c ^ buf[i]) & 0xFF] ^ (c >> 8); + return c ^ 0xFFFFFFFFL; +} + +static inline unsigned int WriteBE32(unsigned int v) +{ + return ((v >> 24) & 0xFF) | + ((v >> 8) & 0xFF00) | + ((v << 8) & 0xFF0000) | + ((v << 24) & 0xFF000000); +} + +static void GetGameHDDPath(char *outPath, int maxLen) +{ + char curDir[256]; +#ifdef __linux__ + getcwd(curDir, sizeof(curDir)); + snprintf(outPath, maxLen, "%s/Linux/GameHDD", curDir); +#else + GetCurrentDirectoryA(sizeof(curDir), curDir); + sprintf_s(outPath, maxLen, "%s\\Windows64\\GameHDD", curDir); +#endif +} + +CSaveGame::CSaveGame() +{ + m_pSaveData = nullptr; + m_uiSaveSize = 0; + m_bIsSafeDisabled = false; + + ZeroMemory(m_szSaveUniqueName, sizeof(m_szSaveUniqueName)); + ZeroMemory(m_wszSaveTitle, sizeof(m_wszSaveTitle)); + + m_pSaveDetails = nullptr; + m_bHasSaveDetails = false; + + m_pbThumbnail = nullptr; + m_dwThumbnailBytes = 0; + m_pbDefaultThumbnail = nullptr; + m_dwDefaultThumbnailBytes = 0; + m_pbDefaultSaveImage = nullptr; + m_dwDefaultSaveImageBytes = 0; + + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + +#ifdef __linux__ + char curDir[256]; + getcwd(curDir, sizeof(curDir)); + char win64Path[256]; + snprintf(win64Path, sizeof(win64Path), "%s/Linux", curDir); + mkdir(win64Path, 0755); + mkdir(gameHDDPath, 0755); +#else + char win64Path[256]; + char curDir[256]; + GetCurrentDirectoryA(sizeof(curDir), curDir); + sprintf_s(win64Path, sizeof(win64Path), "%s\\Windows64", curDir); + CreateDirectoryA(win64Path, 0); + CreateDirectoryA(gameHDDPath, 0); +#endif +} + +void CSaveGame::SetSaveDisabled(bool bDisable) +{ + m_bIsSafeDisabled = bDisable; +} + +bool CSaveGame::GetSaveDisabled(void) +{ + return m_bIsSafeDisabled; +} + +void CSaveGame::ResetSaveData() +{ + free(m_pSaveData); + m_pSaveData = nullptr; + m_uiSaveSize = 0; + + ZeroMemory(m_szSaveUniqueName, sizeof(m_szSaveUniqueName)); + ZeroMemory(m_wszSaveTitle, sizeof(m_wszSaveTitle)); + + if (m_pbThumbnail) + { + free(m_pbThumbnail); + m_pbThumbnail = nullptr; + m_dwThumbnailBytes = 0; + } +} + +C4JStorage::ESaveGameState CSaveGame::GetSavesInfo(int iPad, int (*Func)(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, const bool), LPVOID lpParam, + char *pszSavePackName) +{ + if (!m_pSaveDetails) + { + m_pSaveDetails = new SAVE_DETAILS(); + memset(m_pSaveDetails, 0, sizeof(SAVE_DETAILS)); + } + + delete[] m_pSaveDetails->SaveInfoA; + m_pSaveDetails->SaveInfoA = nullptr; + m_pSaveDetails->iSaveC = 0; + + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + +#ifdef __linux__ + int resultCount = 0; + DIR *dir = opendir(gameHDDPath); + if (!dir) + { + + } + else + { + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + char saveFilePath[512]; + snprintf(saveFilePath, sizeof(saveFilePath), "%s/%s/saveData.ms", gameHDDPath, entry->d_name); + struct stat st; + if (stat(saveFilePath, &st) == 0) + resultCount++; + } + closedir(dir); + } + + if (resultCount > 0) + { + m_pSaveDetails->SaveInfoA = new SAVE_INFO[resultCount]; + memset(m_pSaveDetails->SaveInfoA, 0, sizeof(SAVE_INFO) * resultCount); + m_pSaveDetails->iSaveC = 0; + + int i = 0; + dir = opendir(gameHDDPath); + if (dir) + { + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + char saveFilePath[512]; + snprintf(saveFilePath, sizeof(saveFilePath), "%s/%s/saveData.ms", gameHDDPath, entry->d_name); + struct stat stFile; + if (stat(saveFilePath, &stFile) != 0) + continue; + + strncpy(m_pSaveDetails->SaveInfoA[i].UTF8SaveFilename, entry->d_name, sizeof(m_pSaveDetails->SaveInfoA[i].UTF8SaveFilename) - 1); + + char saveDirPath[512]; + snprintf(saveDirPath, sizeof(saveDirPath), "%s/%s", gameHDDPath, entry->d_name); + + char titleBuf[MAX_DISPLAYNAME_LENGTH]; + if (LoadTitleFromFile(saveDirPath, titleBuf, sizeof(titleBuf))) + { + strncpy(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, titleBuf, sizeof(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle) - 1); + } + else + { + strncpy(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, entry->d_name, sizeof(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle) - 1); + } + + m_pSaveDetails->SaveInfoA[i].metaData.dataSize = (DWORD)stFile.st_size; + + char thumbFilePath[512]; + snprintf(thumbFilePath, sizeof(thumbFilePath), "%s/%s/saveThumbnail.png", gameHDDPath, entry->d_name); + struct stat stThumb; + if (stat(thumbFilePath, &stThumb) == 0) + { + m_pSaveDetails->SaveInfoA[i].metaData.thumbnailSize = (DWORD)stThumb.st_size; + } + + m_pSaveDetails->SaveInfoA[i].metaData.modifiedTime = stFile.st_mtime; + + i++; + m_pSaveDetails->iSaveC++; + } + closedir(dir); + } + } + +#else + WIN32_FIND_DATAA findFileData; + WIN32_FILE_ATTRIBUTE_DATA fileInfoBuffer; + + char searchPattern[280]; + sprintf_s(searchPattern, sizeof(searchPattern), "%s\\*", gameHDDPath); + + int resultCount = 0; + HANDLE h = FindFirstFileExA(searchPattern, FindExInfoStandard, &findFileData, FindExSearchLimitToDirectories, 0, 0); + if (h == INVALID_HANDLE_VALUE) + { + DWORD error = GetLastError(); + printf("Error finding save dirs: 0x%08x\n", error); + } + else + { + do + { + if ((findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && + strcmp(findFileData.cFileName, ".") != 0 && + strcmp(findFileData.cFileName, "..") != 0) + { + char saveFilePath[512]; + sprintf_s(saveFilePath, sizeof(saveFilePath), "%s\\%s\\saveData.ms", gameHDDPath, findFileData.cFileName); + if (GetFileAttributesA(saveFilePath) != INVALID_FILE_ATTRIBUTES) + { + resultCount++; + } + } + } while (FindNextFileA(h, &findFileData)); + FindClose(h); + } + + if (resultCount > 0) + { + m_pSaveDetails->SaveInfoA = new SAVE_INFO[resultCount]; + memset(m_pSaveDetails->SaveInfoA, 0, sizeof(SAVE_INFO) * resultCount); + + m_pSaveDetails->iSaveC = 0; + int i = 0; + HANDLE fi = FindFirstFileExA(searchPattern, FindExInfoStandard, &findFileData, FindExSearchLimitToDirectories, 0, 0); + if (fi != INVALID_HANDLE_VALUE) + { + do + { + if ((findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && + strcmp(findFileData.cFileName, ".") != 0 && + strcmp(findFileData.cFileName, "..") != 0) + { + char saveFilePath[512]; + sprintf_s(saveFilePath, sizeof(saveFilePath), "%s\\%s\\saveData.ms", gameHDDPath, findFileData.cFileName); + + if (GetFileAttributesA(saveFilePath) == INVALID_FILE_ATTRIBUTES) + continue; + + strcpy_s(m_pSaveDetails->SaveInfoA[i].UTF8SaveFilename, findFileData.cFileName); + + char saveDirPath[512]; + sprintf_s(saveDirPath, sizeof(saveDirPath), "%s\\%s", gameHDDPath, findFileData.cFileName); + + char titleBuf[MAX_DISPLAYNAME_LENGTH]; + if (LoadTitleFromFile(saveDirPath, titleBuf, sizeof(titleBuf))) + { + strcpy_s(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, titleBuf); + } + else + { + strcpy_s(m_pSaveDetails->SaveInfoA[i].UTF8SaveTitle, findFileData.cFileName); + } + + GetFileAttributesExA(saveFilePath, GetFileExInfoStandard, &fileInfoBuffer); + m_pSaveDetails->SaveInfoA[i].metaData.dataSize = fileInfoBuffer.nFileSizeLow; + + char thumbFilePath[512]; + sprintf_s(thumbFilePath, sizeof(thumbFilePath), "%s\\%s\\saveThumbnail.png", gameHDDPath, findFileData.cFileName); + WIN32_FILE_ATTRIBUTE_DATA thumbInfo; + if (GetFileAttributesExA(thumbFilePath, GetFileExInfoStandard, &thumbInfo)) + { + m_pSaveDetails->SaveInfoA[i].metaData.thumbnailSize = thumbInfo.nFileSizeLow; + } + + FILETIME ft = fileInfoBuffer.ftLastWriteTime; + ULARGE_INTEGER ull; + ull.LowPart = ft.dwLowDateTime; + ull.HighPart = ft.dwHighDateTime; + + m_pSaveDetails->SaveInfoA[i].metaData.modifiedTime = (time_t)((ull.QuadPart - 116444736000000000ULL) / 10000000ULL); + + i++; + m_pSaveDetails->iSaveC++; + } + } while (FindNextFileA(fi, &findFileData)); + FindClose(fi); + } + } +#endif + + m_bHasSaveDetails = true; + if (Func) + { + Func(lpParam, m_pSaveDetails, true); + } + + return C4JStorage::ESaveGame_Idle; +} + +PSAVE_DETAILS CSaveGame::ReturnSavesInfo() +{ + if (m_bHasSaveDetails) + return m_pSaveDetails; + else + return nullptr; +} + +void CSaveGame::ClearSavesInfo() +{ + m_bHasSaveDetails = false; + if (m_pSaveDetails) + { + if (m_pSaveDetails->SaveInfoA) + { + delete[] m_pSaveDetails->SaveInfoA; + m_pSaveDetails->SaveInfoA = nullptr; + m_pSaveDetails->iSaveC = 0; + } + delete m_pSaveDetails; + m_pSaveDetails = 0; + } +} + +C4JStorage::ESaveGameState CSaveGame::LoadSaveDataThumbnail(PSAVE_INFO pSaveInfo, + int (*Func)(LPVOID lpParam, PBYTE pbThumbnail, DWORD dwThumbnailBytes), LPVOID lpParam) +{ + PBYTE pbThumbnail = nullptr; + DWORD dwThumbnailBytes = 0; + + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + + char thumbPath[512]; +#ifdef __linux__ + snprintf(thumbPath, sizeof(thumbPath), "%s/%s/saveThumbnail.png", gameHDDPath, pSaveInfo->UTF8SaveFilename); + + int fd = open(thumbPath, O_RDONLY); + if (fd >= 0) + { + struct stat st; + if (fstat(fd, &st) == 0 && st.st_size > 0) + { + pbThumbnail = (PBYTE)malloc(st.st_size); + if (pbThumbnail) + { + ssize_t bytesRead = read(fd, pbThumbnail, st.st_size); + if (bytesRead == st.st_size) + { + dwThumbnailBytes = (DWORD)st.st_size; + } + else + { + free(pbThumbnail); + pbThumbnail = nullptr; + } + } + } + close(fd); + } +#else + sprintf_s(thumbPath, sizeof(thumbPath), "%s\\%s\\saveThumbnail.png", gameHDDPath, pSaveInfo->UTF8SaveFilename); + + HANDLE h = CreateFileA(thumbPath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (h != INVALID_HANDLE_VALUE) + { + DWORD fileSize = GetFileSize(h, NULL); + if (fileSize != 0 && fileSize != INVALID_FILE_SIZE) + { + pbThumbnail = (PBYTE)malloc(fileSize); + if (pbThumbnail) + { + DWORD bytesRead = 0; + if (ReadFile(h, pbThumbnail, fileSize, &bytesRead, 0) && bytesRead == fileSize) + { + dwThumbnailBytes = fileSize; + } + else + { + free(pbThumbnail); + pbThumbnail = nullptr; + } + } + } + CloseHandle(h); + } +#endif + + Func(lpParam, pbThumbnail, dwThumbnailBytes); + + if (pbThumbnail) + { + free(pbThumbnail); + } + + return C4JStorage::ESaveGame_GetSaveThumbnail; +} + +C4JStorage::ESaveGameState CSaveGame::LoadSaveData(PSAVE_INFO pSaveInfo, int (*Func)(LPVOID lpParam, const bool, const bool), LPVOID lpParam) +{ + SetSaveUniqueFilename(pSaveInfo->UTF8SaveFilename); + + if (m_pSaveData) + { + free(m_pSaveData); + m_pSaveData = nullptr; + } + + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + +#ifdef __linux__ + char saveDirPath[512]; + snprintf(saveDirPath, sizeof(saveDirPath), "%s/%s", gameHDDPath, m_szSaveUniqueName); + + char titleBuf[MAX_DISPLAYNAME_LENGTH]; + if (LoadTitleFromFile(saveDirPath, titleBuf, sizeof(titleBuf))) + { + mbstowcs(m_wszSaveTitle, titleBuf, MAX_DISPLAYNAME_LENGTH); + } + + char fileName[512]; + snprintf(fileName, sizeof(fileName), "%s/saveData.ms", saveDirPath); + + struct stat stFile; + if (stat(fileName, &stFile) != 0) + { + if (Func) Func(lpParam, 0, false); + return C4JStorage::ESaveGame_Idle; + } + + m_uiSaveSize = (unsigned int)stFile.st_size; + m_pSaveData = malloc(m_uiSaveSize); + + int fd = open(fileName, O_RDONLY); + bool success = false; + if (fd >= 0) + { + ssize_t bytesRead = read(fd, m_pSaveData, m_uiSaveSize); + close(fd); + success = (bytesRead == (ssize_t)m_uiSaveSize); + } + + if (!success && m_pSaveData) + { + free(m_pSaveData); + m_pSaveData = nullptr; + m_uiSaveSize = 0; + } + +#else + char saveDirPath[512]; + sprintf_s(saveDirPath, sizeof(saveDirPath), "%s\\%s", gameHDDPath, m_szSaveUniqueName); + + char titleBuf[MAX_DISPLAYNAME_LENGTH]; + if (LoadTitleFromFile(saveDirPath, titleBuf, sizeof(titleBuf))) + { + MultiByteToWideChar(CP_UTF8, 0, titleBuf, -1, m_wszSaveTitle, MAX_DISPLAYNAME_LENGTH); + } + + char fileName[512]; + sprintf_s(fileName, sizeof(fileName), "%s\\saveData.ms", saveDirPath); + + WIN32_FILE_ATTRIBUTE_DATA fileInfo; + if (!GetFileAttributesExA(fileName, GetFileExInfoStandard, &fileInfo)) + { + if (Func) Func(lpParam, 0, false); + return C4JStorage::ESaveGame_Idle; + } + + m_uiSaveSize = fileInfo.nFileSizeLow; + m_pSaveData = malloc(m_uiSaveSize); + + HANDLE h = CreateFileA(fileName, GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + bool success = false; + if (h != INVALID_HANDLE_VALUE) + { + DWORD bytesRead = 0; + BOOL res = ReadFile(h, m_pSaveData, m_uiSaveSize, &bytesRead, 0); + _ASSERT(res && bytesRead == m_uiSaveSize); + CloseHandle(h); + success = (res && bytesRead == m_uiSaveSize); + } + + if (!success && m_pSaveData) + { + free(m_pSaveData); + m_pSaveData = nullptr; + m_uiSaveSize = 0; + } +#endif + + if (Func) + { + Func(lpParam, 0, success); + } + + return C4JStorage::ESaveGame_Idle; +} + +unsigned int CSaveGame::GetSaveSize() +{ + return m_uiSaveSize; +} + +void CSaveGame::GetSaveData(void *pvData, unsigned int *puiBytes) +{ + if (pvData) + { + memmove(pvData, m_pSaveData, m_uiSaveSize); + *puiBytes = m_uiSaveSize; + } + else + { + *puiBytes = 0; + } +} + +// @Patoke add +bool CSaveGame::GetSaveUniqueNumber(INT *piVal) +{ + if (m_szSaveUniqueName[0] == '\0') + { + return 0; + } + int year, month, day, hour, minute; + sscanf(&m_szSaveUniqueName[4], "%02d%02d%02d%02d%02d", &year, &month, &day, &hour, &minute); + *piVal = 2678400 * year + 86400 * month + 3600 * day + 60 * hour + minute; + return true; +} + +// @Patoke add +bool CSaveGame::GetSaveUniqueFilename(char *pszName) +{ + if (m_szSaveUniqueName[0] == '\0') + { + return false; + } + memset(pszName, 0, 14); + for (int i = 0; i < 12; i++) + { + pszName[i] = m_szSaveUniqueName[i + 2]; + } + return true; +} + +void CSaveGame::SetSaveTitle(LPCWSTR pwchDefaultSaveName) +{ + if (m_szSaveUniqueName[0] == '\0') + { + CreateSaveUniqueName(); + } + wcscpy_s(m_wszSaveTitle, MAX_DISPLAYNAME_LENGTH, pwchDefaultSaveName); +} + +LPCWSTR CSaveGame::GetSaveTitle() +{ + return m_wszSaveTitle; +} + +PVOID CSaveGame::AllocateSaveData(unsigned int uiBytes) +{ + free(m_pSaveData); + + m_pSaveData = malloc(uiBytes); + if (m_pSaveData) + { + m_uiSaveSize = uiBytes; + } + + return m_pSaveData; +} + +// https://github.com/LCEMP/LCEMP +void CSaveGame::SetSaveImages(PBYTE pbThumbnail, DWORD dwThumbnailBytes, PBYTE pbImage, DWORD dwImageBytes, PBYTE pbTextData, DWORD dwTextDataBytes) +{ + if (m_pbThumbnail) + { + free(m_pbThumbnail); + m_pbThumbnail = nullptr; + m_dwThumbnailBytes = 0; + } + + + if (pbThumbnail && dwThumbnailBytes > 0) + { + const DWORD kInsertOffset = 33; // end of PNG sig + IHDR + + if (pbTextData && dwTextDataBytes > 0 && dwThumbnailBytes > kInsertOffset) + { + const DWORD chunkOverhead = 4 + 4 + 4; + const DWORD chunkTotal = chunkOverhead + dwTextDataBytes; + const DWORD newSize = dwThumbnailBytes + chunkTotal; + + m_pbThumbnail = (PBYTE)malloc(newSize); + if (m_pbThumbnail) + { + memcpy(m_pbThumbnail, pbThumbnail, kInsertOffset); + + PBYTE p = m_pbThumbnail + kInsertOffset; + + *(unsigned int *)p = WriteBE32(dwTextDataBytes); + p += 4; + + p[0] = 't'; p[1] = 'E'; p[2] = 'X'; p[3] = 't'; + p += 4; + + memcpy(p, pbTextData, dwTextDataBytes); + p += dwTextDataBytes; + + unsigned long crc = PngCrc32(m_pbThumbnail + kInsertOffset + 4, + 4 + dwTextDataBytes); + *(unsigned int *)p = WriteBE32((unsigned int)crc); + + memcpy(m_pbThumbnail + kInsertOffset + chunkTotal, + pbThumbnail + kInsertOffset, + dwThumbnailBytes - kInsertOffset); + + m_dwThumbnailBytes = newSize; + } + } + else + { + m_pbThumbnail = (PBYTE)malloc(dwThumbnailBytes); + if (m_pbThumbnail) + { + memcpy(m_pbThumbnail, pbThumbnail, dwThumbnailBytes); + m_dwThumbnailBytes = dwThumbnailBytes; + } + } + } +} + +// https://github.com/LCEMP/LCEMP +C4JStorage::ESaveGameState CSaveGame::SaveSaveData(int (*Func)(LPVOID, const bool), LPVOID lpParam) +{ + if (!m_pSaveData || m_uiSaveSize == 0) + { + if (Func) Func(lpParam, false); + return C4JStorage::ESaveGame_Idle; + } + + if (m_szSaveUniqueName[0] == '\0') + { + CreateSaveUniqueName(); + } + + if (!m_pbThumbnail || m_dwThumbnailBytes == 0) + { + PBYTE pbRuntimeThumbnail = nullptr; + DWORD dwRuntimeThumbnailBytes = 0; + app.GetSaveThumbnail(&pbRuntimeThumbnail, &dwRuntimeThumbnailBytes); + + if (pbRuntimeThumbnail && dwRuntimeThumbnailBytes > 0) + { + m_pbThumbnail = pbRuntimeThumbnail; + m_dwThumbnailBytes = dwRuntimeThumbnailBytes; + } + else if (m_pbDefaultThumbnail && m_dwDefaultThumbnailBytes > 0) + { + m_pbThumbnail = (PBYTE)malloc(m_dwDefaultThumbnailBytes); + if (m_pbThumbnail) + { + memcpy(m_pbThumbnail, m_pbDefaultThumbnail, m_dwDefaultThumbnailBytes); + m_dwThumbnailBytes = m_dwDefaultThumbnailBytes; + } + } + } + + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + +#ifdef __linux__ + char saveDirPath[512]; + snprintf(saveDirPath, sizeof(saveDirPath), "%s/%s", gameHDDPath, m_szSaveUniqueName); + mkdir(saveDirPath, 0755); + + char saveFilePath[512]; + snprintf(saveFilePath, sizeof(saveFilePath), "%s/saveData.ms", saveDirPath); + + int fd = open(saveFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + if (Func) Func(lpParam, false); + return C4JStorage::ESaveGame_Idle; + } + + ssize_t bytesWritten = write(fd, m_pSaveData, m_uiSaveSize); + close(fd); + + SaveTitleFile(saveDirPath); + + if (m_pbThumbnail && m_dwThumbnailBytes > 0) + { + char thumbPath[512]; + snprintf(thumbPath, sizeof(thumbPath), "%s/saveThumbnail.png", saveDirPath); + + int tfd = open(thumbPath, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (tfd >= 0) + { + write(tfd, m_pbThumbnail, m_dwThumbnailBytes); + close(tfd); + } + + free(m_pbThumbnail); + m_pbThumbnail = nullptr; + m_dwThumbnailBytes = 0; + } + + bool success = (bytesWritten == (ssize_t)m_uiSaveSize); +#else + char saveDirPath[512]; + sprintf_s(saveDirPath, sizeof(saveDirPath), "%s\\%s", gameHDDPath, m_szSaveUniqueName); + CreateDirectoryA(saveDirPath, 0); + + char saveFilePath[512]; + sprintf_s(saveFilePath, sizeof(saveFilePath), "%s\\saveData.ms", saveDirPath); + + HANDLE h = CreateFileA(saveFilePath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (h == INVALID_HANDLE_VALUE) + { + if (Func) Func(lpParam, false); + return C4JStorage::ESaveGame_Idle; + } + + DWORD bytesWritten = 0; + BOOL res = WriteFile(h, m_pSaveData, m_uiSaveSize, &bytesWritten, 0); + CloseHandle(h); + + SaveTitleFile(saveDirPath); + + if (m_pbThumbnail && m_dwThumbnailBytes > 0) + { + char thumbPath[512]; + sprintf_s(thumbPath, sizeof(thumbPath), "%s\\saveThumbnail.png", saveDirPath); + + HANDLE hThumb = CreateFileA(thumbPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (hThumb != INVALID_HANDLE_VALUE) + { + DWORD thumbWritten = 0; + WriteFile(hThumb, m_pbThumbnail, m_dwThumbnailBytes, &thumbWritten, 0); + CloseHandle(hThumb); + } + + free(m_pbThumbnail); + m_pbThumbnail = nullptr; + m_dwThumbnailBytes = 0; + } + + bool success = (res && bytesWritten == m_uiSaveSize); +#endif + + if (Func) Func(lpParam, success); + + return C4JStorage::ESaveGame_Idle; +} + +C4JStorage::ESaveGameState CSaveGame::DeleteSaveData(PSAVE_INFO pSaveInfo, int (*Func)(LPVOID lpParam, const bool), LPVOID lpParam) +{ + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + +#ifdef __linux__ + char saveDirPath[512]; + snprintf(saveDirPath, sizeof(saveDirPath), "%s/%s", gameHDDPath, pSaveInfo->UTF8SaveFilename); + + DIR *dir = opendir(saveDirPath); + if (dir) + { + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + char filePath[512]; + snprintf(filePath, sizeof(filePath), "%s/%s", saveDirPath, entry->d_name); + unlink(filePath); + } + closedir(dir); + } + + rmdir(saveDirPath); + + struct stat st; + bool success = (stat(saveDirPath, &st) != 0); +#else + char saveDirPath[512]; + sprintf_s(saveDirPath, sizeof(saveDirPath), "%s\\%s", gameHDDPath, pSaveInfo->UTF8SaveFilename); + + char searchPattern[512]; + sprintf_s(searchPattern, sizeof(searchPattern), "%s\\*", saveDirPath); + + WIN32_FIND_DATAA findData; + HANDLE hFind = FindFirstFileA(searchPattern, &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + char filePath[512]; + sprintf_s(filePath, sizeof(filePath), "%s\\%s", saveDirPath, findData.cFileName); + DeleteFileA(filePath); + } + } while (FindNextFileA(hFind, &findData)); + FindClose(hFind); + } + + RemoveDirectoryA(saveDirPath); + + bool success = (GetFileAttributesA(saveDirPath) == INVALID_FILE_ATTRIBUTES); +#endif + + if (Func) Func(lpParam, success); + + return C4JStorage::ESaveGame_Idle; +} + +C4JStorage::ESaveGameState CSaveGame::DoesSaveExist(bool *pbExists) +{ + if (m_szSaveUniqueName[0] == '\0') + { + *pbExists = false; + return C4JStorage::ESaveGame_Idle; + } + + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + + char saveFilePath[512]; +#ifdef __linux__ + snprintf(saveFilePath, sizeof(saveFilePath), "%s/%s/saveData.ms", gameHDDPath, m_szSaveUniqueName); + struct stat st; + *pbExists = (stat(saveFilePath, &st) == 0); +#else + sprintf_s(saveFilePath, sizeof(saveFilePath), "%s\\%s\\saveData.ms", gameHDDPath, m_szSaveUniqueName); + *pbExists = (GetFileAttributesA(saveFilePath) != INVALID_FILE_ATTRIBUTES); +#endif + return C4JStorage::ESaveGame_Idle; +} + +void CSaveGame::CopySaveDataToNewSave(PBYTE pbThumbnail, DWORD cbThumbnail, WCHAR *wchNewName, int (*Func)(LPVOID lpParam, bool), LPVOID lpParam) +{ + char oldUniqueName[32]; + strcpy_s(oldUniqueName, m_szSaveUniqueName); + + CreateSaveUniqueName(); + + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + +#ifdef __linux__ + char newSaveDirPath[512]; + snprintf(newSaveDirPath, sizeof(newSaveDirPath), "%s/%s", gameHDDPath, m_szSaveUniqueName); + mkdir(newSaveDirPath, 0755); + + char oldSaveFile[512], newSaveFile[512]; + snprintf(oldSaveFile, sizeof(oldSaveFile), "%s/%s/saveData.ms", gameHDDPath, oldUniqueName); + snprintf(newSaveFile, sizeof(newSaveFile), "%s/saveData.ms", newSaveDirPath); + + { + int sfd = open(oldSaveFile, O_RDONLY); + int dfd = open(newSaveFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (sfd >= 0 && dfd >= 0) + { + char buf[8192]; + ssize_t n; + while ((n = read(sfd, buf, sizeof(buf))) > 0) write(dfd, buf, n); + } + if (sfd >= 0) close(sfd); + if (dfd >= 0) close(dfd); + } + + char oldThumbFile[512], newThumbFile[512]; + snprintf(oldThumbFile, sizeof(oldThumbFile), "%s/%s/saveThumbnail.png", gameHDDPath, oldUniqueName); + snprintf(newThumbFile, sizeof(newThumbFile), "%s/saveThumbnail.png", newSaveDirPath); + + { + int sfd = open(oldThumbFile, O_RDONLY); + int dfd = open(newThumbFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (sfd >= 0 && dfd >= 0) + { + char buf[8192]; + ssize_t n; + while ((n = read(sfd, buf, sizeof(buf))) > 0) write(dfd, buf, n); + } + if (sfd >= 0) close(sfd); + if (dfd >= 0) close(dfd); + } + + if (pbThumbnail && cbThumbnail > 0) + { + int tfd = open(newThumbFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (tfd >= 0) + { + write(tfd, pbThumbnail, cbThumbnail); + close(tfd); + } + } + + if (wchNewName) + { + wcscpy_s(m_wszSaveTitle, MAX_DISPLAYNAME_LENGTH, wchNewName); + } + SaveTitleFile(newSaveDirPath); + + struct stat st; + bool success = (stat(newSaveFile, &st) == 0); +#else + char newSaveDirPath[512]; + sprintf_s(newSaveDirPath, sizeof(newSaveDirPath), "%s\\%s", gameHDDPath, m_szSaveUniqueName); + CreateDirectoryA(newSaveDirPath, 0); + + char oldSaveFile[512], newSaveFile[512]; + sprintf_s(oldSaveFile, sizeof(oldSaveFile), "%s\\%s\\saveData.ms", gameHDDPath, oldUniqueName); + sprintf_s(newSaveFile, sizeof(newSaveFile), "%s\\saveData.ms", newSaveDirPath); + CopyFileA(oldSaveFile, newSaveFile, FALSE); + + char oldThumbFile[512], newThumbFile[512]; + sprintf_s(oldThumbFile, sizeof(oldThumbFile), "%s\\%s\\saveThumbnail.png", gameHDDPath, oldUniqueName); + sprintf_s(newThumbFile, sizeof(newThumbFile), "%s\\saveThumbnail.png", newSaveDirPath); + CopyFileA(oldThumbFile, newThumbFile, FALSE); + + if (pbThumbnail && cbThumbnail > 0) + { + HANDLE hThumb = CreateFileA(newThumbFile, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (hThumb != INVALID_HANDLE_VALUE) + { + DWORD thumbWritten = 0; + WriteFile(hThumb, pbThumbnail, cbThumbnail, &thumbWritten, 0); + CloseHandle(hThumb); + } + } + + if (wchNewName) + { + wcscpy_s(m_wszSaveTitle, MAX_DISPLAYNAME_LENGTH, wchNewName); + } + SaveTitleFile(newSaveDirPath); + + bool success = (GetFileAttributesA(newSaveFile) != INVALID_FILE_ATTRIBUTES); +#endif + if (Func) Func(lpParam, success); +} + +void CSaveGame::SetSaveUniqueFilename(char *szFilename) +{ + strcpy_s(m_szSaveUniqueName, szFilename); +} + +void CSaveGame::CreateSaveUniqueName(void) +{ +#ifdef __linux__ + time_t now = time(NULL); + struct tm t; + gmtime_r(&now, &t); + snprintf(m_szSaveUniqueName, sizeof(m_szSaveUniqueName), "%4d%02d%02d%02d%02d%02d", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); +#else + _SYSTEMTIME UTCSysTime; + GetSystemTime(&UTCSysTime); + + sprintf_s(m_szSaveUniqueName, sizeof(m_szSaveUniqueName), "%4d%02d%02d%02d%02d%02d", UTCSysTime.wYear, UTCSysTime.wMonth, UTCSysTime.wDay, + UTCSysTime.wHour, UTCSysTime.wMinute, UTCSysTime.wSecond); +#endif +} + +void CSaveGame::SaveTitleFile(const char *saveDirPath) +{ + if (m_wszSaveTitle[0] == L'\0') + return; + + char titleFilePath[512]; +#ifdef __linux__ + snprintf(titleFilePath, sizeof(titleFilePath), "%s/saveTitle.txt", saveDirPath); + + char utf8Title[MAX_DISPLAYNAME_LENGTH * 3]; + int len = (int)wcstombs(utf8Title, m_wszSaveTitle, sizeof(utf8Title)); + + if (len > 0) + { + int fd = open(titleFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) + { + write(fd, utf8Title, len); + close(fd); + } + } +#else + sprintf_s(titleFilePath, sizeof(titleFilePath), "%s\\saveTitle.txt", saveDirPath); + + char utf8Title[MAX_DISPLAYNAME_LENGTH * 3]; + int len = WideCharToMultiByte(CP_UTF8, 0, m_wszSaveTitle, -1, utf8Title, sizeof(utf8Title), NULL, NULL); + + if (len > 0) + { + HANDLE h = CreateFileA(titleFilePath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (h != INVALID_HANDLE_VALUE) + { + DWORD bytesWritten = 0; + WriteFile(h, utf8Title, len - 1, &bytesWritten, 0); + CloseHandle(h); + } + } +#endif +} + +bool CSaveGame::LoadTitleFromFile(const char *saveDirPath, char *outUTF8Title, int maxLen) +{ + char titleFilePath[512]; +#ifdef __linux__ + snprintf(titleFilePath, sizeof(titleFilePath), "%s/saveTitle.txt", saveDirPath); + + int fd = open(titleFilePath, O_RDONLY); + if (fd < 0) + return false; + + struct stat st; + if (fstat(fd, &st) != 0 || st.st_size == 0 || st.st_size >= maxLen) + { + close(fd); + return false; + } + + ssize_t bytesRead = read(fd, outUTF8Title, st.st_size); + close(fd); + + if (bytesRead > 0) + { + outUTF8Title[bytesRead] = '\0'; + return true; + } + return false; +#else + sprintf_s(titleFilePath, sizeof(titleFilePath), "%s\\saveTitle.txt", saveDirPath); + + HANDLE h = CreateFileA(titleFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (h == INVALID_HANDLE_VALUE) + return false; + + DWORD fileSize = GetFileSize(h, NULL); + if (fileSize == 0 || fileSize == INVALID_FILE_SIZE || fileSize >= (DWORD)maxLen) + { + CloseHandle(h); + return false; + } + + DWORD bytesRead = 0; + ReadFile(h, outUTF8Title, fileSize, &bytesRead, 0); + CloseHandle(h); + + if (bytesRead > 0) + { + outUTF8Title[bytesRead] = '\0'; + return true; + } + return false; +#endif +} + +void CSaveGame::SetDefaultImages(PBYTE pbOptionsImage, DWORD dwOptionsImageBytes, PBYTE pbSaveImage, DWORD dwSaveImageBytes, PBYTE pbSaveThumbnail, DWORD dwSaveThumbnailBytes) +{ + if (m_pbDefaultSaveImage) + { + free(m_pbDefaultSaveImage); + m_pbDefaultSaveImage = nullptr; + m_dwDefaultSaveImageBytes = 0; + } + if (pbSaveImage && dwSaveImageBytes > 0) + { + m_pbDefaultSaveImage = (PBYTE)malloc(dwSaveImageBytes); + if (m_pbDefaultSaveImage) + { + memcpy(m_pbDefaultSaveImage, pbSaveImage, dwSaveImageBytes); + m_dwDefaultSaveImageBytes = dwSaveImageBytes; + } + } + + if (m_pbDefaultThumbnail) + { + free(m_pbDefaultThumbnail); + m_pbDefaultThumbnail = nullptr; + m_dwDefaultThumbnailBytes = 0; + } + if (pbSaveThumbnail && dwSaveThumbnailBytes > 0) + { + m_pbDefaultThumbnail = (PBYTE)malloc(dwSaveThumbnailBytes); + if (m_pbDefaultThumbnail) + { + memcpy(m_pbDefaultThumbnail, pbSaveThumbnail, dwSaveThumbnailBytes); + m_dwDefaultThumbnailBytes = dwSaveThumbnailBytes; + } + } +} + +void CSaveGame::GetDefaultSaveImage(PBYTE *ppbSaveImage, DWORD *pdwSaveImageBytes) +{ + if (ppbSaveImage) *ppbSaveImage = m_pbDefaultSaveImage; + if (pdwSaveImageBytes) *pdwSaveImageBytes = m_dwDefaultSaveImageBytes; +} + +void CSaveGame::GetDefaultSaveThumbnail(PBYTE *ppbSaveThumbnail, DWORD *pdwSaveThumbnailBytes) +{ + if (ppbSaveThumbnail) *ppbSaveThumbnail = m_pbDefaultThumbnail; + if (pdwSaveThumbnailBytes) *pdwSaveThumbnailBytes = m_dwDefaultThumbnailBytes; +} + +C4JStorage::ESaveGameState CSaveGame::RenameSaveData(int iRenameIndex, uint16_t *pui16NewName, int (*Func)(LPVOID lpParam, const bool), LPVOID lpParam) +{ + bool bSuccess = false; + + if (m_pSaveDetails && iRenameIndex >= 0 && iRenameIndex < m_pSaveDetails->iSaveC && pui16NewName) + { + char gameHDDPath[256]; + GetGameHDDPath(gameHDDPath, sizeof(gameHDDPath)); + + wchar_t newTitle[MAX_DISPLAYNAME_LENGTH]; + int i = 0; + while (i < MAX_DISPLAYNAME_LENGTH - 1 && pui16NewName[i] != 0) + { + newTitle[i] = (wchar_t)pui16NewName[i]; + i++; + } + newTitle[i] = L'\0'; + +#ifdef __linux__ + char saveDirPath[512]; + snprintf(saveDirPath, sizeof(saveDirPath), "%s/%s", gameHDDPath, m_pSaveDetails->SaveInfoA[iRenameIndex].UTF8SaveFilename); + + char titleFilePath[512]; + snprintf(titleFilePath, sizeof(titleFilePath), "%s/saveTitle.txt", saveDirPath); + + char utf8Title[MAX_DISPLAYNAME_LENGTH * 3]; + int len = (int)wcstombs(utf8Title, newTitle, sizeof(utf8Title)); + + if (len > 0) + { + int fd = open(titleFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) + { + write(fd, utf8Title, len); + close(fd); + + wcstombs(m_pSaveDetails->SaveInfoA[iRenameIndex].UTF8SaveTitle, newTitle, MAX_DISPLAYNAME_LENGTH); + bSuccess = true; + } + } +#else + char saveDirPath[512]; + sprintf_s(saveDirPath, sizeof(saveDirPath), "%s\\%s", gameHDDPath, m_pSaveDetails->SaveInfoA[iRenameIndex].UTF8SaveFilename); + + char titleFilePath[512]; + sprintf_s(titleFilePath, sizeof(titleFilePath), "%s\\saveTitle.txt", saveDirPath); + + char utf8Title[MAX_DISPLAYNAME_LENGTH * 3]; + int len = WideCharToMultiByte(CP_UTF8, 0, newTitle, -1, utf8Title, sizeof(utf8Title), NULL, NULL); + + if (len > 0) + { + HANDLE h = CreateFileA(titleFilePath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (h != INVALID_HANDLE_VALUE) + { + DWORD bytesWritten = 0; + WriteFile(h, utf8Title, len - 1, &bytesWritten, 0); + CloseHandle(h); + + WideCharToMultiByte(CP_UTF8, 0, newTitle, -1, m_pSaveDetails->SaveInfoA[iRenameIndex].UTF8SaveTitle, MAX_DISPLAYNAME_LENGTH, NULL, NULL); + bSuccess = true; + } + } +#endif + } + + if (Func) Func(lpParam, bSuccess); + return C4JStorage::ESaveGame_Rename; +} diff --git a/Minecraft.Client/Platform_Libs/Dev/Storage/STO_SaveGame.h b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_SaveGame.h new file mode 100644 index 00000000..66b405ad --- /dev/null +++ b/Minecraft.Client/Platform_Libs/Dev/Storage/STO_SaveGame.h @@ -0,0 +1,81 @@ +#pragma once +/* +MIT License + +Copyright (c) 2026 Patoke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "../../../Windows64/4JLibs/inc/4J_Storage.h" + +class CSaveGame +{ +public: + CSaveGame(); + void SetSaveDisabled(bool bDisable); + bool GetSaveDisabled(void); + + void ResetSaveData(); + C4JStorage::ESaveGameState GetSavesInfo(int iPad, int (*Func)(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, const bool), LPVOID lpParam, + char *pszSavePackName); + PSAVE_DETAILS ReturnSavesInfo(); + void ClearSavesInfo(); + C4JStorage::ESaveGameState LoadSaveDataThumbnail(PSAVE_INFO pSaveInfo, int (*Func)(LPVOID lpParam, PBYTE pbThumbnail, DWORD dwThumbnailBytes), + LPVOID lpParam); + C4JStorage::ESaveGameState LoadSaveData(PSAVE_INFO pSaveInfo, int (*Func)(LPVOID lpParam, const bool, const bool), LPVOID lpParam); + unsigned int GetSaveSize(); + void GetSaveData(void *pvData, unsigned int *puiBytes); + bool GetSaveUniqueNumber(INT *piVal); + bool GetSaveUniqueFilename(char *pszName); + void SetSaveTitle(LPCWSTR pwchDefaultSaveName); + PVOID AllocateSaveData(unsigned int uiBytes); + void SetSaveImages(PBYTE pbThumbnail, DWORD dwThumbnailBytes, PBYTE pbImage, DWORD dwImageBytes, PBYTE pbTextData, DWORD dwTextDataBytes); + C4JStorage::ESaveGameState SaveSaveData(int (*Func)(LPVOID, const bool), LPVOID lpParam); + void CopySaveDataToNewSave(PBYTE pbThumbnail, DWORD cbThumbnail, WCHAR *wchNewName, int (*Func)(LPVOID lpParam, bool), LPVOID lpParam); + C4JStorage::ESaveGameState DeleteSaveData(PSAVE_INFO pSaveInfo, int (*Func)(LPVOID lpParam, const bool), LPVOID lpParam); + C4JStorage::ESaveGameState RenameSaveData(int iRenameIndex, uint16_t *pui16NewName, int (*Func)(LPVOID lpParam, const bool), LPVOID lpParam); + C4JStorage::ESaveGameState DoesSaveExist(bool *pbExists); + void SetSaveUniqueFilename(char *szFilename); + LPCWSTR GetSaveTitle(); + + void CreateSaveUniqueName(void); + void SaveTitleFile(const char *saveDirPath); + static bool LoadTitleFromFile(const char *saveDirPath, char *outUTF8Title, int maxLen); + + void SetDefaultImages(PBYTE pbOptionsImage, DWORD dwOptionsImageBytes, PBYTE pbSaveImage, DWORD dwSaveImageBytes, PBYTE pbSaveThumbnail, DWORD dwSaveThumbnailBytes); + void GetDefaultSaveImage(PBYTE *ppbSaveImage, DWORD *pdwSaveImageBytes); + void GetDefaultSaveThumbnail(PBYTE *ppbSaveThumbnail, DWORD *pdwSaveThumbnailBytes); + + void *m_pSaveData; + unsigned int m_uiSaveSize; + char m_szSaveUniqueName[32]; + wchar_t m_wszSaveTitle[MAX_DISPLAYNAME_LENGTH]; + bool m_bIsSafeDisabled; + bool m_bHasSaveDetails; + SAVE_DETAILS *m_pSaveDetails; + + PBYTE m_pbThumbnail; + DWORD m_dwThumbnailBytes; + + PBYTE m_pbDefaultThumbnail; + DWORD m_dwDefaultThumbnailBytes; + PBYTE m_pbDefaultSaveImage; + DWORD m_dwDefaultSaveImageBytes; +}; diff --git a/Minecraft.Client/Windows64/4JLibs/inc/4J_Storage.h b/Minecraft.Client/Windows64/4JLibs/inc/4J_Storage.h index 006c3c88..2000a6c5 100644 --- a/Minecraft.Client/Windows64/4JLibs/inc/4J_Storage.h +++ b/Minecraft.Client/Windows64/4JLibs/inc/4J_Storage.h @@ -151,6 +151,11 @@ public: EDeleteGame_InProgress, }; + enum ESaveIncompleteType + { + ESaveIncomplete_None = 0, + }; + enum ESGIStatus { @@ -252,6 +257,7 @@ public: void ResetSaveData(); // Call before a new save to clear out stored save file name void SetDefaultSaveNameForKeyboardDisplay(LPCWSTR pwchDefaultSaveName); void SetSaveTitle(LPCWSTR pwchDefaultSaveName); + LPCWSTR GetSaveTitle(); bool GetSaveUniqueNumber(INT *piVal); bool GetSaveUniqueFilename(char *pszName); void SetSaveUniqueFilename(char *szFilename); @@ -262,12 +268,19 @@ public: void GetSaveData(void *pvData,unsigned int *puiBytes); PVOID AllocateSaveData(unsigned int uiBytes); void SetSaveImages( PBYTE pbThumbnail,DWORD dwThumbnailBytes,PBYTE pbImage,DWORD dwImageBytes, PBYTE pbTextData ,DWORD dwTextDataBytes); // Sets the thumbnail & image for the save, optionally setting the metadata in the png + void SetDefaultImages(PBYTE pbOptionsImage, DWORD dwOptionsImageBytes, PBYTE pbSaveImage, DWORD dwSaveImageBytes, PBYTE pbSaveThumbnail, DWORD dwSaveThumbnailBytes); + void GetDefaultSaveImage(PBYTE *ppbSaveImage, DWORD *pdwSaveImageBytes); + void GetDefaultSaveThumbnail(PBYTE *ppbSaveThumbnail, DWORD *pdwSaveThumbnailBytes); C4JStorage::ESaveGameState SaveSaveData(int( *Func)(LPVOID ,const bool),LPVOID lpParam); void CopySaveDataToNewSave(PBYTE pbThumbnail,DWORD cbThumbnail,WCHAR *wchNewName,int ( *Func)(LPVOID lpParam, bool), LPVOID lpParam); void SetSaveDeviceSelected(unsigned int uiPad,bool bSelected); bool GetSaveDeviceSelected(unsigned int iPad); C4JStorage::ESaveGameState DoesSaveExist(bool *pbExists); bool EnoughSpaceForAMinSaveGame(); + void SetMaxSaves(int iMaxC); + void SetIncompleteSaveCallback(void (*Func)(LPVOID, const ESaveIncompleteType, int blocksRequired), LPVOID param); + void ContinueIncompleteOperation(); + C4JStorage::ESaveGameState GetSaveState(); void SetSaveMessageVPosition(float fY); // The 'Saving' message will display at a default position unless changed // Get the info for the saves @@ -282,6 +295,7 @@ public: // Load the save. Need to call GetSaveData once the callback is called C4JStorage::ESaveGameState LoadSaveData(PSAVE_INFO pSaveInfo,int( *Func)(LPVOID lpParam,const bool, const bool), LPVOID lpParam); C4JStorage::ESaveGameState DeleteSaveData(PSAVE_INFO pSaveInfo,int( *Func)(LPVOID lpParam,const bool), LPVOID lpParam); + C4JStorage::ESaveGameState RenameSaveData(int iRenameIndex, uint16_t *pui16NewName, int (*Func)(LPVOID lpParam, const bool), LPVOID lpParam); // DLC void RegisterMarketplaceCountsCallback(int ( *Func)(LPVOID lpParam, C4JStorage::DLC_TMS_DETAILS *, int), LPVOID lpParam ); diff --git a/Minecraft.Client/Xbox/Xbox_App.cpp b/Minecraft.Client/Xbox/Xbox_App.cpp index 4fafb9fd..218aaa72 100644 --- a/Minecraft.Client/Xbox/Xbox_App.cpp +++ b/Minecraft.Client/Xbox/Xbox_App.cpp @@ -255,6 +255,7 @@ WCHAR *CConsoleMinecraftApp::wchSceneA[]= L"xuiscene_ingame_player_options", L"xuiscene_inventory_creative", L"xuiscene_multi_launch_more_options", + L"xuiscene_multi_joinload", L"xuiscene_DLCMain", L"xuiscene_NewUpdateMessage", @@ -1658,7 +1659,7 @@ HRESULT CConsoleMinecraftApp::NavigateToScene(int iPad,EUIScene eScene, void *in // If you're navigating to the multigamejoinload, and the player hasn't seen the updates message yet, display it now // display this message the first 3 times // todo: re-enable if we fix this menu, for now its just blank! - if(false && (eScene==eUIScene_LoadOrJoinMenu) && (bSeenUpdateTextThisSession==false) && ( app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplayUpdateMessage)!=0)) + if(false && ((eScene==eUIScene_LoadOrJoinMenu) || (eScene==eUIScene_LoadCreateJoinMenu)) && (bSeenUpdateTextThisSession==false) && ( app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_DisplayUpdateMessage)!=0)) { eScene=eUIScene_NewUpdateMessage; bSeenUpdateTextThisSession=true; @@ -1738,6 +1739,7 @@ HRESULT CConsoleMinecraftApp::NavigateToScene(int iPad,EUIScene eScene, void *in case eUIScene_Credits: case eUIScene_CreateWorldMenu: case eUIScene_LoadOrJoinMenu: + case eUIScene_LoadCreateJoinMenu: case eUIScene_JoinMenu: case eUIScene_DLCOffersMenu: case eUIScene_DLCMainMenu: diff --git a/Minecraft.Client/cmake/sources/Windows.cmake b/Minecraft.Client/cmake/sources/Windows.cmake index 981acede..5b37211c 100644 --- a/Minecraft.Client/cmake/sources/Windows.cmake +++ b/Minecraft.Client/cmake/sources/Windows.cmake @@ -1,5 +1,12 @@ set(BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Windows64/") +set(_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO + "${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/minecraft.xsb" + "${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/resident.xwb" + "${CMAKE_CURRENT_SOURCE_DIR}/Common/res/audio/streamed.xwb" +) +source_group("Common/res/audio" FILES ${_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO}) + set(_MINECRAFT_CLIENT_WINDOWS_COMMON_AUDIO "${CMAKE_CURRENT_SOURCE_DIR}/Common/Audio/SoundEngine.cpp" ) @@ -17,8 +24,6 @@ set(_MINECRAFT_CLIENT_WINDOWS_COMMON_UI "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIBitmapFont.h" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIController.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIController.h" - "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIUnicodeBitmapFont.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIUnicodeBitmapFont.h" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIGroup.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIGroup.h" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UILayer.cpp" @@ -27,6 +32,8 @@ set(_MINECRAFT_CLIENT_WINDOWS_COMMON_UI "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene.h" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UITTFFont.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UITTFFont.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIUnicodeBitmapFont.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIUnicodeBitmapFont.h" ) source_group("Common/UI" FILES ${_MINECRAFT_CLIENT_WINDOWS_COMMON_UI}) @@ -160,6 +167,8 @@ set(_MINECRAFT_CLIENT_WINDOWS_COMMON_UI_SCENES_FRONTEND_MENU_SCREENS "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene_LaunchMoreOptionsMenu.h" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene_LeaderboardsMenu.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene_LeaderboardsMenu.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene_LoadCreateJoinMenu.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene_LoadCreateJoinMenu.h" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene_LoadMenu.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene_LoadMenu.h" "${CMAKE_CURRENT_SOURCE_DIR}/Common/UI/UIScene_LoadOrJoinMenu.cpp" @@ -337,6 +346,17 @@ set(_MINECRAFT_CLIENT_WINDOWS_SOURCE_FILES ) source_group("Source Files" FILES ${_MINECRAFT_CLIENT_WINDOWS_SOURCE_FILES}) +set(_MINECRAFT_CLIENT_WINDOWS_PLATFORM_LIBS_DEV_STORAGE + "${CMAKE_CURRENT_SOURCE_DIR}/Platform_Libs/Dev/Storage/4J_Storage.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Platform_Libs/Dev/Storage/STO_DLC.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Platform_Libs/Dev/Storage/STO_DLC.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Platform_Libs/Dev/Storage/STO_Main.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Platform_Libs/Dev/Storage/STO_Main.h" + "${CMAKE_CURRENT_SOURCE_DIR}/Platform_Libs/Dev/Storage/STO_SaveGame.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/Platform_Libs/Dev/Storage/STO_SaveGame.h" +) +source_group("Platform_Libs/Dev/Storage" FILES ${_MINECRAFT_CLIENT_WINDOWS_PLATFORM_LIBS_DEV_STORAGE}) + set(_MINECRAFT_CLIENT_WINDOWS_WINDOWS "${CMAKE_CURRENT_SOURCE_DIR}/Xbox/MinecraftWindows.rc" "${CMAKE_CURRENT_SOURCE_DIR}/Xbox/Resource.h" @@ -472,6 +492,7 @@ set(_MINECRAFT_CLIENT_WINDOWS_NET_MINECRAFT_STATS source_group("net/minecraft/stats" FILES ${_MINECRAFT_CLIENT_WINDOWS_NET_MINECRAFT_STATS}) set(MINECRAFT_CLIENT_WINDOWS + ${_MINECRAFT_CLIENT_WINDOWS_COMMON_RES_AUDIO} ${_MINECRAFT_CLIENT_WINDOWS_COMMON_AUDIO} ${_MINECRAFT_CLIENT_WINDOWS_COMMON_NETWORK} ${_MINECRAFT_CLIENT_WINDOWS_COMMON_UI} @@ -489,6 +510,7 @@ set(MINECRAFT_CLIENT_WINDOWS ${_MINECRAFT_CLIENT_WINDOWS_DURANGO_IGGY_INCLUDE} ${_MINECRAFT_CLIENT_WINDOWS_PS3_IGGY_INCLUDE} ${_MINECRAFT_CLIENT_WINDOWS_PS3_PS3EXTRAS} + ${_MINECRAFT_CLIENT_WINDOWS_PLATFORM_LIBS_DEV_STORAGE} ${_MINECRAFT_CLIENT_WINDOWS_SOURCE_FILES} ${_MINECRAFT_CLIENT_WINDOWS_WINDOWS} ${_MINECRAFT_CLIENT_WINDOWS_WINDOWS64} @@ -506,3 +528,6 @@ set(MINECRAFT_CLIENT_WINDOWS ${_MINECRAFT_CLIENT_WINDOWS_XBOX_SENTIENT_DYNAMICCONF} ${_MINECRAFT_CLIENT_WINDOWS_NET_MINECRAFT_STATS} ) + + +