the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2
3#include <cstdlib>
4#include <np.h>
5//#include <sys/ppu_thread.h>
6
7#include "SonyLeaderboardManager.h"
8
9#include "base64.h"
10
11#include "Common\Consoles_App.h"
12#include "Common\Network\Sony\SQRNetworkManager.h"
13
14#include "..\..\..\Minecraft.World\StringHelpers.h"
15
16
17#ifdef __ORBIS__
18#include "Orbis\OrbisExtras\ShutdownManager.h"
19#include "Orbis\Orbis_App.h"
20#elif defined __PSVITA__
21#include "PSVita\PSVitaExtras\ShutdownManager.h"
22#include "PSVita\PSVita_App.h"
23#elif defined __PS3__
24#include "PS3\PS3Extras\ShutdownManager.h"
25#include "PS3\PS3_App.h"
26#else
27#error "SonyLeaderboardManager is included for a non-sony platform."
28#endif
29
30SonyLeaderboardManager::SonyLeaderboardManager()
31{
32 m_eStatsState = eStatsState_Idle;
33
34 m_titleContext = -1;
35
36 m_myXUID = INVALID_XUID;
37
38 m_scores = NULL;
39
40 m_statsType = eStatsType_Kills;
41 m_difficulty = 0;
42
43 m_requestId = 0;
44
45 m_openSessions = 0;
46
47 InitializeCriticalSection(&m_csViewsLock);
48
49 m_running = false;
50 m_threadScoreboard = NULL;
51}
52
53SonyLeaderboardManager::~SonyLeaderboardManager()
54{
55 m_running = false;
56
57 // 4J-JEV: Wait for thread to stop and hope it doesn't take too long.
58 long long startShutdown = System::currentTimeMillis();
59 while (m_threadScoreboard->isRunning())
60 {
61 Sleep(1);
62 assert( (System::currentTimeMillis() - startShutdown) < 16 );
63 }
64
65 delete m_threadScoreboard;
66
67 DeleteCriticalSection(&m_csViewsLock);
68}
69
70int SonyLeaderboardManager::scoreboardThreadEntry(LPVOID lpParam)
71{
72 ShutdownManager::HasStarted(ShutdownManager::eLeaderboardThread);
73 SonyLeaderboardManager *self = reinterpret_cast<SonyLeaderboardManager *>(lpParam);
74
75 self->m_running = true;
76 app.DebugPrintf("[SonyLeaderboardManager] Thread started.\n");
77
78 bool needsWriting = false;
79 do
80 {
81 if (self->m_openSessions > 0 || needsWriting)
82 {
83 self->scoreboardThreadInternal();
84 }
85
86 EnterCriticalSection(&self->m_csViewsLock);
87 needsWriting = self->m_views.size() > 0;
88 LeaveCriticalSection(&self->m_csViewsLock);
89
90 // 4J Stu - We can't write while we aren't signed in to live
91 if (!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()))
92 {
93 needsWriting = false;
94 }
95
96 if ( (!needsWriting) && (self->m_eStatsState != eStatsState_Getting) )
97 {
98 Sleep(50); // 4J-JEV: When we're not reading or writing.
99 }
100
101 } while ( (self->m_running || self->m_eStatsState == eStatsState_Getting || needsWriting)
102 && ShutdownManager::ShouldRun(ShutdownManager::eLeaderboardThread)
103 );
104
105 // 4J-JEV, moved this here so setScore can finish up.
106 self->destroyTitleContext(self->m_titleContext);
107
108 // TODO sceNpScoreTerm();
109 app.DebugPrintf("[SonyLeaderboardManager] Thread closed.\n");
110 ShutdownManager::HasFinished(ShutdownManager::eLeaderboardThread);
111 return 0;
112}
113
114void SonyLeaderboardManager::scoreboardThreadInternal()
115{
116 // 4J-JEV: Just initialise the context the once now.
117 if (m_titleContext == -1)
118 {
119 int primaryPad = ProfileManager.GetPrimaryPad();
120
121 if (!ProfileManager.IsSignedInLive(primaryPad)) return;
122
123 int ret = initialiseScoreUtility();
124 if (ret < 0)
125 {
126 if ( !scoreUtilityAlreadyInitialised(ret) )
127 {
128 app.DebugPrintf("[SonyLeaderboardManager] initialiseScoreUtility() failed. ret = 0x%x\n", ret);
129 return;
130 }
131 else
132 {
133 app.DebugPrintf("[SonyLeaderboardManager] initialiseScoreUtility() already initialised, (0x%x)\n", ret);
134 }
135 }
136
137 SceNpId npId;
138 ProfileManager.GetSceNpId(primaryPad,&npId);
139
140 ret = createTitleContext(npId);
141
142 if (ret < 0) return;
143 else m_titleContext = ret;
144 }
145 else assert( m_titleContext > 0 ); //Paranoia
146
147
148 switch (m_eStatsState)
149 {
150 case eStatsState_Getting:
151 // Player starts using async multiplayer feature
152 // 4J-PB - Fix for SCEA FQA #4 - TRC R4064 - Incorrect usage of AsyncMultiplay
153 // Note 1:
154 // The following NP call should be reserved for asynchronous multiplayer modes that require PS Plus to be accessed.
155 //
156 // Note 2:
157 // The message is not displayed with a user without PlayStation�Plus subscription and they are able to access the Leaderboards.
158
159 // NotifyAsyncPlusFeature();
160
161 switch(m_eFilterMode)
162 {
163 case eFM_MyScore:
164 case eFM_Friends:
165 getScoreByIds();
166 break;
167 case eFM_TopRank:
168 getScoreByRange();
169 break;
170 }
171 break;
172
173 case eStatsState_Canceled:
174 case eStatsState_Failed:
175 case eStatsState_Ready:
176 case eStatsState_Idle:
177
178 // 4J-JEV: Moved this here, I don't want reading and
179 // writing going on at the same time.
180 // --
181 // 4J-JEV: Writing no longer changes the manager state,
182 // we'll manage the write queue seperately.
183
184 EnterCriticalSection(&m_csViewsLock);
185 bool hasWork = !m_views.empty();
186 LeaveCriticalSection(&m_csViewsLock);
187
188 if (hasWork)
189 {
190 setScore();
191 }
192
193 break;
194 }
195}
196
197HRESULT SonyLeaderboardManager::fillByIdsQuery(const SceNpId &myNpId, SceNpId* &npIds, uint32_t &len)
198{
199 HRESULT ret;
200
201 // Get queried users.
202 switch(m_eFilterMode)
203 {
204 case eFM_Friends:
205 {
206 // 4J-JEV: Implementation for Orbis & Vita as they a very similar.
207#if (defined __ORBIS__) || (defined __PSVITA__)
208
209 sce::Toolkit::NP::Utilities::Future<sce::Toolkit::NP::FriendsList> s_friendList;
210 ret = getFriendsList(s_friendList);
211
212 if(ret != SCE_TOOLKIT_NP_SUCCESS)
213 {
214 // Error handling
215 if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed;
216 app.DebugPrintf("[SonyLeaderboardManager] 'getFriendslist' fail, 0x%x.\n", ret);
217 return false;
218 }
219 else if (s_friendList.hasResult())
220 {
221 // 4J-JEV: Friends list doesn't include player, leave space for them.
222 len = s_friendList.get()->size() + 1;
223
224 npIds = new SceNpId[len];
225
226 int i = 0;
227
228 sce::Toolkit::NP::FriendsList::const_iterator itr;
229 for (itr = s_friendList.get()->begin(); itr != s_friendList.get()->end(); itr++)
230 {
231 npIds[i] = itr->npid;
232 i++;
233 }
234
235 npIds[len-1] = myNpId; // 4J-JEV: Append player to end of query.
236 }
237 else
238 {
239 // 4J-JEV: Something terrible must have happend,
240 // 'getFriendslist' was supposed to be a synchronous operation.
241 __debugbreak();
242
243 // 4J-JEV: We can at least fall-back to just the players score.
244 len = 1;
245 npIds = new SceNpId[1];
246
247 npIds[0] = myNpId;
248 }
249
250#elif (defined __PS3__)
251 // PS3
252
253 // 4J-JEV: Doesn't include the player (its just their friends).
254 ret = sceNpBasicGetFriendListEntryCount(&len);
255 len += 1;
256
257 npIds = new SceNpId[len];
258
259
260 for (uint32_t i = 0; i < len-1; i++)
261 {
262 ret = sceNpBasicGetFriendListEntry(i, npIds+i);
263 if (ret<0) return ret;
264
265 }
266 npIds[len-1] = myNpId; // 4J-JEV: Append player to end of query.
267
268#endif
269 }
270 break;
271 case eFM_MyScore:
272 {
273 len = 1;
274 npIds = new SceNpId[1];
275 npIds[0] = myNpId;
276 }
277 break;
278 }
279
280 return S_OK;
281}
282
283bool SonyLeaderboardManager::getScoreByIds()
284{
285 if (m_eStatsState == eStatsState_Canceled) return false;
286
287 // ----------------------------
288 SonyRtcTick last_sort_date;
289 SceNpScoreRankNumber mTotalRecord;
290
291 SceNpId *npIds = NULL;
292
293 int ret;
294 uint32_t num = 0;
295
296 SceNpScorePlayerRankData *ptr;
297 SceNpScoreComment *comments;
298 // ----------------------------
299
300 // Check for invalid LManager state.
301 assert( m_eFilterMode == eFM_Friends
302 || m_eFilterMode == eFM_MyScore);
303
304 SceNpId myNpId;
305 // 4J-PB - should it be user 0?
306 if(!ProfileManager.IsSignedInLive(0))
307 {
308 app.DebugPrintf("[SonyLeaderboardManager] OpenSession() fail: User isn't signed in to PSN\n");
309 return false;
310 }
311 ProfileManager.GetSceNpId(0,&myNpId);
312
313 ret = fillByIdsQuery(myNpId, npIds, num);
314#ifdef __PS3__
315 if (ret < 0) goto error2;
316#endif
317
318 ptr = new SceNpScorePlayerRankData[num];
319 comments = new SceNpScoreComment[num];
320
321 ZeroMemory(ptr, sizeof(SceNpScorePlayerRankData) * num);
322 ZeroMemory(comments, sizeof(SceNpScoreComment) * num);
323
324 /* app.DebugPrintf("sceNpScoreGetRankingByNpId(\n\t transaction=%i,\n\t boardID=0,\n\t npId=%i,\n\t friendCount*sizeof(SceNpId)=%i*%i=%i,\
325 rankData=%i,\n\t friendCount*sizeof(SceNpScorePlayerRankData)=%i,\n\t NULL, 0, NULL, 0,\n\t friendCount=%i,\n...\n",
326 transaction, npId, friendCount, sizeof(SceNpId), friendCount*sizeof(SceNpId),
327 rankData, friendCount*sizeof(SceNpScorePlayerRankData), friendCount
328 ); */
329
330 int boardId = getBoardId(m_difficulty, m_statsType);
331
332 // 4J-JEV: Orbis can only do with 100 ids max, so we use batches.
333#ifdef __ORBIS__
334 for (int batch=0; batch<num; batch+=100)
335 {
336#endif
337 ret = createTransactionContext(m_titleContext);
338 if (m_eStatsState == eStatsState_Canceled)
339 {
340 // Cancel operation has been called, abort.
341 app.DebugPrintf("[SonyLeaderboardManager]\tgetScoreByIds() - m_eStatsState == eStatsState_Canceled.\n");
342
343 destroyTransactionContext(ret);
344
345 if (npIds != NULL) delete [] npIds;
346 if (ptr != NULL) delete [] ptr;
347 if (comments != NULL) delete [] comments;
348
349 return false;
350 }
351 else if (ret < 0)
352 {
353 // Error occurred creating a transacion, abort.
354 app.DebugPrintf("[SonyLeaderboardManager]\tgetScoreByIds() - createTransaction failed, ret=0x%X\n", ret);
355
356 m_eStatsState = eStatsState_Failed;
357
358 if (npIds != NULL) delete [] npIds;
359 if (ptr != NULL) delete [] ptr;
360 if (comments != NULL) delete [] comments;
361
362 return false;
363 }
364 else
365 {
366 // Transaction created successfully, continue.
367 m_requestId = ret;
368 }
369
370#ifdef __ORBIS__
371 int tmpNum = min( num - batch, (unsigned int) 100 );
372 app.DebugPrintf("[SonyLeaderboardManager]\t Requesting ids %i-%i of %i.\n", batch, batch+tmpNum, num);
373#else
374 int tmpNum = num;
375#endif
376 ret = sceNpScoreGetRankingByNpId(
377 m_requestId,
378 boardId, // BoardId
379
380#ifdef __ORBIS__
381 batch + npIds, sizeof(SceNpId) * tmpNum, //IN: Player IDs
382 batch + ptr, sizeof(SceNpScorePlayerRankData) * tmpNum, //OUT: Rank Data
383 batch + comments, sizeof(SceNpScoreComment) * tmpNum, //OUT: Comments
384#else
385 npIds, sizeof(SceNpId) * tmpNum, //IN: Player IDs
386 ptr, sizeof(SceNpScorePlayerRankData) * tmpNum, //OUT: Rank Data
387 comments, sizeof(SceNpScoreComment) * tmpNum, //OUT: Comments
388#endif
389
390 NULL, 0, // GameData. (unused)
391
392 tmpNum,
393
394 &last_sort_date,
395 &mTotalRecord,
396
397 NULL // Reserved, specify null.
398 );
399
400 if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED)
401 {
402 ret = destroyTransactionContext(m_requestId);
403 app.DebugPrintf("[SonyLeaderboardManager] getScoreByIds(): 'sceNpScoreGetRankingByRange' aborted (0x%X).\n", ret);
404
405 delete [] ptr;
406 delete [] comments;
407 delete [] npIds;
408
409 return false;
410 }
411 else if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND)
412 {
413 // 4J-JEV: Keep going, other batches might have scores.
414 }
415 else if (ret<0) goto error3;
416
417 // Return.
418 destroyTransactionContext(m_requestId);
419 m_requestId = 0;
420
421#ifdef __ORBIS__
422 }
423#endif
424
425 m_readCount = num;
426
427 // Filter scorers and construct output structure.
428 if (m_scores != NULL) delete [] m_scores;
429 m_scores = new ReadScore[m_readCount];
430 convertToOutput(m_readCount, m_scores, ptr, comments);
431 m_maxRank = m_readCount;
432
433 app.DebugPrintf(
434 "[SonyLeaderboardManager] getScoreByIds(), Success!\n"
435 "\t Board %i\n"
436 "\t %i of %i results have an entry\n"
437 "1stScore=%i\n",
438 boardId,
439 m_readCount, num,
440 ptr->rankData.scoreValue
441 );
442
443 // Sort scores
444 std::sort(m_scores, m_scores + m_readCount, SortByRank);
445
446 delete [] ptr;
447 delete [] comments;
448 delete [] npIds;
449
450 m_eStatsState = eStatsState_Ready;
451 return true;
452
453 // Error.
454error3:
455 if (ret!=SCE_NP_COMMUNITY_ERROR_ABORTED) //0x8002a109
456 destroyTransactionContext(m_requestId);
457 m_requestId = 0;
458 delete [] ptr;
459 delete [] comments;
460error2:
461 if (npIds != NULL) delete [] npIds;
462error1:
463 if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed;
464 app.DebugPrintf("[SonyLeaderboardManager] getScoreByIds() FAILED, ret=0x%X\n", ret);
465 return false;
466}
467
468bool SonyLeaderboardManager::getScoreByRange()
469{
470 SonyRtcTick last_sort_date;
471 SceNpScoreRankNumber mTotalRecord;
472
473 unsigned int num = m_readCount;
474 SceNpScoreRankData *ptr;
475 SceNpScoreComment *comments;
476
477 assert(m_eFilterMode == eFM_TopRank);
478
479 int ret = createTransactionContext(m_titleContext);
480 if (m_eStatsState == eStatsState_Canceled)
481 {
482 // Cancel operation has been called, abort.
483 app.DebugPrintf("[SonyLeaderboardManager]\tgetScoreByRange() - m_eStatsState == eStatsState_Canceled.\n");
484 destroyTransactionContext(ret);
485 return false;
486 }
487 else if (ret < 0)
488 {
489 // Error occurred creating a transaction, abort.
490 m_eStatsState = eStatsState_Failed;
491 app.DebugPrintf("[SonyLeaderboardManager]\tgetScoreByRange() - createTransaction failed, ret=0x%X\n", ret);
492 return false;
493 }
494 else
495 {
496 // Transaction created successfully, continue.
497 m_requestId = ret;
498 }
499
500 ptr = new SceNpScoreRankData[num];
501 comments = new SceNpScoreComment[num];
502
503 int boardId = getBoardId(m_difficulty, m_statsType);
504 ret = sceNpScoreGetRankingByRange(
505 m_requestId,
506 boardId, // BoardId
507
508 m_startIndex,
509
510 ptr, sizeof(SceNpScoreRankData) * num, //OUT: Rank Data
511
512 comments, sizeof(SceNpScoreComment) * num, //OUT: Comment Data
513
514 NULL, 0, // GameData.
515
516 num,
517
518 &last_sort_date,
519 &m_maxRank, // 'Total number of players registered in the target scoreboard.'
520
521 NULL // Reserved, specify null.
522 );
523
524 if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED)
525 {
526 ret = destroyTransactionContext(m_requestId);
527 app.DebugPrintf("[SonyLeaderboardManager] getScoreByRange(): 'sceNpScoreGetRankingByRange' aborted (0x%X).\n", ret);
528
529 delete [] ptr;
530 delete [] comments;
531
532 return false;
533 }
534 else if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND)
535 {
536 ret = destroyTransactionContext(m_requestId);
537 app.DebugPrintf("[SonyLeaderboardManager] getScoreByRange(): Game ranking not found.");
538
539 delete [] ptr;
540 delete [] comments;
541
542 m_scores = NULL;
543 m_readCount = 0;
544
545 m_eStatsState = eStatsState_Ready;
546 return false;
547 }
548 else if (ret<0) goto error2;
549 else
550 {
551 app.DebugPrintf("[SonyLeaderboardManager] getScoreByRange(), success, 1stScore=%i.\n", ptr->scoreValue);
552 }
553
554 // Return.
555 destroyTransactionContext(m_requestId);
556 m_requestId = 0;
557
558 //m_stats = ptr; //Maybe: addPadding(num,ptr);
559
560 if (m_scores != NULL) delete [] m_scores;
561 m_readCount = ret;
562 m_scores = new ReadScore[m_readCount];
563 for (int i=0; i<m_readCount; i++)
564 {
565 //memcpy(m_scores+i, ptr+i, sizeof(SceNpScoreRankData));
566 initReadScoreStruct(m_scores[i], ptr[i]);
567 //fromBase32(m_scores+i, comments+i);
568 fillReadScoreStruct(m_scores[i], comments[i]);
569 }
570
571 m_eStatsState = eStatsState_Ready;
572 return true;
573
574 // Error.
575error2:
576 if (ret!=SCE_NP_COMMUNITY_ERROR_ABORTED) //0x8002a109
577 destroyTransactionContext(m_requestId);
578 m_requestId = 0;
579
580 delete [] ptr;
581 delete [] comments;
582error1:
583 if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed;
584 app.DebugPrintf("[SonyLeaderboardManager]\tgetScoreByRange() failed, ret=0x%X\n", ret);
585 return false;
586}
587
588bool SonyLeaderboardManager::setScore()
589{
590 int ret;
591 SceNpId npId;
592 int32_t writeTitleContext = 0;
593 SceNpScoreRankNumber tmp = 0;
594 SceNpScoreComment comment;
595
596 // Get next job.
597
598 EnterCriticalSection(&m_csViewsLock);
599 RegisterScore rscore = m_views.front();
600 m_views.pop();
601 LeaveCriticalSection(&m_csViewsLock);
602
603 if ( ProfileManager.IsGuest(rscore.m_iPad) )
604 {
605 app.DebugPrintf("[SonyLeaderboardManager] setScore(): m_iPad[%i] is guest.\n", rscore.m_iPad);
606 return true;
607 }
608
609 ProfileManager.GetSceNpId(rscore.m_iPad, &npId);
610 writeTitleContext = createTitleContext(npId);
611 if (writeTitleContext < 0)
612 {
613 app.DebugPrintf("[SonyLeaderboardManager] setScore(): sceNpScoreCreateTitleCtx FAILED, ret == %X.\n", ret);
614 return false;
615 }
616
617 ret = createTransactionContext(writeTitleContext);
618
619 // Start emptying queue if leaderboards has been closed.
620 if (ret == SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED)
621 {
622 EnterCriticalSection(&m_csViewsLock);
623 m_views.pop();
624 LeaveCriticalSection(&m_csViewsLock);
625 }
626
627 // Error handling.
628 if (ret<0)
629 {
630 app.DebugPrintf("[SonyLeaderboardManager] setScore() FAILED, ret=0x%X\n", ret);
631 destroyTitleContext(writeTitleContext);
632 return false;
633 }
634 m_requestId = ret;
635
636 toBase32(&comment, (void *) &rscore.m_commentData);
637
638 int boardId = getBoardId(rscore.m_difficulty, rscore.m_commentData.m_statsType);
639 ret = sceNpScoreRecordScore(
640 m_requestId, // transId,
641 boardId, // boardId,
642 rscore.m_score, //IN: new score,
643
644 &comment, // Comments
645 NULL, // GameInfo
646
647 &tmp, //OUT: current rank,
648
649#ifndef __PS3__
650 NULL, //compareDate
651#endif
652
653 NULL // Reserved, specify null.
654 );
655
656 if (ret==SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE) //0x8002A415
657 {
658 app.DebugPrintf("[SonyLeaderboardManager] setScore(), doesn't beat current score, %i.\n", tmp);
659 }
660 else if (ret==SCE_NP_COMMUNITY_ERROR_ABORTED)
661 {
662 goto error1;//0x8002a109
663 }
664 else if (ret<0)
665 {
666 goto error2;
667 }
668 else
669 {
670 app.DebugPrintf("[SonyLeaderboardManager] setScore(), success. boardId=%i, score=%i\n", boardId, rscore.m_score);
671 }
672
673 // Return.
674 destroyTransactionContext(m_requestId);
675 m_requestId = 0;
676 //m_eStatsState = eStatsState_Idle;
677 return true;
678
679 // Error.
680error2:
681 destroyTransactionContext(m_requestId);
682 m_requestId = 0;
683
684error1:
685 app.DebugPrintf("[SonyLeaderboardManager] setScore() FAILED, ret=0x%X\n", ret);
686 destroyTitleContext(writeTitleContext);
687 return false;
688}
689
690void SonyLeaderboardManager::Tick()
691{
692 ReadView view;
693
694 switch( m_eStatsState )
695 {
696 case eStatsState_Ready:
697 {
698 assert(m_scores != NULL || m_readCount == 0);
699
700 view.m_numQueries = m_readCount;
701 view.m_queries = m_scores;
702
703 // 4J-JEV: Debugging.
704 //LeaderboardManager::printStats(view);
705
706 eStatsReturn ret = eStatsReturn_NoResults;
707 if (view.m_numQueries > 0)
708 ret = eStatsReturn_Success;
709
710 if (m_readListener != NULL)
711 {
712 app.DebugPrintf("[SonyLeaderboardManager] OnStatsReadComplete(%i, %i, _), m_readCount=%i.\n", ret, m_maxRank, m_readCount);
713 m_readListener->OnStatsReadComplete(ret, m_maxRank, view);
714 }
715
716 m_eStatsState = eStatsState_Idle;
717
718 delete [] m_scores;
719 m_scores = NULL;
720 }
721 break;
722
723 case eStatsState_Failed:
724 {
725 view.m_numQueries = 0;
726 view.m_queries = NULL;
727
728 if ( m_readListener != NULL )
729 m_readListener->OnStatsReadComplete(eStatsReturn_NetworkError, 0, view);
730
731 m_eStatsState = eStatsState_Idle;
732 }
733 break;
734
735 case eStatsState_Canceled:
736 {
737 m_eStatsState = eStatsState_Idle;
738 }
739 break;
740
741 default: // Getting or Idle.
742 break;
743 }
744}
745
746bool SonyLeaderboardManager::OpenSession()
747{
748 if (m_openSessions == 0)
749 {
750 if (m_threadScoreboard == NULL)
751 {
752 m_threadScoreboard = new C4JThread(&scoreboardThreadEntry, this, "4JScoreboard");
753 m_threadScoreboard->SetProcessor(CPU_CORE_LEADERBOARDS);
754 m_threadScoreboard->SetPriority(THREAD_PRIORITY_BELOW_NORMAL);
755 m_threadScoreboard->Run();
756 }
757
758 app.DebugPrintf("[SonyLeaderboardManager] OpenSession(): Starting sceNpScore utility.\n");
759 }
760 else
761 {
762 app.DebugPrintf("[SonyLeaderboardManager] OpenSession(): Another session opened, total=%i\n", m_openSessions+1);
763 }
764
765 m_openSessions++;
766 return true;
767}
768
769void SonyLeaderboardManager::CloseSession()
770{
771 m_openSessions--;
772
773 if (m_openSessions == 0) app.DebugPrintf("[SonyLeaderboardManager] CloseSession(): Quitting sceNpScore utility.\n");
774 else app.DebugPrintf("[SonyLeaderboardManager] CloseSession(): %i sessions still open.\n", m_openSessions);
775}
776
777void SonyLeaderboardManager::DeleteSession() {}
778
779bool SonyLeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views)
780{
781 // Need to cancel read/write operation first.
782 //if (m_eStatsState != eStatsState_Idle) return false;
783
784 // Write relevant parameters.
785 //RegisterScore *regScore = reinterpret_cast<RegisterScore *>(views);
786
787 EnterCriticalSection(&m_csViewsLock);
788 for (int i=0; i<viewCount; i++)
789 {
790 app.DebugPrintf("[SonyLeaderboardManager] WriteStats(), starting. difficulty=%i, statsType=%i, score=%i\n",
791 views[i].m_difficulty, views[i].m_commentData.m_statsType, views[i].m_score);
792
793 m_views.push(views[i]);
794 }
795 LeaveCriticalSection(&m_csViewsLock);
796
797 delete [] views; //*regScore;
798
799 //m_eStatsState = eStatsState_Writing;
800 return true;
801}
802
803// myUID ignored on PS3.
804bool SonyLeaderboardManager::ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount)
805{
806 // Need to cancel read/write operation first.
807 if (m_eStatsState != eStatsState_Idle) return false;
808 if (!LeaderboardManager::ReadStats_Friends(listener, difficulty, type, myUID, startIndex, readCount)) return false;
809
810 m_eStatsState = eStatsState_Getting;
811 return true;
812}
813
814// myUID ignored on PS3.
815bool SonyLeaderboardManager::ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount)
816{
817 // Need to cancel read/write operation first.
818 if (m_eStatsState != eStatsState_Idle) return false;
819 if (!LeaderboardManager::ReadStats_MyScore(listener, difficulty, type, myUID, readCount)) return false;
820
821 m_eStatsState = eStatsState_Getting;
822 return true;
823}
824
825// myUID ignored on PS3.
826bool SonyLeaderboardManager::ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount)
827{
828 // Need to cancel read/write operation first.
829 if (m_eStatsState != eStatsState_Idle) return false;
830 if (!LeaderboardManager::ReadStats_TopRank(listener, difficulty, type, startIndex, readCount)) return false;
831
832 m_eStatsState = eStatsState_Getting;
833 return true;
834}
835
836void SonyLeaderboardManager::FlushStats() {}
837
838void SonyLeaderboardManager::CancelOperation()
839{
840 m_readListener = NULL;
841 m_eStatsState = eStatsState_Canceled;
842
843 if (m_requestId != 0)
844 {
845 int ret = abortTransactionContext(m_requestId);
846 if (ret < 0) app.DebugPrintf("[SonyLeaderboardManager] CancelOperation(): Problem encountered aborting current operation, 0x%X.\n", ret);
847 else app.DebugPrintf("[SonyLeaderboardManager] CancelOperation(): Operation aborted successfully.\n");
848 }
849 else
850 {
851 app.DebugPrintf("[SonyLeaderboardManager] CancelOperation(): No current operation.\n");
852 }
853}
854
855bool SonyLeaderboardManager::isIdle()
856{
857 return m_eStatsState == eStatsState_Idle;
858}
859
860int SonyLeaderboardManager::getBoardId(int difficulty, EStatsType statsType)
861{
862 switch (statsType)
863 {
864 case eStatsType_Travelling:
865 if (0 <= difficulty && difficulty < 4) return 1 + difficulty; // [1,2,3,4]
866 else return -1;
867
868 case eStatsType_Mining:
869 if (0 <= difficulty && difficulty < 4) return 5 + difficulty; // [5,6,7,8]
870 else return -1;
871
872 case eStatsType_Farming:
873 if (0 <= difficulty && difficulty < 4) return 9 + difficulty; // [9,10,11,12]
874 else return -1;
875
876 case eStatsType_Kills:
877 if (1 <= difficulty && difficulty < 4) return 13 + difficulty - 1; // [13,14,15,16]
878 else return -1;
879
880 default: return -1;
881 }
882}
883
884// 4J-JEV: Filter out all friends who don't have scores.
885/*
886SceNpScoreRankData *SonyLeaderboardManager::filterJustScorers(unsigned int &num, SceNpScorePlayerRankData *friendsData)
887{
888int num2 = 0;
889for (int i=0; i<num; i++) if (friendsData[i].hasData) num2++;
890num = num2; num2 = 0;
891SceNpScoreRankData *out = new SceNpScoreRankData[num];
892for (int i=0; i<num; i++) if (friendsData[i].hasData) out[num2++] = friendsData[i].rankData;
893return out;
894} */
895
896// 4J-JEV: Unused, here if we want to switch LeaderboardManager::ViewOut to 'SceNpScorePlayerRankData'.
897SceNpScorePlayerRankData *SonyLeaderboardManager::addPadding(unsigned int num, SceNpScoreRankData *rankData)
898{
899 SceNpScorePlayerRankData *out = new SceNpScorePlayerRankData[num];
900 for (int i=0; i<num; i++)
901 {
902 out[i].hasData = true;
903 out[i].rankData = rankData[i];
904 }
905 delete [] rankData;
906 return out;
907}
908
909// 4J-JEV: Filter and create output object.
910void SonyLeaderboardManager::convertToOutput(unsigned int &num, ReadScore *out, SceNpScorePlayerRankData *rankData, SceNpScoreComment *comm)
911{
912 int j=0;
913 for (int i=0; i<num; i++)
914 {
915 if (rankData[i].hasData)
916 {
917 initReadScoreStruct( out[j], rankData[i].rankData );
918 fillReadScoreStruct( out[j], comm[i] );
919
920 j++;
921 }
922 }
923
924 num = j;
925}
926
927void SonyLeaderboardManager::toBinary(void *out, SceNpScoreComment *in)
928{
929 string decoded = base64_decode( string((char*)in) );
930 memcpy(out, decoded.c_str(), RECORD_SIZE);
931}
932
933void SonyLeaderboardManager::fromBinary(SceNpScoreComment **out, void *in)
934{
935 *out = (SceNpScoreComment *) new unsigned char[SCE_NP_SCORE_COMMENT_MAXLEN + 1];
936 string encoded = base64_encode((unsigned char const *) in, RECORD_SIZE);
937 memcpy(out, encoded.c_str(), SCE_NP_SCORE_COMMENT_MAXLEN);
938}
939
940void SonyLeaderboardManager::toBase32(SceNpScoreComment *out, void *in)
941{
942 ZeroMemory(out,sizeof(SceNpScoreComment));
943 PBYTE bytes = (PBYTE) in;
944 char *chars = getComment(out);
945
946 for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++)
947 {
948 int sByte = (i*5) / 8;
949 int eByte = (5+(i*5)) / 8;
950 int dIndex = (i*5) % 8;
951
952 unsigned char fivebits = 0;
953
954 fivebits = *(bytes+sByte) << dIndex;
955
956 if (eByte != sByte)
957 fivebits = fivebits | *(bytes+eByte) >> (8-dIndex);
958
959 fivebits = (fivebits>>3) & 0x1F;
960
961 if (fivebits < 10) // 0 - 9
962 chars[i] = '0' + fivebits;
963 else if (fivebits < 32) // A - V
964 chars[i] = 'A' + (fivebits-10);
965 else
966 assert(false);
967 }
968
969 toSymbols( getComment(out) );
970}
971
972void SonyLeaderboardManager::fromBase32(void *out, SceNpScoreComment *in)
973{
974 PBYTE bytes = (PBYTE) out;
975 ZeroMemory(bytes, RECORD_SIZE);
976
977 fromSymbols( getComment(in) );
978
979 char ch[2] = { 0, 0 };
980 for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++)
981 {
982 ch[0] = getComment(in)[i];
983 unsigned char fivebits = strtol(ch, NULL, 32) << 3;
984
985 int sByte = (i*5) / 8;
986 int eByte = (5+(i*5)) / 8;
987 int dIndex = (i*5) % 8;
988
989 *(bytes + sByte) = *(bytes+sByte) | (fivebits >> dIndex);
990
991 if (eByte != sByte)
992 *(bytes + eByte) = fivebits << (8-dIndex);
993 }
994}
995
996char symbBase32[32] = {
997 ' ', '!','\"', '#', '$', '%', '&','\'', '(', ')',
998 '*', '+', '`', '-', '.', '/', ':', ';', '<', '=',
999 '>', '?', '[','\\', ']', '^', '_', '{', '|', '}',
1000 '~', '@'
1001};
1002
1003char charBase32[32] = {
1004 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1005 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1006 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
1007 'U', 'V'
1008};
1009
1010void SonyLeaderboardManager::toSymbols(char *str)
1011{
1012 for (int i = 0; i < 63; i++)
1013 {
1014 for (int j=0; j < 32; j++)
1015 {
1016 if (str[i]==charBase32[j])
1017 str[i] =symbBase32[j];
1018 }
1019 }
1020}
1021
1022void SonyLeaderboardManager::fromSymbols(char *str)
1023{
1024 for (int i = 0; i < 63; i++)
1025 {
1026 for (int j=0; j < 32; j++)
1027 {
1028 if (str[i]==symbBase32[j])
1029 str[i] =charBase32[j];
1030 }
1031 }
1032}
1033
1034bool SonyLeaderboardManager::test_string(string testing)
1035{
1036#ifndef _CONTENT_PACKAGE
1037 static SceNpScoreComment comment;
1038 ZeroMemory(&comment, sizeof(SceNpScoreComment));
1039 memcpy(&comment, testing.c_str(), SCE_NP_SCORE_COMMENT_MAXLEN);
1040
1041 int ctx = createTransactionContext(m_titleContext);
1042 if (ctx<0) return false;
1043
1044 int ret = sceNpScoreCensorComment(ctx, (const char *) &comment, NULL);
1045
1046 if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_CENSORED)
1047 {
1048 app.DebugPrintf("\n[TEST_STRING]: REJECTED ");
1049 }
1050 else if (ret < 0)
1051 {
1052 destroyTransactionContext(ctx);
1053 return false;
1054 }
1055 else
1056 {
1057 app.DebugPrintf("\n[TEST_STRING]: permitted ");
1058 }
1059
1060 app.DebugPrintf("'%s'\n", getComment(&comment));
1061 destroyTransactionContext(ctx);
1062 return true;
1063#else
1064 return true;
1065#endif
1066}
1067
1068void SonyLeaderboardManager::initReadScoreStruct(ReadScore &out, SceNpScoreRankData &rankData)
1069{
1070 ZeroMemory(&out, sizeof(ReadScore));
1071
1072 // Init rank and onlineID
1073 out.m_uid.setOnlineID( rankData.npId.handle, true );
1074 out.m_rank = rankData.rank;
1075
1076 // Convert to wstring and copy name.
1077 wstring wstrName = convStringToWstring( string(rankData.npId.handle.data) ).c_str();
1078 //memcpy(&out.m_name, wstrName.c_str(), XUSER_NAME_SIZE);
1079 out.m_name=wstrName;
1080}
1081
1082void SonyLeaderboardManager::fillReadScoreStruct(ReadScore &out, SceNpScoreComment &comment)
1083{
1084 StatsData statsData;
1085 fromBase32( (void *) &statsData, &comment );
1086
1087 switch (statsData.m_statsType)
1088 {
1089 case eStatsType_Farming:
1090 out.m_statsSize = 6;
1091 out.m_statsData[0] = statsData.m_farming.m_eggs;
1092 out.m_statsData[1] = statsData.m_farming.m_wheat;
1093 out.m_statsData[2] = statsData.m_farming.m_mushroom;
1094 out.m_statsData[3] = statsData.m_farming.m_sugarcane;
1095 out.m_statsData[4] = statsData.m_farming.m_milk;
1096 out.m_statsData[5] = statsData.m_farming.m_pumpkin;
1097 break;
1098 case eStatsType_Mining:
1099 out.m_statsSize = 7;
1100 out.m_statsData[0] = statsData.m_mining.m_dirt;
1101 out.m_statsData[1] = statsData.m_mining.m_cobblestone;
1102 out.m_statsData[2] = statsData.m_mining.m_sand;
1103 out.m_statsData[3] = statsData.m_mining.m_stone;
1104 out.m_statsData[4] = statsData.m_mining.m_gravel;
1105 out.m_statsData[5] = statsData.m_mining.m_clay;
1106 out.m_statsData[6] = statsData.m_mining.m_obsidian;
1107 break;
1108 case eStatsType_Kills:
1109 out.m_statsSize = 7;
1110 out.m_statsData[0] = statsData.m_kills.m_zombie;
1111 out.m_statsData[1] = statsData.m_kills.m_skeleton;
1112 out.m_statsData[2] = statsData.m_kills.m_creeper;
1113 out.m_statsData[3] = statsData.m_kills.m_spider;
1114 out.m_statsData[4] = statsData.m_kills.m_spiderJockey;
1115 out.m_statsData[5] = statsData.m_kills.m_zombiePigman;
1116 out.m_statsData[6] = statsData.m_kills.m_slime;
1117 break;
1118 case eStatsType_Travelling:
1119 out.m_statsSize = 4;
1120 out.m_statsData[0] = statsData.m_travelling.m_walked;
1121 out.m_statsData[1] = statsData.m_travelling.m_fallen;
1122 out.m_statsData[2] = statsData.m_travelling.m_minecart;
1123 out.m_statsData[3] = statsData.m_travelling.m_boat;
1124 break;
1125 }
1126}
1127
1128bool SonyLeaderboardManager::SortByRank(const ReadScore &lhs, const ReadScore &rhs)
1129{
1130 return lhs.m_rank < rhs.m_rank;
1131}