From 4eda6e89fd37a3d5a420aa7e8d9debd99869a2c2 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 22 Mar 2026 12:13:53 -0500 Subject: [PATCH] fix UIStructsmerge Platform/Common/Leaderboards --- .../Leaderboards/LeaderboardInterface.cpp | 97 ++ .../Leaderboards/LeaderboardInterface.h | 40 + .../Leaderboards/SonyLeaderboardManager.cpp | 1122 +++++++++++++++++ .../Leaderboards/SonyLeaderboardManager.h | 145 +++ .../Platform/Common/Leaderboards/base64.cpp | 133 ++ .../Platform/Common/Leaderboards/base64.h | 7 + .../Common/UI/UIScene_LeaderboardsMenu.h | 2 +- .../Platform/Common/UI/UIStructs.h | 80 +- .../Platform/Durango/Network/base64.cpp | 352 ++++-- .../Platform/Durango/Network/base64.h | 35 +- 10 files changed, 1869 insertions(+), 144 deletions(-) create mode 100644 Minecraft.Client/Platform/Common/Leaderboards/LeaderboardInterface.cpp create mode 100644 Minecraft.Client/Platform/Common/Leaderboards/LeaderboardInterface.h create mode 100644 Minecraft.Client/Platform/Common/Leaderboards/SonyLeaderboardManager.cpp create mode 100644 Minecraft.Client/Platform/Common/Leaderboards/SonyLeaderboardManager.h create mode 100644 Minecraft.Client/Platform/Common/Leaderboards/base64.cpp create mode 100644 Minecraft.Client/Platform/Common/Leaderboards/base64.h diff --git a/Minecraft.Client/Platform/Common/Leaderboards/LeaderboardInterface.cpp b/Minecraft.Client/Platform/Common/Leaderboards/LeaderboardInterface.cpp new file mode 100644 index 000000000..94c320420 --- /dev/null +++ b/Minecraft.Client/Platform/Common/Leaderboards/LeaderboardInterface.cpp @@ -0,0 +1,97 @@ +#include "../../stdafx.h" +#include "LeaderboardInterface.h" + +LeaderboardInterface::LeaderboardInterface(LeaderboardManager* man) { + m_manager = man; + m_pending = false; + + m_filter = (LeaderboardManager::EFilterMode)-1; + m_callback = NULL; + m_difficulty = 0; + m_type = LeaderboardManager::eStatsType_UNDEFINED; + m_startIndex = 0; + m_readCount = 0; + + m_manager->OpenSession(); +} + +LeaderboardInterface::~LeaderboardInterface() { + m_manager->CancelOperation(); + m_manager->CloseSession(); +} + +void LeaderboardInterface::ReadStats_Friends( + LeaderboardReadListener* callback, int difficulty, + LeaderboardManager::EStatsType type, PlayerUID myUID, + unsigned int startIndex, unsigned int readCount) { + m_filter = LeaderboardManager::eFM_Friends; + m_pending = true; + + m_callback = callback; + m_difficulty = difficulty; + m_type = type; + m_myUID = myUID; + m_startIndex = startIndex; + m_readCount = readCount; + + tick(); +} + +void LeaderboardInterface::ReadStats_MyScore( + LeaderboardReadListener* callback, int difficulty, + LeaderboardManager::EStatsType type, PlayerUID myUID, + unsigned int readCount) { + m_filter = LeaderboardManager::eFM_MyScore; + m_pending = true; + + m_callback = callback; + m_difficulty = difficulty; + m_type = type; + m_myUID = myUID; + m_readCount = readCount; + + tick(); +} + +void LeaderboardInterface::ReadStats_TopRank( + LeaderboardReadListener* callback, int difficulty, + LeaderboardManager::EStatsType type, unsigned int startIndex, + unsigned int readCount) { + m_filter = LeaderboardManager::eFM_TopRank; + m_pending = true; + + m_callback = callback; + m_difficulty = difficulty; + m_type = type; + m_startIndex = startIndex; + m_readCount = readCount; + + tick(); +} + +void LeaderboardInterface::CancelOperation() { + m_manager->CancelOperation(); + m_pending = false; +} + +void LeaderboardInterface::tick() { + if (m_pending) m_pending = !callManager(); +} + +bool LeaderboardInterface::callManager() { + switch (m_filter) { + case LeaderboardManager::eFM_Friends: + return m_manager->ReadStats_Friends(m_callback, m_difficulty, + m_type, m_myUID, m_startIndex, + m_readCount); + case LeaderboardManager::eFM_MyScore: + return m_manager->ReadStats_MyScore(m_callback, m_difficulty, + m_type, m_myUID, m_readCount); + case LeaderboardManager::eFM_TopRank: + return m_manager->ReadStats_TopRank( + m_callback, m_difficulty, m_type, m_startIndex, m_readCount); + default: + assert(false); + return true; + } +} \ No newline at end of file diff --git a/Minecraft.Client/Platform/Common/Leaderboards/LeaderboardInterface.h b/Minecraft.Client/Platform/Common/Leaderboards/LeaderboardInterface.h new file mode 100644 index 000000000..1f9d49b9d --- /dev/null +++ b/Minecraft.Client/Platform/Common/Leaderboards/LeaderboardInterface.h @@ -0,0 +1,40 @@ +#pragma once + +#include "LeaderboardManager.h" + +// 4J-JEV: Simple interface for handling ReadStat failures. +class LeaderboardInterface { +private: + LeaderboardManager* m_manager; + bool m_pending; + + // Arguments. + LeaderboardManager::EFilterMode m_filter; + LeaderboardReadListener* m_callback; + int m_difficulty; + LeaderboardManager::EStatsType m_type; + PlayerUID m_myUID; + unsigned int m_startIndex; + unsigned int m_readCount; + +public: + LeaderboardInterface(LeaderboardManager* man); + ~LeaderboardInterface(); + + void ReadStats_Friends(LeaderboardReadListener* callback, int difficulty, + LeaderboardManager::EStatsType type, PlayerUID myUID, + unsigned int startIndex, unsigned int readCount); + void ReadStats_MyScore(LeaderboardReadListener* callback, int difficulty, + LeaderboardManager::EStatsType type, PlayerUID myUID, + unsigned int readCount); + void ReadStats_TopRank(LeaderboardReadListener* callback, int difficulty, + LeaderboardManager::EStatsType type, + unsigned int startIndex, unsigned int readCount); + + void CancelOperation(); + + void tick(); + +private: + bool callManager(); +}; \ No newline at end of file diff --git a/Minecraft.Client/Platform/Common/Leaderboards/SonyLeaderboardManager.cpp b/Minecraft.Client/Platform/Common/Leaderboards/SonyLeaderboardManager.cpp new file mode 100644 index 000000000..88a4077d9 --- /dev/null +++ b/Minecraft.Client/Platform/Common/Leaderboards/SonyLeaderboardManager.cpp @@ -0,0 +1,1122 @@ +#include "../../stdafx.h" + +#include +#include +// #include + +#include "SonyLeaderboardManager.h" + +#include "base64.h" + +#include "Common/Consoles_App.h" +#include "Common/Network/Sony/SQRNetworkManager.h" + +#include "../../../../Minecraft.World/Util/StringHelpers.h" + +#ifdef __ORBIS__ +#include "Orbis/OrbisExtras/ShutdownManager.h" +#include "Orbis/Orbis_App.h" +#elif defined __PSVITA__ +#include "PSVita/PSVitaExtras/ShutdownManager.h" +#include "PSVita/PSVita_App.h" +#elif defined __PS3__ +#include "PS3/PS3Extras/ShutdownManager.h" +#include "PS3/PS3_App.h" +#else +#error "SonyLeaderboardManager is included for a non-sony platform." +#endif + +SonyLeaderboardManager::SonyLeaderboardManager() { + m_eStatsState = eStatsState_Idle; + + m_titleContext = -1; + + m_myXUID = INVALID_XUID; + + m_scores = NULL; + + m_statsType = eStatsType_Kills; + m_difficulty = 0; + + m_requestId = 0; + + m_openSessions = 0; + + InitializeCriticalSection(&m_csViewsLock); + + m_running = false; + m_threadScoreboard = NULL; +} + +SonyLeaderboardManager::~SonyLeaderboardManager() { + m_running = false; + + // 4J-JEV: Wait for thread to stop and hope it doesn't take too long. + long long startShutdown = System::currentTimeMillis(); + while (m_threadScoreboard->isRunning()) { + Sleep(1); + assert((System::currentTimeMillis() - startShutdown) < 16); + } + + delete m_threadScoreboard; + + DeleteCriticalSection(&m_csViewsLock); +} + +int SonyLeaderboardManager::scoreboardThreadEntry(LPVOID lpParam) { + ShutdownManager::HasStarted(ShutdownManager::eLeaderboardThread); + SonyLeaderboardManager* self = + reinterpret_cast(lpParam); + + self->m_running = true; + app.DebugPrintf("[SonyLeaderboardManager] Thread started.\n"); + + bool needsWriting = false; + do { + if (self->m_openSessions > 0 || needsWriting) { + self->scoreboardThreadInternal(); + } + + EnterCriticalSection(&self->m_csViewsLock); + needsWriting = self->m_views.size() > 0; + LeaveCriticalSection(&self->m_csViewsLock); + + // 4J Stu - We can't write while we aren't signed in to live + if (!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) { + needsWriting = false; + } + + if ((!needsWriting) && (self->m_eStatsState != eStatsState_Getting)) { + Sleep(50); // 4J-JEV: When we're not reading or writing. + } + + } while ((self->m_running || self->m_eStatsState == eStatsState_Getting || + needsWriting) && + ShutdownManager::ShouldRun(ShutdownManager::eLeaderboardThread)); + + // 4J-JEV, moved this here so setScore can finish up. + self->destroyTitleContext(self->m_titleContext); + + // TODO sceNpScoreTerm(); + app.DebugPrintf("[SonyLeaderboardManager] Thread closed.\n"); + ShutdownManager::HasFinished(ShutdownManager::eLeaderboardThread); + return 0; +} + +void SonyLeaderboardManager::scoreboardThreadInternal() { + // 4J-JEV: Just initialise the context the once now. + if (m_titleContext == -1) { + int primaryPad = ProfileManager.GetPrimaryPad(); + + if (!ProfileManager.IsSignedInLive(primaryPad)) return; + + int ret = initialiseScoreUtility(); + if (ret < 0) { + if (!scoreUtilityAlreadyInitialised(ret)) { + app.DebugPrintf( + "[SonyLeaderboardManager] initialiseScoreUtility() failed. " + "ret = 0x%x\n", + ret); + return; + } else { + app.DebugPrintf( + "[SonyLeaderboardManager] initialiseScoreUtility() already " + "initialised, (0x%x)\n", + ret); + } + } + + SceNpId npId; + ProfileManager.GetSceNpId(primaryPad, &npId); + + ret = createTitleContext(npId); + + if (ret < 0) + return; + else + m_titleContext = ret; + } else + assert(m_titleContext > 0); // Paranoia + + switch (m_eStatsState) { + case eStatsState_Getting: + // Player starts using async multiplayer feature + // 4J-PB - Fix for SCEA FQA #4 - TRC R4064 - Incorrect usage of + // AsyncMultiplay Note 1: The following NP call should be reserved + // for asynchronous multiplayer modes that require PS Plus to be + // accessed. + // + // Note 2: + // The message is not displayed with a user without PlayStation�Plus + // subscription and they are able to access the Leaderboards. + + // NotifyAsyncPlusFeature(); + + switch (m_eFilterMode) { + case eFM_MyScore: + case eFM_Friends: + getScoreByIds(); + break; + case eFM_TopRank: + getScoreByRange(); + break; + } + break; + + case eStatsState_Canceled: + case eStatsState_Failed: + case eStatsState_Ready: + case eStatsState_Idle: + + // 4J-JEV: Moved this here, I don't want reading and + // writing going on at the same time. + // -- + // 4J-JEV: Writing no longer changes the manager state, + // we'll manage the write queue seperately. + + EnterCriticalSection(&m_csViewsLock); + bool hasWork = !m_views.empty(); + LeaveCriticalSection(&m_csViewsLock); + + if (hasWork) { + setScore(); + } + + break; + } +} + +HRESULT SonyLeaderboardManager::fillByIdsQuery(const SceNpId& myNpId, + SceNpId*& npIds, uint32_t& len) { + HRESULT ret; + + // Get queried users. + switch (m_eFilterMode) { + case eFM_Friends: { + // 4J-JEV: Implementation for Orbis & Vita as they a very similar. +#if (defined __ORBIS__) || (defined __PSVITA__) + + sce::Toolkit::NP::Utilities::Future + s_friendList; + ret = getFriendsList(s_friendList); + + if (ret != SCE_TOOLKIT_NP_SUCCESS) { + // Error handling + if (m_eStatsState != eStatsState_Canceled) + m_eStatsState = eStatsState_Failed; + app.DebugPrintf( + "[SonyLeaderboardManager] 'getFriendslist' fail, 0x%x.\n", + ret); + return false; + } else if (s_friendList.hasResult()) { + // 4J-JEV: Friends list doesn't include player, leave space for + // them. + len = s_friendList.get()->size() + 1; + + npIds = new SceNpId[len]; + + int i = 0; + + sce::Toolkit::NP::FriendsList::const_iterator itr; + for (itr = s_friendList.get()->begin(); + itr != s_friendList.get()->end(); itr++) { + npIds[i] = itr->npid; + i++; + } + + npIds[len - 1] = + myNpId; // 4J-JEV: Append player to end of query. + } else { + // 4J-JEV: Something terrible must have happend, + // 'getFriendslist' was supposed to be a synchronous operation. + __debugbreak(); + + // 4J-JEV: We can at least fall-back to just the players score. + len = 1; + npIds = new SceNpId[1]; + + npIds[0] = myNpId; + } + +#elif (defined __PS3__) + // PS3 + + // 4J-JEV: Doesn't include the player (its just their friends). + ret = sceNpBasicGetFriendListEntryCount(&len); + len += 1; + + npIds = new SceNpId[len]; + + for (uint32_t i = 0; i < len - 1; i++) { + ret = sceNpBasicGetFriendListEntry(i, npIds + i); + if (ret < 0) return ret; + } + npIds[len - 1] = myNpId; // 4J-JEV: Append player to end of query. + +#endif + } break; + case eFM_MyScore: { + len = 1; + npIds = new SceNpId[1]; + npIds[0] = myNpId; + } break; + } + + return S_OK; +} + +bool SonyLeaderboardManager::getScoreByIds() { + if (m_eStatsState == eStatsState_Canceled) return false; + + // ---------------------------- + SonyRtcTick last_sort_date; + SceNpScoreRankNumber mTotalRecord; + + SceNpId* npIds = NULL; + + int ret; + uint32_t num = 0; + + SceNpScorePlayerRankData* ptr; + SceNpScoreComment* comments; + // ---------------------------- + + // Check for invalid LManager state. + assert(m_eFilterMode == eFM_Friends || m_eFilterMode == eFM_MyScore); + + SceNpId myNpId; + // 4J-PB - should it be user 0? + if (!ProfileManager.IsSignedInLive(0)) { + app.DebugPrintf( + "[SonyLeaderboardManager] OpenSession() fail: User isn't signed in " + "to PSN\n"); + return false; + } + ProfileManager.GetSceNpId(0, &myNpId); + + ret = fillByIdsQuery(myNpId, npIds, num); +#ifdef __PS3__ + if (ret < 0) goto error2; +#endif + + ptr = new SceNpScorePlayerRankData[num]; + comments = new SceNpScoreComment[num]; + + ZeroMemory(ptr, sizeof(SceNpScorePlayerRankData) * num); + ZeroMemory(comments, sizeof(SceNpScoreComment) * num); + + /* app.DebugPrintf("sceNpScoreGetRankingByNpId(\n\t transaction=%i,\n\t + boardID=0,\n\t npId=%i,\n\t friendCount*sizeof(SceNpId)=%i*%i=%i,\ + rankData=%i,\n\t friendCount*sizeof(SceNpScorePlayerRankData)=%i,\n\t NULL, + 0, NULL, 0,\n\t friendCount=%i,\n...\n", transaction, npId, friendCount, + sizeof(SceNpId), friendCount*sizeof(SceNpId), rankData, + friendCount*sizeof(SceNpScorePlayerRankData), friendCount + ); */ + + int boardId = getBoardId(m_difficulty, m_statsType); + + // 4J-JEV: Orbis can only do with 100 ids max, so we use batches. +#ifdef __ORBIS__ + for (int batch = 0; batch < num; batch += 100) { +#endif + ret = createTransactionContext(m_titleContext); + if (m_eStatsState == eStatsState_Canceled) { + // Cancel operation has been called, abort. + app.DebugPrintf( + "[SonyLeaderboardManager]\tgetScoreByIds() - m_eStatsState == " + "eStatsState_Canceled.\n"); + + destroyTransactionContext(ret); + + if (npIds != NULL) delete[] npIds; + if (ptr != NULL) delete[] ptr; + if (comments != NULL) delete[] comments; + + return false; + } else if (ret < 0) { + // Error occurred creating a transacion, abort. + app.DebugPrintf( + "[SonyLeaderboardManager]\tgetScoreByIds() - createTransaction " + "failed, ret=0x%X\n", + ret); + + m_eStatsState = eStatsState_Failed; + + if (npIds != NULL) delete[] npIds; + if (ptr != NULL) delete[] ptr; + if (comments != NULL) delete[] comments; + + return false; + } else { + // Transaction created successfully, continue. + m_requestId = ret; + } + +#ifdef __ORBIS__ + int tmpNum = std::min(num - batch, (unsigned int)100); + app.DebugPrintf( + "[SonyLeaderboardManager]\t Requesting ids %i-%i of %i.\n", batch, + batch + tmpNum, num); +#else + int tmpNum = num; +#endif + ret = sceNpScoreGetRankingByNpId( + m_requestId, + boardId, // BoardId + +#ifdef __ORBIS__ + batch + npIds, sizeof(SceNpId) * tmpNum, // IN: Player IDs + batch + ptr, + sizeof(SceNpScorePlayerRankData) * tmpNum, // OUT: Rank Data + batch + comments, + sizeof(SceNpScoreComment) * tmpNum, // OUT: Comments +#else + npIds, sizeof(SceNpId) * tmpNum, // IN: Player IDs + ptr, sizeof(SceNpScorePlayerRankData) * tmpNum, // OUT: Rank Data + comments, sizeof(SceNpScoreComment) * tmpNum, // OUT: Comments +#endif + + NULL, 0, // GameData. (unused) + + tmpNum, + + &last_sort_date, &mTotalRecord, + + NULL // Reserved, specify null. + ); + + if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED) { + ret = destroyTransactionContext(m_requestId); + app.DebugPrintf( + "[SonyLeaderboardManager] getScoreByIds(): " + "'sceNpScoreGetRankingByRange' aborted (0x%X).\n", + ret); + + delete[] ptr; + delete[] comments; + delete[] npIds; + + return false; + } else if (ret == + SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND) { + // 4J-JEV: Keep going, other batches might have scores. + } else if (ret < 0) + goto error3; + + // Return. + destroyTransactionContext(m_requestId); + m_requestId = 0; + +#ifdef __ORBIS__ + } +#endif + + m_readCount = num; + + // Filter scorers and construct output structure. + if (m_scores != NULL) delete[] m_scores; + m_scores = new ReadScore[m_readCount]; + convertToOutput(m_readCount, m_scores, ptr, comments); + m_maxRank = m_readCount; + + app.DebugPrintf( + "[SonyLeaderboardManager] getScoreByIds(), Success!\n" + "\t Board %i\n" + "\t %i of %i results have an entry\n" + "1stScore=%i\n", + boardId, m_readCount, num, ptr->rankData.scoreValue); + + // Sort scores + std::sort(m_scores, m_scores + m_readCount, SortByRank); + + delete[] ptr; + delete[] comments; + delete[] npIds; + + m_eStatsState = eStatsState_Ready; + return true; + + // Error. +error3: + if (ret != SCE_NP_COMMUNITY_ERROR_ABORTED) // 0x8002a109 + destroyTransactionContext(m_requestId); + m_requestId = 0; + delete[] ptr; + delete[] comments; +error2: + if (npIds != NULL) delete[] npIds; +error1: + if (m_eStatsState != eStatsState_Canceled) + m_eStatsState = eStatsState_Failed; + app.DebugPrintf( + "[SonyLeaderboardManager] getScoreByIds() FAILED, ret=0x%X\n", ret); + return false; +} + +bool SonyLeaderboardManager::getScoreByRange() { + SonyRtcTick last_sort_date; + SceNpScoreRankNumber mTotalRecord; + + unsigned int num = m_readCount; + SceNpScoreRankData* ptr; + SceNpScoreComment* comments; + + assert(m_eFilterMode == eFM_TopRank); + + int ret = createTransactionContext(m_titleContext); + if (m_eStatsState == eStatsState_Canceled) { + // Cancel operation has been called, abort. + app.DebugPrintf( + "[SonyLeaderboardManager]\tgetScoreByRange() - m_eStatsState == " + "eStatsState_Canceled.\n"); + destroyTransactionContext(ret); + return false; + } else if (ret < 0) { + // Error occurred creating a transaction, abort. + m_eStatsState = eStatsState_Failed; + app.DebugPrintf( + "[SonyLeaderboardManager]\tgetScoreByRange() - createTransaction " + "failed, ret=0x%X\n", + ret); + return false; + } else { + // Transaction created successfully, continue. + m_requestId = ret; + } + + ptr = new SceNpScoreRankData[num]; + comments = new SceNpScoreComment[num]; + + int boardId = getBoardId(m_difficulty, m_statsType); + ret = sceNpScoreGetRankingByRange( + m_requestId, + boardId, // BoardId + + m_startIndex, + + ptr, sizeof(SceNpScoreRankData) * num, // OUT: Rank Data + + comments, sizeof(SceNpScoreComment) * num, // OUT: Comment Data + + NULL, 0, // GameData. + + num, + + &last_sort_date, + &m_maxRank, // 'Total number of players registered in the target + // scoreboard.' + + NULL // Reserved, specify null. + ); + + if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED) { + ret = destroyTransactionContext(m_requestId); + app.DebugPrintf( + "[SonyLeaderboardManager] getScoreByRange(): " + "'sceNpScoreGetRankingByRange' aborted (0x%X).\n", + ret); + + delete[] ptr; + delete[] comments; + + return false; + } else if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND) { + ret = destroyTransactionContext(m_requestId); + app.DebugPrintf( + "[SonyLeaderboardManager] getScoreByRange(): Game ranking not " + "found."); + + delete[] ptr; + delete[] comments; + + m_scores = NULL; + m_readCount = 0; + + m_eStatsState = eStatsState_Ready; + return false; + } else if (ret < 0) + goto error2; + else { + app.DebugPrintf( + "[SonyLeaderboardManager] getScoreByRange(), success, " + "1stScore=%i.\n", + ptr->scoreValue); + } + + // Return. + destroyTransactionContext(m_requestId); + m_requestId = 0; + + // m_stats = ptr; //Maybe: addPadding(num,ptr); + + if (m_scores != NULL) delete[] m_scores; + m_readCount = ret; + m_scores = new ReadScore[m_readCount]; + for (int i = 0; i < m_readCount; i++) { + // memcpy(m_scores+i, ptr+i, sizeof(SceNpScoreRankData)); + initReadScoreStruct(m_scores[i], ptr[i]); + // fromBase32(m_scores+i, comments+i); + fillReadScoreStruct(m_scores[i], comments[i]); + } + + m_eStatsState = eStatsState_Ready; + return true; + + // Error. +error2: + if (ret != SCE_NP_COMMUNITY_ERROR_ABORTED) // 0x8002a109 + destroyTransactionContext(m_requestId); + m_requestId = 0; + + delete[] ptr; + delete[] comments; +error1: + if (m_eStatsState != eStatsState_Canceled) + m_eStatsState = eStatsState_Failed; + app.DebugPrintf( + "[SonyLeaderboardManager]\tgetScoreByRange() failed, ret=0x%X\n", ret); + return false; +} + +bool SonyLeaderboardManager::setScore() { + int ret; + SceNpId npId; + int32_t writeTitleContext = 0; + SceNpScoreRankNumber tmp = 0; + SceNpScoreComment comment; + + // Get next job. + + EnterCriticalSection(&m_csViewsLock); + RegisterScore rscore = m_views.front(); + m_views.pop(); + LeaveCriticalSection(&m_csViewsLock); + + if (ProfileManager.IsGuest(rscore.m_iPad)) { + app.DebugPrintf( + "[SonyLeaderboardManager] setScore(): m_iPad[%i] is guest.\n", + rscore.m_iPad); + return true; + } + + ProfileManager.GetSceNpId(rscore.m_iPad, &npId); + writeTitleContext = createTitleContext(npId); + if (writeTitleContext < 0) { + app.DebugPrintf( + "[SonyLeaderboardManager] setScore(): sceNpScoreCreateTitleCtx " + "FAILED, ret == %X.\n", + ret); + return false; + } + + ret = createTransactionContext(writeTitleContext); + + // Start emptying queue if leaderboards has been closed. + if (ret == SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED) { + EnterCriticalSection(&m_csViewsLock); + m_views.pop(); + LeaveCriticalSection(&m_csViewsLock); + } + + // Error handling. + if (ret < 0) { + app.DebugPrintf( + "[SonyLeaderboardManager] setScore() FAILED, ret=0x%X\n", ret); + destroyTitleContext(writeTitleContext); + return false; + } + m_requestId = ret; + + toBase32(&comment, (void*)&rscore.m_commentData); + + int boardId = + getBoardId(rscore.m_difficulty, rscore.m_commentData.m_statsType); + ret = sceNpScoreRecordScore(m_requestId, // transId, + boardId, // boardId, + rscore.m_score, // IN: new score, + + &comment, // Comments + NULL, // GameInfo + + &tmp, // OUT: current rank, + +#ifndef __PS3__ + NULL, // compareDate +#endif + + NULL // Reserved, specify null. + ); + + if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE) // 0x8002A415 + { + app.DebugPrintf( + "[SonyLeaderboardManager] setScore(), doesn't beat current score, " + "%i.\n", + tmp); + } else if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED) { + goto error1; // 0x8002a109 + } else if (ret < 0) { + goto error2; + } else { + app.DebugPrintf( + "[SonyLeaderboardManager] setScore(), success. boardId=%i, " + "score=%i\n", + boardId, rscore.m_score); + } + + // Return. + destroyTransactionContext(m_requestId); + m_requestId = 0; + // m_eStatsState = eStatsState_Idle; + return true; + + // Error. +error2: + destroyTransactionContext(m_requestId); + m_requestId = 0; + +error1: + app.DebugPrintf("[SonyLeaderboardManager] setScore() FAILED, ret=0x%X\n", + ret); + destroyTitleContext(writeTitleContext); + return false; +} + +void SonyLeaderboardManager::Tick() { + ReadView view; + + switch (m_eStatsState) { + case eStatsState_Ready: { + assert(m_scores != NULL || m_readCount == 0); + + view.m_numQueries = m_readCount; + view.m_queries = m_scores; + + // 4J-JEV: Debugging. + // LeaderboardManager::printStats(view); + + eStatsReturn ret = eStatsReturn_NoResults; + if (view.m_numQueries > 0) ret = eStatsReturn_Success; + + if (m_readListener != NULL) { + app.DebugPrintf( + "[SonyLeaderboardManager] OnStatsReadComplete(%i, %i, _), " + "m_readCount=%i.\n", + ret, m_maxRank, m_readCount); + m_readListener->OnStatsReadComplete(ret, m_maxRank, view); + } + + m_eStatsState = eStatsState_Idle; + + delete[] m_scores; + m_scores = NULL; + } break; + + case eStatsState_Failed: { + view.m_numQueries = 0; + view.m_queries = NULL; + + if (m_readListener != NULL) + m_readListener->OnStatsReadComplete(eStatsReturn_NetworkError, + 0, view); + + m_eStatsState = eStatsState_Idle; + } break; + + case eStatsState_Canceled: { + m_eStatsState = eStatsState_Idle; + } break; + + default: // Getting or Idle. + break; + } +} + +bool SonyLeaderboardManager::OpenSession() { + if (m_openSessions == 0) { + if (m_threadScoreboard == NULL) { + m_threadScoreboard = + new C4JThread(&scoreboardThreadEntry, this, "4JScoreboard"); + m_threadScoreboard->SetProcessor(CPU_CORE_LEADERBOARDS); + m_threadScoreboard->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); + m_threadScoreboard->Run(); + } + + app.DebugPrintf( + "[SonyLeaderboardManager] OpenSession(): Starting sceNpScore " + "utility.\n"); + } else { + app.DebugPrintf( + "[SonyLeaderboardManager] OpenSession(): Another session opened, " + "total=%i\n", + m_openSessions + 1); + } + + m_openSessions++; + return true; +} + +void SonyLeaderboardManager::CloseSession() { + m_openSessions--; + + if (m_openSessions == 0) + app.DebugPrintf( + "[SonyLeaderboardManager] CloseSession(): Quitting sceNpScore " + "utility.\n"); + else + app.DebugPrintf( + "[SonyLeaderboardManager] CloseSession(): %i sessions still " + "open.\n", + m_openSessions); +} + +void SonyLeaderboardManager::DeleteSession() {} + +bool SonyLeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views) { + // Need to cancel read/write operation first. + // if (m_eStatsState != eStatsState_Idle) return false; + + // Write relevant parameters. + // RegisterScore *regScore = reinterpret_cast(views); + + EnterCriticalSection(&m_csViewsLock); + for (int i = 0; i < viewCount; i++) { + app.DebugPrintf( + "[SonyLeaderboardManager] WriteStats(), starting. difficulty=%i, " + "statsType=%i, score=%i\n", + views[i].m_difficulty, views[i].m_commentData.m_statsType, + views[i].m_score); + + m_views.push(views[i]); + } + LeaveCriticalSection(&m_csViewsLock); + + delete[] views; //*regScore; + + // m_eStatsState = eStatsState_Writing; + return true; +} + +// myUID ignored on PS3. +bool SonyLeaderboardManager::ReadStats_Friends( + LeaderboardReadListener* listener, int difficulty, EStatsType type, + PlayerUID myUID, unsigned int startIndex, unsigned int readCount) { + // Need to cancel read/write operation first. + if (m_eStatsState != eStatsState_Idle) return false; + if (!LeaderboardManager::ReadStats_Friends(listener, difficulty, type, + myUID, startIndex, readCount)) + return false; + + m_eStatsState = eStatsState_Getting; + return true; +} + +// myUID ignored on PS3. +bool SonyLeaderboardManager::ReadStats_MyScore( + LeaderboardReadListener* listener, int difficulty, EStatsType type, + PlayerUID myUID, unsigned int readCount) { + // Need to cancel read/write operation first. + if (m_eStatsState != eStatsState_Idle) return false; + if (!LeaderboardManager::ReadStats_MyScore(listener, difficulty, type, + myUID, readCount)) + return false; + + m_eStatsState = eStatsState_Getting; + return true; +} + +// myUID ignored on PS3. +bool SonyLeaderboardManager::ReadStats_TopRank( + LeaderboardReadListener* listener, int difficulty, EStatsType type, + unsigned int startIndex, unsigned int readCount) { + // Need to cancel read/write operation first. + if (m_eStatsState != eStatsState_Idle) return false; + if (!LeaderboardManager::ReadStats_TopRank(listener, difficulty, type, + startIndex, readCount)) + return false; + + m_eStatsState = eStatsState_Getting; + return true; +} + +void SonyLeaderboardManager::FlushStats() {} + +void SonyLeaderboardManager::CancelOperation() { + m_readListener = NULL; + m_eStatsState = eStatsState_Canceled; + + if (m_requestId != 0) { + int ret = abortTransactionContext(m_requestId); + if (ret < 0) + app.DebugPrintf( + "[SonyLeaderboardManager] CancelOperation(): Problem " + "encountered aborting current operation, 0x%X.\n", + ret); + else + app.DebugPrintf( + "[SonyLeaderboardManager] CancelOperation(): Operation aborted " + "successfully.\n"); + } else { + app.DebugPrintf( + "[SonyLeaderboardManager] CancelOperation(): No current " + "operation.\n"); + } +} + +bool SonyLeaderboardManager::isIdle() { + return m_eStatsState == eStatsState_Idle; +} + +int SonyLeaderboardManager::getBoardId(int difficulty, EStatsType statsType) { + switch (statsType) { + case eStatsType_Travelling: + if (0 <= difficulty && difficulty < 4) + return 1 + difficulty; // [1,2,3,4] + else + return -1; + + case eStatsType_Mining: + if (0 <= difficulty && difficulty < 4) + return 5 + difficulty; // [5,6,7,8] + else + return -1; + + case eStatsType_Farming: + if (0 <= difficulty && difficulty < 4) + return 9 + difficulty; // [9,10,11,12] + else + return -1; + + case eStatsType_Kills: + if (1 <= difficulty && difficulty < 4) + return 13 + difficulty - 1; // [13,14,15,16] + else + return -1; + + default: + return -1; + } +} + +// 4J-JEV: Filter out all friends who don't have scores. +/* +SceNpScoreRankData *SonyLeaderboardManager::filterJustScorers(unsigned int &num, +SceNpScorePlayerRankData *friendsData) +{ +int num2 = 0; +for (int i=0; i> (8 - dIndex); + + fivebits = (fivebits >> 3) & 0x1F; + + if (fivebits < 10) // 0 - 9 + chars[i] = '0' + fivebits; + else if (fivebits < 32) // A - V + chars[i] = 'A' + (fivebits - 10); + else + assert(false); + } + + toSymbols(getComment(out)); +} + +void SonyLeaderboardManager::fromBase32(void* out, SceNpScoreComment* in) { + PBYTE bytes = (PBYTE)out; + ZeroMemory(bytes, RECORD_SIZE); + + fromSymbols(getComment(in)); + + char ch[2] = {0, 0}; + for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++) { + ch[0] = getComment(in)[i]; + unsigned char fivebits = strtol(ch, NULL, 32) << 3; + + int sByte = (i * 5) / 8; + int eByte = (5 + (i * 5)) / 8; + int dIndex = (i * 5) % 8; + + *(bytes + sByte) = *(bytes + sByte) | (fivebits >> dIndex); + + if (eByte != sByte) *(bytes + eByte) = fivebits << (8 - dIndex); + } +} + +char symbBase32[32] = {' ', '!', '\"', '#', '$', '%', '&', '\'', '(', ')', '*', + '+', '`', '-', '.', '/', ':', ';', '<', '=', '>', '?', + '[', '\\', ']', '^', '_', '{', '|', '}', '~', '@'}; + +char charBase32[32] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', + 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'}; + +void SonyLeaderboardManager::toSymbols(char* str) { + for (int i = 0; i < 63; i++) { + for (int j = 0; j < 32; j++) { + if (str[i] == charBase32[j]) str[i] = symbBase32[j]; + } + } +} + +void SonyLeaderboardManager::fromSymbols(char* str) { + for (int i = 0; i < 63; i++) { + for (int j = 0; j < 32; j++) { + if (str[i] == symbBase32[j]) str[i] = charBase32[j]; + } + } +} + +bool SonyLeaderboardManager::test_string(std::string testing) { +#ifndef _CONTENT_PACKAGE + static SceNpScoreComment comment; + ZeroMemory(&comment, sizeof(SceNpScoreComment)); + memcpy(&comment, testing.c_str(), SCE_NP_SCORE_COMMENT_MAXLEN); + + int ctx = createTransactionContext(m_titleContext); + if (ctx < 0) return false; + + int ret = sceNpScoreCensorComment(ctx, (const char*)&comment, NULL); + + if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_CENSORED) { + app.DebugPrintf("\n[TEST_STRING]: REJECTED "); + } else if (ret < 0) { + destroyTransactionContext(ctx); + return false; + } else { + app.DebugPrintf("\n[TEST_STRING]: permitted "); + } + + app.DebugPrintf("'%s'\n", getComment(&comment)); + destroyTransactionContext(ctx); + return true; +#else + return true; +#endif +} + +void SonyLeaderboardManager::initReadScoreStruct(ReadScore& out, + SceNpScoreRankData& rankData) { + ZeroMemory(&out, sizeof(ReadScore)); + + // Init rank and onlineID + out.m_uid.setOnlineID(rankData.npId.handle, true); + out.m_rank = rankData.rank; + + // Convert to std::wstring and copy name. + std::wstring wstrName = + convStringToWstring(std::string(rankData.npId.handle.data)).c_str(); + // memcpy(&out.m_name, wstrName.c_str(), XUSER_NAME_SIZE); + out.m_name = wstrName; +} + +void SonyLeaderboardManager::fillReadScoreStruct(ReadScore& out, + SceNpScoreComment& comment) { + StatsData statsData; + fromBase32((void*)&statsData, &comment); + + switch (statsData.m_statsType) { + case eStatsType_Farming: + out.m_statsSize = 6; + out.m_statsData[0] = statsData.m_farming.m_eggs; + out.m_statsData[1] = statsData.m_farming.m_wheat; + out.m_statsData[2] = statsData.m_farming.m_mushroom; + out.m_statsData[3] = statsData.m_farming.m_sugarcane; + out.m_statsData[4] = statsData.m_farming.m_milk; + out.m_statsData[5] = statsData.m_farming.m_pumpkin; + break; + case eStatsType_Mining: + out.m_statsSize = 7; + out.m_statsData[0] = statsData.m_mining.m_dirt; + out.m_statsData[1] = statsData.m_mining.m_cobblestone; + out.m_statsData[2] = statsData.m_mining.m_sand; + out.m_statsData[3] = statsData.m_mining.m_stone; + out.m_statsData[4] = statsData.m_mining.m_gravel; + out.m_statsData[5] = statsData.m_mining.m_clay; + out.m_statsData[6] = statsData.m_mining.m_obsidian; + break; + case eStatsType_Kills: + out.m_statsSize = 7; + out.m_statsData[0] = statsData.m_kills.m_zombie; + out.m_statsData[1] = statsData.m_kills.m_skeleton; + out.m_statsData[2] = statsData.m_kills.m_creeper; + out.m_statsData[3] = statsData.m_kills.m_spider; + out.m_statsData[4] = statsData.m_kills.m_spiderJockey; + out.m_statsData[5] = statsData.m_kills.m_zombiePigman; + out.m_statsData[6] = statsData.m_kills.m_slime; + break; + case eStatsType_Travelling: + out.m_statsSize = 4; + out.m_statsData[0] = statsData.m_travelling.m_walked; + out.m_statsData[1] = statsData.m_travelling.m_fallen; + out.m_statsData[2] = statsData.m_travelling.m_minecart; + out.m_statsData[3] = statsData.m_travelling.m_boat; + break; + } +} + +bool SonyLeaderboardManager::SortByRank(const ReadScore& lhs, + const ReadScore& rhs) { + return lhs.m_rank < rhs.m_rank; +} diff --git a/Minecraft.Client/Platform/Common/Leaderboards/SonyLeaderboardManager.h b/Minecraft.Client/Platform/Common/Leaderboards/SonyLeaderboardManager.h new file mode 100644 index 000000000..79e7ecf3c --- /dev/null +++ b/Minecraft.Client/Platform/Common/Leaderboards/SonyLeaderboardManager.h @@ -0,0 +1,145 @@ +#pragma once + +#include "Common/Leaderboards/LeaderboardManager.h" + +#ifdef __PS3__ +typedef CellRtcTick SonyRtcTick; +#else +typedef SceRtcTick SonyRtcTick; +#endif + +class SonyLeaderboardManager : public LeaderboardManager { +protected: + enum EStatsState { + eStatsState_Idle, + eStatsState_Getting, + eStatsState_Failed, + eStatsState_Ready, + eStatsState_Canceled, + eStatsState_Max + }; + +public: + SonyLeaderboardManager(); + virtual ~SonyLeaderboardManager(); + +protected: + unsigned short m_openSessions; + + C4JThread* m_threadScoreboard; + bool m_running; + + int m_titleContext; + int32_t m_requestId; + + // SceNpId m_myNpId; + + static int scoreboardThreadEntry(LPVOID lpParam); + void scoreboardThreadInternal(); + + virtual bool getScoreByIds(); + virtual bool getScoreByRange(); + + virtual bool setScore(); + + std::queue m_views; + + CRITICAL_SECTION m_csViewsLock; + + EStatsState m_eStatsState; // State of the stats read + // EFilterMode m_eFilterMode; + + ReadScore* m_scores; + unsigned int m_maxRank; + // SceNpScoreRankData *m_stats; + +public: + virtual void Tick(); + + // Open a session + virtual bool OpenSession(); + + // Close a session + virtual void CloseSession(); + + // Delete a session + virtual void DeleteSession(); + + // Write the given stats + // This is called synchronously and will not free any memory allocated for + // views when it is done + + virtual bool WriteStats(unsigned int viewCount, ViewIn views); + + virtual bool ReadStats_Friends(LeaderboardReadListener* callback, + int difficulty, EStatsType type, + PlayerUID myUID, unsigned int startIndex, + unsigned int readCount); + virtual bool ReadStats_MyScore(LeaderboardReadListener* callback, + int difficulty, EStatsType type, + PlayerUID myUID, unsigned int readCount); + virtual bool ReadStats_TopRank(LeaderboardReadListener* callback, + int difficulty, EStatsType type, + unsigned int startIndex, + unsigned int readCount); + + // Perform a flush of the stats + virtual void FlushStats(); + + // Cancel the current operation + virtual void CancelOperation(); + + // Is the leaderboard manager idle. + virtual bool isIdle(); + +protected: + int getBoardId(int difficulty, EStatsType); + + SceNpScorePlayerRankData* addPadding(unsigned int num, + SceNpScoreRankData* rankData); + + void convertToOutput(unsigned int& num, ReadScore* out, + SceNpScorePlayerRankData* rankData, + SceNpScoreComment* comm); + + void toBinary(void* out, SceNpScoreComment* in); + void fromBinary(SceNpScoreComment** out, void* in); + + void toBase32(SceNpScoreComment* out, void* in); + void fromBase32(void* out, SceNpScoreComment* in); + + void toSymbols(char*); + void fromSymbols(char*); + + bool test_string(std::string); + + void initReadScoreStruct(ReadScore& out, SceNpScoreRankData&); + void fillReadScoreStruct(ReadScore& out, SceNpScoreComment& comment); + + static bool SortByRank(const ReadScore& lhs, const ReadScore& rhs); + +protected: + // 4J-JEV: Interface differences: + + // Sce NP score library function redirects. + virtual HRESULT initialiseScoreUtility() { return ERROR_SUCCESS; } + virtual bool scoreUtilityAlreadyInitialised(HRESULT hr) { return false; } + + virtual HRESULT createTitleContext(const SceNpId& npId) = 0; + virtual HRESULT destroyTitleContext(int titleContext) = 0; + + virtual HRESULT createTransactionContext(int titleContext) = 0; + virtual HRESULT abortTransactionContext(int transactionContext) = 0; + virtual HRESULT destroyTransactionContext(int transactionContext) = 0; + + virtual HRESULT fillByIdsQuery(const SceNpId& myNpId, SceNpId*& npIds, + uint32_t& len); + +#if (defined __ORBIS__) || (defined __PSVITA__) + virtual HRESULT getFriendsList( + sce::Toolkit::NP::Utilities::Future& + friendsList) = 0; +#endif + + virtual char* getComment(SceNpScoreComment* comment) = 0; +}; diff --git a/Minecraft.Client/Platform/Common/Leaderboards/base64.cpp b/Minecraft.Client/Platform/Common/Leaderboards/base64.cpp new file mode 100644 index 000000000..6c5a9d986 --- /dev/null +++ b/Minecraft.Client/Platform/Common/Leaderboards/base64.cpp @@ -0,0 +1,133 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 Ren� Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + Ren� Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "../../stdafx.h" + +#include "base64.h" +#include + +static const sstd::td::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +// 4J ADDED, +sstd::td::string base64_encode(sstd::td::string str) { + return base64_encode(reinterpret_cast(str.c_str()), + str.length()); +} + +sstd::td::string base64_encode(unsigned char const* bytes_to_encode, + unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (int ii = 0; (ii < 4); ii++) + ret += base64_chars[char_array_4[ii]]; + i = 0; + } + } + + if (i) { + for (j = i; j < 3; j++) char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = + ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = + ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; + + while ((i++ < 3)) ret += '='; + } + + return ret; +} + +sstd::td::string base64_decode(sstd::td::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && (encoded_string[in_] != '=') && + is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; + in_++; + if (i == 4) { + for (i = 0; i < 4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = + (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) char_array_4[j] = 0; + + for (j = 0; j < 4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = + (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = + ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} \ No newline at end of file diff --git a/Minecraft.Client/Platform/Common/Leaderboards/base64.h b/Minecraft.Client/Platform/Common/Leaderboards/base64.h new file mode 100644 index 000000000..85f7e9935 --- /dev/null +++ b/Minecraft.Client/Platform/Common/Leaderboards/base64.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +std::string base64_encode(std::string str); +std::string base64_encode(unsigned char const*, unsigned int len); +std::string base64_decode(std::string const& s); \ No newline at end of file diff --git a/Minecraft.Client/Platform/Common/UI/UIScene_LeaderboardsMenu.h b/Minecraft.Client/Platform/Common/UI/UIScene_LeaderboardsMenu.h index e5f72ba50..55f98001e 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene_LeaderboardsMenu.h +++ b/Minecraft.Client/Platform/Common/UI/UIScene_LeaderboardsMenu.h @@ -1,7 +1,7 @@ #pragma once #include "UIScene.h" -#include "../Leaderboards/LeaderboardManager.h" +#include "../Leaderboards/LeaderboardInterface.h" class UIScene_LeaderboardsMenu : public UIScene, public LeaderboardReadListener { diff --git a/Minecraft.Client/Platform/Common/UI/UIStructs.h b/Minecraft.Client/Platform/Common/UI/UIStructs.h index b63bfc392..18f7e5d50 100644 --- a/Minecraft.Client/Platform/Common/UI/UIStructs.h +++ b/Minecraft.Client/Platform/Common/UI/UIStructs.h @@ -5,6 +5,7 @@ #include #include "UIEnums.h" +#include "../App_Defines.h" class Container; class Inventory; @@ -253,54 +254,79 @@ typedef struct _JoinMenuInitData { // More Options typedef struct _LaunchMoreOptionsMenuInitData { - bool bOnlineGame = true; - bool bInviteOnly = false; - bool bAllowFriendsOfFriends = true; + bool bOnlineGame; + bool bInviteOnly; + bool bAllowFriendsOfFriends; - bool bGenerateOptions = false; - bool bStructures = false; - bool bFlatWorld = false; - bool bBonusChest = false; + bool bGenerateOptions; + bool bStructures; + bool bFlatWorld; + bool bBonusChest; - bool bPVP = true; - bool bTrust = false; - bool bFireSpreads = true; - bool bTNT = true; + bool bPVP; + bool bTrust; + bool bFireSpreads; + bool bTNT; - bool bHostPrivileges = false; - bool bResetNether = false; + bool bHostPrivileges; + bool bResetNether; - bool bOnlineSettingChangedBySystem = false; + bool bMobGriefing; + bool bKeepInventory; + bool bDoMobSpawning; + bool bDoMobLoot; + bool bDoTileDrops; + bool bNaturalRegeneration; + bool bDoDaylightCycle; - bool bDoMobSpawning = false; - bool bDoDaylightCycle = false; - int iPad = -1; + bool bOnlineSettingChangedBySystem; - std::uint32_t dwTexturePack = 0; + int iPad; - std::wstring seed = L""; - int worldSize = 3; - bool bDisableSaving = false; + DWORD dwTexturePack; + + std::wstring seed; + int worldSize; + bool bDisableSaving; EGameHostOptionWorldSize currentWorldSize; EGameHostOptionWorldSize newWorldSize; + bool newWorldSizeOverwriteEdges; _LaunchMoreOptionsMenuInitData() { - memset(this, 0, sizeof(_LaunchMoreOptionsMenuInitData)); bOnlineGame = true; + bInviteOnly = false; bAllowFriendsOfFriends = true; + bGenerateOptions = false; + bStructures = false; + bFlatWorld = false; + bBonusChest = false; bPVP = true; + bTrust = false; bFireSpreads = true; - worldSize = 3; - seed = L""; - bDisableSaving = false; - newWorldSize = e_worldSize_Unknown; - newWorldSizeOverwriteEdges = false; + bTNT = false; + bHostPrivileges = false; + bResetNether = false; bMobGriefing = true; + bKeepInventory = false; + bDoMobSpawning = false; bDoMobLoot = true; bDoTileDrops = true; bNaturalRegeneration = true; bDoDaylightCycle = true; + bOnlineSettingChangedBySystem = false; + + iPad = 0; + + dwTexturePack = 0; + + worldSize = 3; + seed = L""; + bDisableSaving = false; + + currentWorldSize = e_worldSize_Unknown; + newWorldSize = e_worldSize_Unknown; + newWorldSizeOverwriteEdges = false; } } LaunchMoreOptionsMenuInitData; diff --git a/Minecraft.Client/Platform/Durango/Network/base64.cpp b/Minecraft.Client/Platform/Durango/Network/base64.cpp index 2d4111f3d..ced57d2bf 100644 --- a/Minecraft.Client/Platform/Durango/Network/base64.cpp +++ b/Minecraft.Client/Platform/Durango/Network/base64.cpp @@ -1,7 +1,13 @@ -/* +/* base64.cpp and base64.h - Copyright (C) 2004-2008 Ren� Nyffenegger + base64 encoding and decoding with C++. + More information at + https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp + + Version: 2.rc.09 (release candidate) + + Copyright (C) 2004-2017, 2020-2022 René Nyffenegger This source code is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages @@ -21,136 +27,256 @@ 3. This notice may not be removed or altered from any source distribution. - Ren� Nyffenegger rene.nyffenegger@adp-gmbh.ch + René Nyffenegger rene.nyffenegger@adp-gmbh.ch */ -#include "../../../../Minecraft.World/Platform/stdafx.h" - #include "base64.h" -#include -static const std::wstring base64_chars = - L"ABCDEFGHIJKLMNOPQRSTUVWXYZ" - L"abcdefghijklmnopqrstuvwxyz" - L"0123456789+/"; +#include +#include + // + // Depending on the url parameter in base64_chars, one of + // two sets of base64 characters needs to be chosen. + // They differ in their last two characters. + // +static const char* base64_chars[2] = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/", -static inline bool is_base64(wchar_t c) -{ - return (isalnum(c) || (c == L'+') || (c == L'/')); + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "-_"}; + +static unsigned int pos_of_char(const unsigned char chr) { + // + // Return the position of chr within base64_encode() + // + + if (chr >= 'A' && chr <= 'Z') return chr - 'A'; + else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1; + else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2; + else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters ( + else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_' + else + // + // 2020-10-23: Throw std::exception rather than const char* + //(Pablo Martin-Gomez, https://github.com/Bouska) + // + throw std::runtime_error("Input is not valid base64-encoded data."); } -// 4J changed to use Platform::String -Platform::String^ base64_encode(unsigned char* chars_to_encode, unsigned int in_len) -{ - std::wstring ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; +static std::string insert_linebreaks(std::string str, size_t distance) { + // + // Provided by https://github.com/JomaCorpFX, adapted by me. + // + if (!str.length()) { + return ""; + } - while (in_len--) - { - char_array_3[i++] = *(chars_to_encode++); - if (i == 3) - { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; + size_t pos = distance; - for(int ii = 0; (ii <4) ; ii++) - { - ret += base64_chars[char_array_4[ii]]; - } - i = 0; - } - } - - if (i) - { - for(j = i; j < 3; j++) - { - char_array_3[j] = '\0'; - } - - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for (j = 0; (j < i + 1); j++) - { - ret += base64_chars[char_array_4[j]]; - } - - while((i++ < 3)) - { - ret += L'='; - } - - } - - return ref new Platform::String(ret.c_str()); + while (pos < str.size()) { + str.insert(pos, "\n"); + pos += distance + 1; + } + return str; } -void base64_decode(Platform::String ^encoded_string, unsigned char *output, unsigned int out_len) -{ - int in_len = encoded_string->Length(); - int i = 0; - int j = 0; - int in_ = 0; - unsigned char char_array_4[4]; - unsigned char char_array_3[3]; +template +static std::string encode_with_line_breaks(String s) { + return insert_linebreaks(base64_encode(s, false), line_length); +} - unsigned char *pucOut = output; +template +static std::string encode_pem(String s) { + return encode_with_line_breaks(s); +} - while (in_len-- && ( encoded_string->Data()[in_] != L'=') && is_base64(encoded_string->Data()[in_])) - { - char_array_4[i++] = (unsigned char)(encoded_string->Data()[in_]); - in_++; - if (i ==4) - { - for (i = 0; i <4; i++) - { - char_array_4[i] = (unsigned char )base64_chars.find(char_array_4[i]); - } +template +static std::string encode_mime(String s) { + return encode_with_line_breaks(s); +} - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; +template +static std::string encode(String s, bool url) { + return base64_encode(reinterpret_cast(s.data()), s.length(), url); +} - for (i = 0; (i < 3); i++) - { - *pucOut++ = char_array_3[i]; - if( ( pucOut - output ) >= out_len ) return; - } - i = 0; - } - } +std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len, bool url) { - if(i) - { - for (j = i; j <4; j++) - { - char_array_4[j] = 0; - } + size_t len_encoded = (in_len +2) / 3 * 4; - for (j = 0; j <4; j++) - { - char_array_4[j] = (unsigned char )base64_chars.find(char_array_4[j]); - } + unsigned char trailing_char = url ? '.' : '='; - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + // + // Choose set of base64 characters. They differ + // for the last two positions, depending on the url + // parameter. + // A bool (as is the parameter url) is guaranteed + // to evaluate to either 0 or 1 in C++ therefore, + // the correct character set is chosen by subscripting + // base64_chars with url. + // + const char* base64_chars_ = base64_chars[url]; - for (j = 0; (j < i - 1); j++) - { - *pucOut++ = char_array_3[j]; - if( ( pucOut - output ) >= out_len ) return; - } - } -} \ No newline at end of file + std::string ret; + ret.reserve(len_encoded); + + unsigned int pos = 0; + + while (pos < in_len) { + ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]); + + if (pos+1 < in_len) { + ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]); + + if (pos+2 < in_len) { + ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]); + ret.push_back(base64_chars_[ bytes_to_encode[pos + 2] & 0x3f]); + } + else { + ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]); + ret.push_back(trailing_char); + } + } + else { + + ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]); + ret.push_back(trailing_char); + ret.push_back(trailing_char); + } + + pos += 3; + } + + + return ret; +} + +template +static std::string decode(String const& encoded_string, bool remove_linebreaks) { + // + // decode(…) is templated so that it can be used with String = const std::string& + // or std::string_view (requires at least C++17) + // + + if (encoded_string.empty()) return std::string(); + + if (remove_linebreaks) { + + std::string copy(encoded_string); + + copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end()); + + return base64_decode(copy, false); + } + + size_t length_of_string = encoded_string.length(); + size_t pos = 0; + + // + // The approximate length (bytes) of the decoded string might be one or + // two bytes smaller, depending on the amount of trailing equal signs + // in the encoded string. This approximation is needed to reserve + // enough space in the string to be returned. + // + size_t approx_length_of_decoded_string = length_of_string / 4 * 3; + std::string ret; + ret.reserve(approx_length_of_decoded_string); + + while (pos < length_of_string) { + // + // Iterate over encoded input string in chunks. The size of all + // chunks except the last one is 4 bytes. + // + // The last chunk might be padded with equal signs or dots + // in order to make it 4 bytes in size as well, but this + // is not required as per RFC 2045. + // + // All chunks except the last one produce three output bytes. + // + // The last chunk produces at least one and up to three bytes. + // + + size_t pos_of_char_1 = pos_of_char(encoded_string.at(pos+1) ); + + // + // Emit the first output byte that is produced in each chunk: + // + ret.push_back(static_cast( ( (pos_of_char(encoded_string.at(pos+0)) ) << 2 ) + ( (pos_of_char_1 & 0x30 ) >> 4))); + + if ( ( pos + 2 < length_of_string ) && // Check for data that is not padded with equal signs (which is allowed by RFC 2045) + encoded_string.at(pos+2) != '=' && + encoded_string.at(pos+2) != '.' // accept URL-safe base 64 strings, too, so check for '.' also. + ) + { + // + // Emit a chunk's second byte (which might not be produced in the last chunk). + // + unsigned int pos_of_char_2 = pos_of_char(encoded_string.at(pos+2) ); + ret.push_back(static_cast( (( pos_of_char_1 & 0x0f) << 4) + (( pos_of_char_2 & 0x3c) >> 2))); + + if ( ( pos + 3 < length_of_string ) && + encoded_string.at(pos+3) != '=' && + encoded_string.at(pos+3) != '.' + ) + { + // + // Emit a chunk's third byte (which might not be produced in the last chunk). + // + ret.push_back(static_cast( ( (pos_of_char_2 & 0x03 ) << 6 ) + pos_of_char(encoded_string.at(pos+3)) )); + } + } + + pos += 4; + } + + return ret; +} + +std::string base64_decode(std::string const& s, bool remove_linebreaks) { + return decode(s, remove_linebreaks); +} + +std::string base64_encode(std::string const& s, bool url) { + return encode(s, url); +} + +std::string base64_encode_pem (std::string const& s) { + return encode_pem(s); +} + +std::string base64_encode_mime(std::string const& s) { + return encode_mime(s); +} + +#if __cplusplus >= 201703L +// +// Interface with std::string_view rather than const std::string& +// Requires C++17 +// Provided by Yannic Bonenberger (https://github.com/Yannic) +// + +std::string base64_encode(std::string_view s, bool url) { + return encode(s, url); +} + +std::string base64_encode_pem(std::string_view s) { + return encode_pem(s); +} + +std::string base64_encode_mime(std::string_view s) { + return encode_mime(s); +} + +std::string base64_decode(std::string_view s, bool remove_linebreaks) { + return decode(s, remove_linebreaks); +} + +#endif // __cplusplus >= 201703L \ No newline at end of file diff --git a/Minecraft.Client/Platform/Durango/Network/base64.h b/Minecraft.Client/Platform/Durango/Network/base64.h index 637a08321..052cea5a3 100644 --- a/Minecraft.Client/Platform/Durango/Network/base64.h +++ b/Minecraft.Client/Platform/Durango/Network/base64.h @@ -1,6 +1,35 @@ -#pragma once +// +// base64 encoding and decoding with C++. +// Version: 2.rc.09 (release candidate) +// + +#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A +#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A #include -Platform::String^ base64_encode(unsigned char* chars_to_encode, unsigned int in_len); -void base64_decode(Platform::String ^encoded_string, unsigned char *output, unsigned int out_len); \ No newline at end of file +#if __cplusplus >= 201703L +#include +#endif // __cplusplus >= 201703L + +std::string base64_encode (std::string const& s, bool url = false); +std::string base64_encode_pem (std::string const& s); +std::string base64_encode_mime(std::string const& s); + +std::string base64_decode(std::string const& s, bool remove_linebreaks = false); +std::string base64_encode(unsigned char const*, size_t len, bool url = false); + +#if __cplusplus >= 201703L +// +// Interface with std::string_view rather than const std::string& +// Requires C++17 +// Provided by Yannic Bonenberger (https://github.com/Yannic) +// +std::string base64_encode (std::string_view s, bool url = false); +std::string base64_encode_pem (std::string_view s); +std::string base64_encode_mime(std::string_view s); + +std::string base64_decode(std::string_view s, bool remove_linebreaks = false); +#endif // __cplusplus >= 201703L + +#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ \ No newline at end of file