2020-12-05 13:08:24 +01:00
# include "stdafx.h"
2020-10-30 21:26:22 +01:00
# include "Emu/System.h"
2023-04-26 19:21:26 +02:00
# include "Emu/system_config.h"
2020-02-15 23:36:20 +01:00
# include "Emu/VFS.h"
2016-03-21 20:42:14 +01:00
# include "Emu/IdManager.h"
# include "Emu/Cell/PPUModule.h"
2025-02-11 03:00:37 +01:00
# include "Emu/Cell/timers.hpp"
2022-05-05 13:35:37 +02:00
# include "Emu/Cell/Modules/cellMsgDialog.h"
2014-03-17 20:34:19 +01:00
2014-08-23 22:40:04 +02:00
# include "Utilities/rXml.h"
2014-02-16 02:51:04 +01:00
# include "Loader/TRP.h"
2014-03-20 19:23:14 +01:00
# include "Loader/TROPUSR.h"
2016-03-21 20:42:14 +01:00
2014-08-23 22:40:04 +02:00
# include "sceNp.h"
# include "sceNpTrophy.h"
2018-12-23 00:16:03 +01:00
# include "cellSysutil.h"
2014-04-01 02:33:55 +02:00
2016-04-27 00:27:24 +02:00
# include "Utilities/StrUtil.h"
2019-01-02 23:35:39 +01:00
# include "Emu/Cell/lv2/sys_event.h"
2021-03-12 07:33:38 +01:00
# include "Emu/Cell/lv2/sys_fs.h"
2019-01-02 23:35:39 +01:00
2024-11-11 20:54:44 +01:00
# include <algorithm>
# include <functional>
2021-01-12 17:14:51 +01:00
# include <shared_mutex>
2020-12-18 15:43:34 +01:00
# include "util/asm.hpp"
2019-12-17 23:43:00 +01:00
2018-08-25 14:39:00 +02:00
LOG_CHANNEL ( sceNpTrophy ) ;
2014-02-16 02:51:04 +01:00
2017-10-24 17:43:05 +02:00
TrophyNotificationBase : : ~ TrophyNotificationBase ( )
{
}
2015-07-02 03:54:36 +02:00
struct trophy_context_t
2014-02-16 02:51:04 +01:00
{
2017-01-25 18:50:30 +01:00
static const u32 id_base = 1 ;
static const u32 id_step = 1 ;
2019-12-17 20:55:16 +01:00
static const u32 id_count = 4 ;
2022-07-04 15:02:17 +02:00
SAVESTATE_INIT_POS ( 42 ) ;
2017-01-25 18:50:30 +01:00
2014-02-16 02:51:04 +01:00
std : : string trp_name ;
2014-04-18 13:28:27 +02:00
std : : unique_ptr < TROPUSRLoader > tropusr ;
2022-05-05 18:47:41 +02:00
bool read_only = false ;
2022-07-04 15:02:17 +02:00
trophy_context_t ( ) = default ;
trophy_context_t ( utils : : serial & ar )
2023-11-15 20:07:42 +01:00
: trp_name ( ar . pop < std : : string > ( ) )
2022-07-04 15:02:17 +02:00
{
std : : string trophy_path = vfs : : get ( Emu . GetDir ( ) + " TROPDIR/ " + trp_name + " /TROPHY.TRP " ) ;
fs : : file trp_stream ( trophy_path ) ;
if ( ! trp_stream )
{
// Fallback
trophy_path = vfs : : get ( " /dev_bdvd/PS3_GAME/TROPDIR/ " + trp_name + " /TROPHY.TRP " ) ;
trp_stream . open ( trophy_path ) ;
}
2023-11-15 20:07:42 +01:00
if ( ! ar . pop < bool > ( ) )
2022-07-04 15:02:17 +02:00
{
ar ( read_only ) ;
return ;
}
ar ( read_only ) ;
if ( ! trp_stream & & g_cfg . savestate . state_inspection_mode )
{
return ;
}
const std : : string trophyPath = " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + trp_name ;
tropusr = std : : make_unique < TROPUSRLoader > ( ) ;
const std : : string trophyUsrPath = trophyPath + " /TROPUSR.DAT " ;
const std : : string trophyConfPath = trophyPath + " /TROPCONF.SFM " ;
ensure ( tropusr - > Load ( trophyUsrPath , trophyConfPath ) . success ) ;
}
void save ( utils : : serial & ar )
{
ar ( trp_name , tropusr . operator bool ( ) , read_only ) ;
}
2015-07-02 03:54:36 +02:00
} ;
2014-04-15 16:12:15 +02:00
2015-07-02 03:54:36 +02:00
struct trophy_handle_t
{
2017-01-25 18:50:30 +01:00
static const u32 id_base = 1 ;
static const u32 id_step = 1 ;
2019-12-17 20:55:16 +01:00
static const u32 id_count = 4 ;
2022-07-04 15:02:17 +02:00
SAVESTATE_INIT_POS ( 43 ) ;
2019-12-17 20:55:16 +01:00
bool is_aborted = false ;
2022-07-04 15:02:17 +02:00
trophy_handle_t ( ) = default ;
trophy_handle_t ( utils : : serial & ar )
: is_aborted ( ar )
{
}
void save ( utils : : serial & ar )
{
ar ( is_aborted ) ;
}
2019-12-17 20:55:16 +01:00
} ;
struct sce_np_trophy_manager
{
shared_mutex mtx ;
2020-12-06 13:15:19 +01:00
atomic_t < bool > is_initialized = false ;
2019-12-17 20:55:16 +01:00
// Get context + check handle given
2022-05-05 18:47:41 +02:00
static std : : pair < trophy_context_t * , SceNpTrophyError > get_context_ex ( u32 context , u32 handle , bool test_writeable = false )
2019-12-17 20:55:16 +01:00
{
decltype ( get_context_ex ( 0 , 0 ) ) res { } ;
auto & [ ctxt , error ] = res ;
if ( context < trophy_context_t : : id_base | |
context > = trophy_context_t : : id_base + trophy_context_t : : id_count )
{
// Id was not in range of valid ids
error = SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
return res ;
}
2024-12-22 19:59:48 +01:00
ctxt = idm : : check_unlocked < trophy_context_t > ( context ) ;
2019-12-17 20:55:16 +01:00
if ( ! ctxt )
{
error = SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT ;
return res ;
}
2022-05-05 18:47:41 +02:00
if ( test_writeable & & ctxt - > read_only )
{
error = SCE_NP_TROPHY_ERROR_INVALID_CONTEXT ;
return res ;
}
2019-12-17 20:55:16 +01:00
if ( handle < trophy_handle_t : : id_base | |
handle > = trophy_handle_t : : id_base + trophy_handle_t : : id_count )
{
error = SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
return res ;
}
2024-12-22 19:59:48 +01:00
const auto hndl = idm : : check_unlocked < trophy_handle_t > ( handle ) ;
2019-12-17 20:55:16 +01:00
if ( ! hndl )
{
error = SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE ;
return res ;
}
else if ( hndl - > is_aborted )
{
error = SCE_NP_TROPHY_ERROR_ABORT ;
return res ;
2020-02-19 18:03:59 +01:00
}
2019-12-17 20:55:16 +01:00
return res ;
}
2022-07-04 15:02:17 +02:00
SAVESTATE_INIT_POS ( 12 ) ;
sce_np_trophy_manager ( ) = default ;
sce_np_trophy_manager ( utils : : serial & ar )
: is_initialized ( ar )
{
}
void save ( utils : : serial & ar )
{
ar ( is_initialized ) ;
if ( is_initialized )
{
USING_SERIALIZATION_VERSION ( sceNpTrophy ) ;
}
}
2015-07-02 03:54:36 +02:00
} ;
2014-04-18 13:28:27 +02:00
2017-05-15 13:30:14 +02:00
template < >
void fmt_class_string < SceNpTrophyError > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( auto error )
{
switch ( error )
{
STR_CASE ( SCE_NP_TROPHY_ERROR_ALREADY_INITIALIZED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_NOT_SUPPORTED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_CONTEXT_NOT_REGISTERED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_OUT_OF_MEMORY ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_EXCEEDS_MAX ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INSUFFICIENT ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INVALID_FORMAT ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_BAD_RESPONSE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INVALID_GRADE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INVALID_CONTEXT ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_PROCESSING_ABORTED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_ABORT ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_LOCKED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_HIDDEN ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_CANNOT_UNLOCK_PLATINUM ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_ALREADY_UNLOCKED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INVALID_TYPE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INVALID_HANDLE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_UNKNOWN_NP_COMM_ID ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_DISC_IO ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_UNSUPPORTED_FORMAT ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_ALREADY_INSTALLED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_BROKEN_DATA ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_VERIFICATION_FAILURE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INVALID_TROPHY_ID ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_UNKNOWN_TROPHY_ID ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_UNKNOWN_TITLE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_UNKNOWN_FILE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_DISC_NOT_MOUNTED ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_SHUTDOWN ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_TITLE_ICON_NOT_FOUND ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_TROPHY_ICON_NOT_FOUND ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_INSUFFICIENT_DISK_SPACE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_ILLEGAL_UPDATE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_SAVEDATA_USER_DOES_NOT_MATCH ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_TROPHY_ID_DOES_NOT_EXIST ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_SERVICE_UNAVAILABLE ) ;
STR_CASE ( SCE_NP_TROPHY_ERROR_UNKNOWN ) ;
}
return unknown ;
} ) ;
}
2020-09-20 10:55:30 +02:00
template < >
void fmt_class_string < SceNpCommunicationSignature > : : format ( std : : string & out , u64 arg )
{
const auto & sign = get_object ( arg ) ;
2021-07-17 10:36:27 +02:00
fmt : : append ( out , " %s " , sign . data ) ;
2020-09-20 10:55:30 +02:00
}
2023-07-25 16:56:12 +02:00
template < >
void fmt_class_string < SceNpCommunicationId > : : format ( std : : string & out , u64 arg )
{
const auto & id = get_object ( arg ) ;
2023-07-26 15:28:09 +02:00
const u8 term = id . data [ 9 ] ;
fmt : : append ( out , " { data='%s', term='%s' (0x%x), num=%d, dummy=%d } " , id . data , std : : isprint ( term ) ? fmt : : format ( " %c " , term ) : " " , term , id . num , id . dummy ) ;
2023-07-25 16:56:12 +02:00
}
2019-12-21 11:46:43 +01:00
// Helpers
2020-02-14 17:10:06 +01:00
static error_code NpTrophyGetTrophyInfo ( const trophy_context_t * ctxt , s32 trophyId , SceNpTrophyDetails * details , SceNpTrophyData * data ) ;
static void show_trophy_notification ( const trophy_context_t * ctxt , s32 trophyId )
2019-12-21 11:46:43 +01:00
{
// Get icon for the notification.
const std : : string padded_trophy_id = fmt : : format ( " %03u " , trophyId ) ;
2020-02-14 17:10:06 +01:00
const std : : string trophy_icon_path = " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name + " /TROP " + padded_trophy_id + " .PNG " ;
2019-12-21 11:46:43 +01:00
fs : : file trophy_icon_file = fs : : file ( vfs : : get ( trophy_icon_path ) ) ;
std : : vector < uchar > trophy_icon_data ;
trophy_icon_file . read ( trophy_icon_data , trophy_icon_file . size ( ) ) ;
2020-02-14 17:10:06 +01:00
SceNpTrophyDetails details { } ;
2019-12-21 11:46:43 +01:00
2020-02-14 17:10:06 +01:00
if ( const auto ret = NpTrophyGetTrophyInfo ( ctxt , trophyId , & details , nullptr ) )
2019-12-21 11:46:43 +01:00
{
2020-02-14 17:10:06 +01:00
sceNpTrophy . error ( " Failed to get info for trophy dialog. Error code 0x%x " , + ret ) ;
2019-12-21 11:46:43 +01:00
}
if ( auto trophy_notification_dialog = Emu . GetCallbacks ( ) . get_trophy_notification_dialog ( ) )
{
2020-02-14 17:10:06 +01:00
trophy_notification_dialog - > ShowTrophyNotification ( details , trophy_icon_data ) ;
2019-12-21 11:46:43 +01:00
}
}
2015-07-02 03:54:36 +02:00
// Functions
2019-12-21 11:46:43 +01:00
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyInit ( vm : : ptr < void > pool , u32 poolSize , u32 containerId , u64 options )
2015-07-02 03:54:36 +02:00
{
2016-01-12 22:57:16 +01:00
sceNpTrophy . warning ( " sceNpTrophyInit(pool=*0x%x, poolSize=0x%x, containerId=0x%x, options=0x%llx) " , pool , poolSize , containerId , options ) ;
2014-04-18 13:28:27 +02:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-09-08 14:11:39 +02:00
2021-03-02 12:59:19 +01:00
std : : scoped_lock lock ( trophy_manager . mtx ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
if ( trophy_manager . is_initialized )
2019-09-08 14:11:39 +02:00
{
return SCE_NP_TROPHY_ERROR_ALREADY_INITIALIZED ;
}
if ( options > 0 )
{
return SCE_NP_TROPHY_ERROR_NOT_SUPPORTED ;
}
2021-03-02 12:59:19 +01:00
trophy_manager . is_initialized = true ;
2019-09-08 14:11:39 +02:00
2015-07-02 03:54:36 +02:00
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyTerm ( )
2015-07-02 03:54:36 +02:00
{
2016-01-12 22:57:16 +01:00
sceNpTrophy . warning ( " sceNpTrophyTerm() " ) ;
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-09-08 14:11:39 +02:00
2021-03-02 12:59:19 +01:00
std : : scoped_lock lock ( trophy_manager . mtx ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2019-09-08 14:11:39 +02:00
{
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
}
2020-05-07 16:15:06 +02:00
idm : : clear < trophy_context_t > ( ) ;
idm : : clear < trophy_handle_t > ( ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
trophy_manager . is_initialized = false ;
2019-09-08 14:11:39 +02:00
2015-07-02 03:54:36 +02:00
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyCreateHandle ( vm : : ptr < u32 > handle )
2014-02-16 02:51:04 +01:00
{
2016-01-12 22:57:16 +01:00
sceNpTrophy . warning ( " sceNpTrophyCreateHandle(handle=*0x%x) " , handle ) ;
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
std : : scoped_lock lock ( trophy_manager . mtx ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2019-09-08 14:11:39 +02:00
{
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
}
2015-07-02 03:54:36 +02:00
if ( ! handle )
2014-02-16 02:51:04 +01:00
{
2015-07-02 03:54:36 +02:00
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
2014-02-16 02:51:04 +01:00
}
2019-12-17 20:55:16 +01:00
const u32 id = idm : : make < trophy_handle_t > ( ) ;
if ( ! id )
{
return SCE_NP_TROPHY_ERROR_EXCEEDS_MAX ;
}
2014-02-16 02:51:04 +01:00
2019-12-17 20:55:16 +01:00
* handle = id ;
2015-07-02 03:54:36 +02:00
return CELL_OK ;
2015-02-08 12:37:10 +01:00
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyDestroyHandle ( u32 handle )
2014-02-16 02:51:04 +01:00
{
2016-01-12 22:57:16 +01:00
sceNpTrophy . warning ( " sceNpTrophyDestroyHandle(handle=0x%x) " , handle ) ;
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
std : : scoped_lock lock ( trophy_manager . mtx ) ;
2019-12-17 20:55:16 +01:00
2019-09-08 14:11:39 +02:00
// TODO: find out if this is checked
2021-03-02 12:59:19 +01:00
//if (!trophy_manager.is_initialized)
2019-09-08 14:11:39 +02:00
//{
// return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED;
//}
2019-12-17 20:55:16 +01:00
if ( handle < trophy_handle_t : : id_base | |
handle > = trophy_handle_t : : id_base + trophy_handle_t : : id_count )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2014-02-16 02:51:04 +01:00
2019-12-17 20:55:16 +01:00
if ( ! idm : : remove < trophy_handle_t > ( handle ) )
2015-07-02 03:54:36 +02:00
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE ;
}
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2019-04-10 19:34:44 +02:00
error_code sceNpTrophyGetGameDetails ( )
{
UNIMPLEMENTED_FUNC ( sceNpTrophy ) ;
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyAbortHandle ( u32 handle )
2014-02-16 02:51:04 +01:00
{
2016-01-12 22:57:16 +01:00
sceNpTrophy . todo ( " sceNpTrophyAbortHandle(handle=0x%x) " , handle ) ;
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
std : : scoped_lock lock ( trophy_manager . mtx ) ;
2019-12-17 20:55:16 +01:00
2019-09-08 14:11:39 +02:00
// TODO: find out if this is checked
2021-03-02 12:59:19 +01:00
//if (!trophy_manager.is_initialized)
2019-09-08 14:11:39 +02:00
//{
// return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED;
//}
2019-12-17 20:55:16 +01:00
if ( handle < trophy_handle_t : : id_base | |
handle > = trophy_handle_t : : id_base + trophy_handle_t : : id_count )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2024-12-22 19:59:48 +01:00
const auto hndl = idm : : check_unlocked < trophy_handle_t > ( handle ) ;
2015-07-02 03:54:36 +02:00
if ( ! hndl )
2015-04-25 23:26:54 +02:00
{
2015-07-02 03:54:36 +02:00
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE ;
2015-04-25 23:26:54 +02:00
}
2019-12-17 20:55:16 +01:00
// Once it is aborted it cannot be used anymore
// TODO: Implement function abortion process maybe? (depends if its actually make sense for some functions)
hndl - > is_aborted = true ;
2015-07-02 03:54:36 +02:00
return CELL_OK ;
}
2019-09-08 14:11:39 +02:00
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyCreateContext ( vm : : ptr < u32 > context , vm : : cptr < SceNpCommunicationId > commId , vm : : cptr < SceNpCommunicationSignature > commSign , u64 options )
2015-07-02 03:54:36 +02:00
{
2016-01-12 22:57:16 +01:00
sceNpTrophy . warning ( " sceNpTrophyCreateContext(context=*0x%x, commId=*0x%x, commSign=*0x%x, options=0x%llx) " , context , commId , commSign , options ) ;
2015-07-02 03:54:36 +02:00
2019-09-08 14:11:39 +02:00
if ( ! commSign )
2017-05-15 13:30:14 +02:00
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2020-09-20 10:55:30 +02:00
sceNpTrophy . notice ( " sceNpTrophyCreateContext(): commSign = %s " , * commSign ) ;
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
std : : scoped_lock lock ( trophy_manager . mtx ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2019-09-08 14:11:39 +02:00
{
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
}
2022-05-05 13:35:37 +02:00
if ( ! context | | ! commId )
2019-09-08 14:11:39 +02:00
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2019-10-15 16:04:22 +02:00
if ( options > SCE_NP_TROPHY_OPTIONS_CREATE_CONTEXT_READ_ONLY )
2019-09-08 14:11:39 +02:00
{
return SCE_NP_TROPHY_ERROR_NOT_SUPPORTED ;
}
2023-07-25 16:56:12 +02:00
sceNpTrophy . warning ( " sceNpTrophyCreateContext(): commId = %s " , * commId ) ;
2015-07-02 03:54:36 +02:00
// rough checks for further fmt::format call
2023-07-25 16:56:12 +02:00
const s32 comm_num = commId - > num ;
if ( comm_num > 99 )
2015-04-25 23:26:54 +02:00
{
2015-07-02 03:54:36 +02:00
return SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID ;
2015-04-25 23:26:54 +02:00
}
2019-09-08 14:11:39 +02:00
2020-09-19 00:59:34 +02:00
// NOTE: commId->term is unused in our code (at least until someone finds out if we need to account for it)
2023-07-25 16:56:12 +02:00
// Generate trophy context name, limited to 9 characters
// Read once for thread-safety reasons
std : : string name_str ( commId - > data , 9 ) ;
2020-09-19 00:59:34 +02:00
// resize the name if it was shorter than expected
2023-07-25 16:56:12 +02:00
if ( const auto pos = name_str . find_first_of ( ' \0 ' ) ; pos ! = std : : string_view : : npos )
{
name_str = name_str . substr ( 0 , pos ) ;
}
const SceNpCommunicationSignature commSign_data = * commSign ;
if ( read_from_ptr < be_t < u32 > > ( commSign_data . data , 0 ) ! = NP_TROPHY_COMM_SIGN_MAGIC )
2017-02-14 18:46:56 +01:00
{
2023-07-25 16:56:12 +02:00
return SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID ;
2017-02-14 18:46:56 +01:00
}
2017-04-13 19:29:47 +02:00
2024-11-11 20:54:44 +01:00
if ( std : : any_of ( & commSign_data . data [ 6 ] , & commSign_data . data [ 6 ] + 6 , FN ( x ! = ' \0 ' ) ) )
2023-07-25 16:56:12 +02:00
{
// 6 padding bytes - must be 0
return SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID ;
}
if ( read_from_ptr < be_t < u16 > > ( commSign_data . data , 4 ) ! = 0x100 )
{
// Signifies version (1.00), although only one constant is allowed
return SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID ;
}
2020-09-21 13:48:07 +02:00
2020-09-19 00:59:34 +02:00
// append the commId number as "_xx"
2023-07-25 16:56:12 +02:00
std : : string name = fmt : : format ( " %s_%02d " , name_str , comm_num ) ;
2020-09-19 00:59:34 +02:00
2015-07-02 03:54:36 +02:00
// create trophy context
2015-08-05 17:30:32 +02:00
const auto ctxt = idm : : make_ptr < trophy_context_t > ( ) ;
2014-02-16 16:37:32 +01:00
2019-12-17 20:55:16 +01:00
if ( ! ctxt )
{
return SCE_NP_TROPHY_ERROR_EXCEEDS_MAX ;
}
2015-07-02 03:54:36 +02:00
// set trophy context parameters (could be passed to constructor through make_ptr call)
2024-12-31 11:09:53 +01:00
ctxt - > trp_name = name ;
2022-05-05 14:08:25 +02:00
ctxt - > read_only = ! ! ( options & SCE_NP_TROPHY_OPTIONS_CREATE_CONTEXT_READ_ONLY ) ;
2017-02-04 16:09:02 +01:00
* context = idm : : last_id ( ) ;
2014-02-16 02:51:04 +01:00
2024-12-31 11:09:53 +01:00
// set current trophy name for trophy list overlay
{
current_trophy_name & current_id = g_fxo - > get < current_trophy_name > ( ) ;
std : : lock_guard lock ( current_id . mtx ) ;
current_id . name = std : : move ( name ) ;
}
2015-07-02 03:54:36 +02:00
return CELL_OK ;
2014-02-16 02:51:04 +01:00
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyDestroyContext ( u32 context )
2014-02-16 02:51:04 +01:00
{
2016-01-12 22:57:16 +01:00
sceNpTrophy . warning ( " sceNpTrophyDestroyContext(context=0x%x) " , context ) ;
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
std : : scoped_lock lock ( trophy_manager . mtx ) ;
2019-12-17 20:55:16 +01:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2019-09-08 14:11:39 +02:00
{
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
}
2019-12-17 20:55:16 +01:00
if ( context < trophy_context_t : : id_base | |
context > = trophy_context_t : : id_base + trophy_context_t : : id_count )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2014-02-16 02:51:04 +01:00
2019-12-17 20:55:16 +01:00
if ( ! idm : : remove < trophy_context_t > ( context ) )
2015-07-02 03:54:36 +02:00
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT ;
}
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyRegisterContext ( ppu_thread & ppu , u32 context , u32 handle , vm : : ptr < SceNpTrophyStatusCallback > statusCb , vm : : ptr < void > arg , u64 options )
2014-02-16 02:51:04 +01:00
{
2021-03-17 15:19:35 +01:00
sceNpTrophy . warning ( " sceNpTrophyRegisterContext(context=0x%x, handle=0x%x, statusCb=*0x%x, arg=*0x%x, options=0x%llx) " , context , handle , statusCb , arg , options ) ;
2015-07-02 03:54:36 +02:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2017-05-15 13:30:14 +02:00
2021-03-02 12:59:19 +01:00
std : : shared_lock lock ( trophy_manager . mtx ) ;
2015-07-02 03:54:36 +02:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2015-02-08 12:37:10 +01:00
}
2014-02-16 02:51:04 +01:00
2022-05-05 18:47:41 +02:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle , true ) ;
2024-12-22 19:59:48 +01:00
const auto handle_ptr = idm : : get_unlocked < trophy_handle_t > ( handle ) ;
2015-07-02 03:54:36 +02:00
2019-12-17 20:55:16 +01:00
if ( error )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2015-07-02 03:54:36 +02:00
}
2014-03-06 13:27:58 +01:00
2019-09-08 14:11:39 +02:00
if ( ! statusCb )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2022-05-05 13:35:37 +02:00
if ( options > SCE_NP_TROPHY_OPTIONS_REGISTER_CONTEXT_SHOW_ERROR_EXIT )
{
return SCE_NP_TROPHY_ERROR_NOT_SUPPORTED ;
}
const auto on_error = [ options ] ( )
{
if ( ! ! ( options & SCE_NP_TROPHY_OPTIONS_REGISTER_CONTEXT_SHOW_ERROR_EXIT ) )
{
2024-09-20 16:38:31 +02:00
static_cast < void > ( open_exit_dialog ( " Error during trophy registration! The game will now be terminated. " , true , msg_dialog_source : : _sceNpTrophy ) ) ;
2022-05-05 13:35:37 +02:00
}
} ;
2023-06-17 00:15:34 +02:00
// open trophy pack file
std : : string trp_path = vfs : : get ( Emu . GetDir ( ) + " TROPDIR/ " + ctxt - > trp_name + " /TROPHY.TRP " ) ;
fs : : file stream ( trp_path ) ;
if ( ! stream & & Emu . GetCat ( ) = = " GD " )
{
sceNpTrophy . warning ( " sceNpTrophyRegisterContext failed to open trophy file from boot path: '%s' (%s) " , trp_path , fs : : g_tls_error ) ;
trp_path = vfs : : get ( " /dev_bdvd/PS3_GAME/TROPDIR/ " + ctxt - > trp_name + " /TROPHY.TRP " ) ;
stream . open ( trp_path ) ;
}
// check if exists and opened
if ( ! stream )
{
const std : : string msg = fmt : : format ( " Failed to open trophy file: '%s' (%s) " , trp_path , fs : : g_tls_error ) ;
return { SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST , msg } ;
}
// TODO:
// SCE_NP_TROPHY_STATUS_DATA_CORRUPT -> reinstall
// SCE_NP_TROPHY_STATUS_REQUIRES_UPDATE -> reinstall (for example if a patch has updates for the trophy data)
// SCE_NP_TROPHY_STATUS_CHANGES_DETECTED -> reinstall (only possible in dev mode)
2020-09-20 10:24:03 +02:00
2019-12-19 02:57:40 +01:00
const std : : string trophyPath = " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name ;
2021-01-12 17:14:51 +01:00
const s32 trp_status = fs : : is_dir ( vfs : : get ( trophyPath ) ) ? SCE_NP_TROPHY_STATUS_INSTALLED : SCE_NP_TROPHY_STATUS_NOT_INSTALLED ;
lock . unlock ( ) ;
sceNpTrophy . notice ( " sceNpTrophyRegisterContext(): Callback is being called (trp_status=%u) " , trp_status ) ;
2020-09-20 10:24:03 +02:00
2023-06-17 00:15:34 +02:00
// "Ask permission" to install the trophy data.
2020-09-20 10:24:03 +02:00
// The callback is called once and then if it returns >= 0 the cb is called through events(coming from vsh) that are passed to the CB through cellSysutilCheckCallback
2021-01-12 17:14:51 +01:00
if ( statusCb ( ppu , context , trp_status , 0 , 0 , arg ) < 0 )
2020-09-20 10:24:03 +02:00
{
2022-05-05 13:35:37 +02:00
on_error ( ) ;
2020-09-20 10:24:03 +02:00
return SCE_NP_TROPHY_ERROR_PROCESSING_ABORTED ;
}
2021-03-02 12:59:19 +01:00
std : : unique_lock lock2 ( trophy_manager . mtx ) ;
2021-01-12 17:14:51 +01:00
// Rerun error checks, the callback could have changed stuff by calling sceNpTrophy functions internally
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2021-01-12 17:14:51 +01:00
{
2022-05-05 13:35:37 +02:00
on_error ( ) ;
2021-01-12 17:14:51 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
}
2021-03-02 12:59:19 +01:00
const auto [ ctxt2 , error2 ] = trophy_manager . get_context_ex ( context , handle ) ;
2021-01-12 17:14:51 +01:00
if ( error2 )
{
// Recheck for any errors, such as if AbortHandle was called
return error2 ;
}
// Paranoid checks: context/handler could have been destroyed and replaced with new ones with the same IDs
// Return an error for such cases
if ( ctxt2 ! = ctxt )
{
2022-05-05 13:35:37 +02:00
on_error ( ) ;
2021-01-12 17:14:51 +01:00
return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT ;
}
2024-12-22 19:59:48 +01:00
if ( handle_ptr . get ( ) ! = idm : : check_unlocked < trophy_handle_t > ( handle ) )
2021-01-12 17:14:51 +01:00
{
2022-05-05 13:35:37 +02:00
on_error ( ) ;
2021-01-12 17:14:51 +01:00
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE ;
}
2022-05-05 14:08:25 +02:00
TRPLoader trp ( stream ) ;
if ( ! trp . LoadHeader ( ) )
{
sceNpTrophy . error ( " sceNpTrophyRegisterContext(): Failed to load trophy config header " ) ;
on_error ( ) ;
return SCE_NP_TROPHY_ERROR_ILLEGAL_UPDATE ;
}
// Rename or discard certain entries based on the files found
const usz kTargetBufferLength = 31 ;
char target [ kTargetBufferLength + 1 ] { } ;
strcpy_trunc ( target , fmt : : format ( " TROP_%02d.SFM " , static_cast < s32 > ( g_cfg . sys . language ) ) ) ;
if ( trp . ContainsEntry ( target ) )
{
trp . RemoveEntry ( " TROPCONF.SFM " ) ;
trp . RemoveEntry ( " TROP.SFM " ) ;
trp . RenameEntry ( target , " TROPCONF.SFM " ) ;
}
else if ( trp . ContainsEntry ( " TROP.SFM " ) )
{
trp . RemoveEntry ( " TROPCONF.SFM " ) ;
trp . RenameEntry ( " TROP.SFM " , " TROPCONF.SFM " ) ;
}
else if ( ! trp . ContainsEntry ( " TROPCONF.SFM " ) )
{
sceNpTrophy . error ( " sceNpTrophyRegisterContext(): Invalid/Incomplete trophy config " ) ;
on_error ( ) ;
return SCE_NP_TROPHY_ERROR_ILLEGAL_UPDATE ;
}
// Discard unnecessary TROP_XX.SFM files
for ( s32 i = 0 ; i < = 18 ; i + + )
{
strcpy_trunc ( target , fmt : : format ( " TROP_%02d.SFM " , i ) ) ;
if ( i ! = g_cfg . sys . language )
{
trp . RemoveEntry ( target ) ;
}
}
2014-03-20 19:23:14 +01:00
if ( ! trp . Install ( trophyPath ) )
2015-07-13 21:06:16 +02:00
{
2019-12-02 22:31:34 +01:00
sceNpTrophy . error ( " sceNpTrophyRegisterContext(): Failed to install trophy context '%s' (%s) " , trophyPath , fs : : g_tls_error ) ;
2022-05-05 13:35:37 +02:00
on_error ( ) ;
2014-03-17 20:34:19 +01:00
return SCE_NP_TROPHY_ERROR_ILLEGAL_UPDATE ;
2015-07-13 21:06:16 +02:00
}
2017-04-13 19:29:47 +02:00
2021-03-13 14:36:59 +01:00
const auto & tropusr = ctxt - > tropusr = std : : make_unique < TROPUSRLoader > ( ) ;
2019-12-19 02:57:40 +01:00
const std : : string trophyUsrPath = trophyPath + " /TROPUSR.DAT " ;
const std : : string trophyConfPath = trophyPath + " /TROPCONF.SFM " ;
2021-03-13 14:36:59 +01:00
ensure ( tropusr - > Load ( trophyUsrPath , trophyConfPath ) . success ) ;
2014-03-20 19:23:14 +01:00
2018-12-23 00:16:03 +01:00
// This emulates vsh sending the events and ensures that not 2 events are processed at once
2020-02-15 19:30:29 +01:00
const std : : pair < u32 , s32 > statuses [ ] =
2018-12-23 00:16:03 +01:00
{
2019-01-02 23:35:39 +01:00
{ SCE_NP_TROPHY_STATUS_PROCESSING_SETUP , 3 } ,
2020-02-15 19:30:29 +01:00
{ SCE_NP_TROPHY_STATUS_PROCESSING_PROGRESS , : : narrow < s32 > ( tropusr - > GetTrophiesCount ( ) ) - 1 } ,
2019-01-02 23:35:39 +01:00
{ SCE_NP_TROPHY_STATUS_PROCESSING_FINALIZE , 4 } ,
{ SCE_NP_TROPHY_STATUS_PROCESSING_COMPLETE , 0 }
2018-12-23 00:16:03 +01:00
} ;
2018-09-07 18:26:59 +02:00
2021-01-12 17:14:51 +01:00
lock2 . unlock ( ) ;
2019-09-21 13:43:12 +02:00
lv2_obj : : sleep ( ppu ) ;
// Create a counter which is destroyed after the function ends
const auto queued = std : : make_shared < atomic_t < u32 > > ( 0 ) ;
2019-01-02 23:35:39 +01:00
2018-09-07 18:26:59 +02:00
for ( auto status : statuses )
2017-04-14 12:08:17 +02:00
{
2019-01-02 23:35:39 +01:00
// One status max per cellSysutilCheckCallback call
2019-09-21 13:43:12 +02:00
* queued + = status . second ;
2020-02-15 19:30:29 +01:00
for ( s32 completed = 0 ; completed < = status . second ; completed + + )
2019-01-02 23:35:39 +01:00
{
2024-12-22 19:59:48 +01:00
sysutil_register_cb ( [ statusCb , status , context , completed , arg , queued ] ( ppu_thread & cb_ppu ) - > s32
2019-01-02 23:35:39 +01:00
{
2023-06-17 00:15:34 +02:00
// TODO: it is possible that we need to check the return value here as well.
2019-01-02 23:35:39 +01:00
statusCb ( cb_ppu , context , status . first , completed , status . second , arg ) ;
2019-09-21 13:43:12 +02:00
if ( queued & & ( * queued ) - - = = 1 )
{
queued - > notify_one ( ) ;
}
2019-01-02 23:35:39 +01:00
return 0 ;
} ) ;
}
2019-11-30 22:16:29 +01:00
u64 current = get_system_time ( ) ;
const u64 until = current + 300'000 ;
2019-09-21 13:43:12 +02:00
// If too much time passes just send the rest of the events anyway
2019-11-30 22:16:29 +01:00
for ( u32 old_value ; current < until & & ( old_value = * queued ) ;
current = get_system_time ( ) )
2019-09-21 13:43:12 +02:00
{
2021-02-13 15:50:07 +01:00
thread_ctrl : : wait_on ( * queued , old_value , until - current ) ;
2019-09-21 13:43:12 +02:00
2019-11-30 22:16:29 +01:00
if ( ppu . is_stopped ( ) )
{
2021-02-13 15:50:07 +01:00
return { } ;
2019-11-30 22:16:29 +01:00
}
2019-01-02 23:35:39 +01:00
}
2017-04-14 12:08:17 +02:00
}
2014-03-17 20:34:19 +01:00
return CELL_OK ;
2014-02-16 02:51:04 +01:00
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyGetRequiredDiskSpace ( u32 context , u32 handle , vm : : ptr < u64 > reqspace , u64 options )
2014-02-16 02:51:04 +01:00
{
2017-11-20 14:08:35 +01:00
sceNpTrophy . warning ( " sceNpTrophyGetRequiredDiskSpace(context=0x%x, handle=0x%x, reqspace=*0x%x, options=0x%llx) " , context , handle , reqspace , options ) ;
2014-02-16 02:51:04 +01:00
2017-05-15 13:30:14 +02:00
if ( ! reqspace )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2019-09-08 14:11:39 +02:00
if ( options > 0 )
{
return SCE_NP_TROPHY_ERROR_NOT_SUPPORTED ;
}
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-09-08 14:11:39 +02:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2015-02-08 12:37:10 +01:00
}
2014-02-27 04:21:08 +01:00
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2015-07-02 03:54:36 +02:00
2019-12-17 20:55:16 +01:00
if ( error )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2015-07-02 03:54:36 +02:00
}
2019-12-13 02:41:56 +01:00
u64 space = 0 ;
2018-06-14 00:11:51 +02:00
if ( ! fs : : is_dir ( vfs : : get ( " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name ) ) )
2017-11-20 14:08:35 +01:00
{
2022-05-05 14:08:25 +02:00
// open trophy pack file
std : : string trophy_path = vfs : : get ( Emu . GetDir ( ) + " TROPDIR/ " + ctxt - > trp_name + " /TROPHY.TRP " ) ;
fs : : file stream ( trophy_path ) ;
if ( ! stream & & Emu . GetCat ( ) = = " GD " )
{
sceNpTrophy . warning ( " sceNpTrophyGetRequiredDiskSpace failed to open trophy file from boot path: '%s' " , trophy_path ) ;
trophy_path = vfs : : get ( " /dev_bdvd/PS3_GAME/TROPDIR/ " + ctxt - > trp_name + " /TROPHY.TRP " ) ;
stream . open ( trophy_path ) ;
}
// check if exists and opened
if ( ! stream )
{
return { SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST , trophy_path } ;
}
TRPLoader trp ( stream ) ;
2017-11-20 14:08:35 +01:00
if ( trp . LoadHeader ( ) )
{
2019-12-13 02:41:56 +01:00
space = trp . GetRequiredSpace ( ) ;
}
else
{
sceNpTrophy . error ( " sceNpTrophyGetRequiredDiskSpace(): Failed to load trophy header! (trp_name=%s) " , ctxt - > trp_name ) ;
2017-11-20 14:08:35 +01:00
}
}
2019-12-13 02:41:56 +01:00
else
{
sceNpTrophy . warning ( " sceNpTrophyGetRequiredDiskSpace(): Trophy config is already installed (trp_name=%s) " , ctxt - > trp_name ) ;
}
2020-02-19 18:03:59 +01:00
2019-12-13 02:41:56 +01:00
sceNpTrophy . warning ( " sceNpTrophyGetRequiredDiskSpace(): reqspace is 0x%llx " , space ) ;
2014-03-06 13:27:58 +01:00
2019-12-13 02:41:56 +01:00
* reqspace = space ;
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophySetSoundLevel ( u32 context , u32 handle , u32 level , u64 options )
2014-02-16 02:51:04 +01:00
{
2016-01-12 22:57:16 +01:00
sceNpTrophy . todo ( " sceNpTrophySetSoundLevel(context=0x%x, handle=0x%x, level=%d, options=0x%llx) " , context , handle , level , options ) ;
2015-07-02 03:54:36 +02:00
2019-12-17 20:55:16 +01:00
if ( level > 100 | | level < 20 )
2019-09-08 14:11:39 +02:00
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
if ( options > 0 )
{
return SCE_NP_TROPHY_ERROR_NOT_SUPPORTED ;
}
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-09-08 14:11:39 +02:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2017-05-15 13:30:14 +02:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2017-05-15 13:30:14 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2017-05-15 13:30:14 +02:00
}
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2017-05-15 13:30:14 +02:00
2019-12-17 20:55:16 +01:00
if ( error )
2017-05-15 13:30:14 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2017-05-15 13:30:14 +02:00
}
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyGetGameInfo ( u32 context , u32 handle , vm : : ptr < SceNpTrophyGameDetails > details , vm : : ptr < SceNpTrophyGameData > data )
2014-02-16 02:51:04 +01:00
{
2021-03-17 15:19:35 +01:00
sceNpTrophy . warning ( " sceNpTrophyGetGameInfo(context=0x%x, handle=0x%x, details=*0x%x, data=*0x%x) " , context , handle , details , data ) ;
2014-09-30 20:42:15 +02:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2017-04-13 19:29:47 +02:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2014-09-30 20:42:15 +02:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2015-07-02 03:54:36 +02:00
}
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2014-03-09 04:57:19 +01:00
2019-12-17 20:55:16 +01:00
if ( error )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2015-07-02 03:54:36 +02:00
}
2014-03-09 04:57:19 +01:00
2020-02-06 21:14:29 +01:00
if ( ! ctxt - > tropusr )
{
// TODO: May return SCE_NP_TROPHY_ERROR_UNKNOWN_TITLE for older sdk version
return SCE_NP_TROPHY_ERROR_CONTEXT_NOT_REGISTERED ;
}
2019-09-08 14:11:39 +02:00
if ( ! details & & ! data )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2022-03-17 23:18:33 +01:00
const std : : string config_path = vfs : : get ( " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name + " /TROPCONF.SFM " ) ;
fs : : file config ( config_path ) ;
2017-09-03 21:29:20 +02:00
if ( ! config )
{
2022-03-17 23:18:33 +01:00
return { SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST , config_path } ;
2017-09-03 21:29:20 +02:00
}
2017-04-13 19:29:47 +02:00
2023-05-28 13:36:45 +02:00
if ( details )
* details = { } ;
if ( data )
* data = { } ;
2022-03-17 23:18:33 +01:00
trophy_xml_document doc { } ;
pugi : : xml_parse_result res = doc . Read ( config . to_string ( ) ) ;
if ( ! res )
2018-03-21 23:04:05 +01:00
{
2022-03-17 23:18:33 +01:00
sceNpTrophy . error ( " sceNpTrophyGetGameInfo: Failed to read TROPCONF.SFM: %s " , config_path ) ;
// TODO: return some error
2023-05-28 13:36:45 +02:00
return CELL_OK ;
2018-03-21 23:04:05 +01:00
}
2023-05-28 13:36:45 +02:00
std : : shared_ptr < rXmlNode > trophy_base = doc . GetRoot ( ) ;
if ( ! trophy_base )
{
sceNpTrophy . error ( " sceNpTrophyGetGameInfo: Failed to read TROPCONF.SFM (root is null): %s " , config_path ) ;
// TODO: return some error
return CELL_OK ;
}
2018-06-15 10:28:32 +02:00
2018-03-21 23:04:05 +01:00
for ( std : : shared_ptr < rXmlNode > n = trophy_base - > GetChildren ( ) ; n ; n = n - > GetNext ( ) )
2015-07-26 11:15:15 +02:00
{
2018-03-21 23:04:05 +01:00
const std : : string n_name = n - > GetName ( ) ;
2017-04-13 19:29:47 +02:00
if ( details )
{
2018-03-21 23:04:05 +01:00
if ( n_name = = " title-name " )
2017-04-13 19:29:47 +02:00
{
2018-06-15 10:28:32 +02:00
strcpy_trunc ( details - > title , n - > GetNodeContent ( ) ) ;
2018-03-21 23:04:05 +01:00
continue ;
2017-04-13 19:29:47 +02:00
}
2018-03-21 23:04:05 +01:00
else if ( n_name = = " title-detail " )
2017-04-13 19:29:47 +02:00
{
2018-06-15 10:28:32 +02:00
strcpy_trunc ( details - > description , n - > GetNodeContent ( ) ) ;
2018-03-21 23:04:05 +01:00
continue ;
2017-04-13 19:29:47 +02:00
}
}
2018-03-21 23:04:05 +01:00
if ( n_name = = " trophy " )
2014-03-20 19:23:14 +01:00
{
2017-04-13 19:29:47 +02:00
if ( details )
2014-03-20 19:23:14 +01:00
{
2017-04-13 19:29:47 +02:00
details - > numTrophies + + ;
2018-03-21 23:04:05 +01:00
switch ( n - > GetAttribute ( " ttype " ) [ 0 ] )
{
2017-04-13 19:29:47 +02:00
case ' B ' : details - > numBronze + + ; break ;
case ' S ' : details - > numSilver + + ; break ;
case ' G ' : details - > numGold + + ; break ;
case ' P ' : details - > numPlatinum + + ; break ;
}
}
if ( data )
{
2019-12-19 02:57:40 +01:00
const u32 trophy_id = atoi ( n - > GetAttribute ( " id " ) . c_str ( ) ) ;
2017-04-13 19:29:47 +02:00
if ( ctxt - > tropusr - > GetTrophyUnlockState ( trophy_id ) )
{
data - > unlockedTrophies + + ;
2018-03-21 23:04:05 +01:00
switch ( n - > GetAttribute ( " ttype " ) [ 0 ] )
{
2017-04-13 19:29:47 +02:00
case ' B ' : data - > unlockedBronze + + ; break ;
case ' S ' : data - > unlockedSilver + + ; break ;
case ' G ' : data - > unlockedGold + + ; break ;
case ' P ' : data - > unlockedPlatinum + + ; break ;
2021-04-09 21:12:47 +02:00
default : break ;
2017-04-13 19:29:47 +02:00
}
2014-03-20 19:23:14 +01:00
}
}
}
}
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2019-04-10 19:34:44 +02:00
error_code sceNpTrophyGetLatestTrophies ( )
{
UNIMPLEMENTED_FUNC ( sceNpTrophy ) ;
return CELL_OK ;
}
2024-04-18 17:11:59 +02:00
error_code sceNpTrophyUnlockTrophy ( ppu_thread & ppu , u32 context , u32 handle , s32 trophyId , vm : : ptr < u32 > platinumId )
2014-02-16 02:51:04 +01:00
{
2021-03-17 15:19:35 +01:00
sceNpTrophy . warning ( " sceNpTrophyUnlockTrophy(context=0x%x, handle=0x%x, trophyId=%d, platinumId=*0x%x) " , context , handle , trophyId , platinumId ) ;
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-09-08 14:11:39 +02:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2015-07-02 03:54:36 +02:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2015-07-02 03:54:36 +02:00
}
2014-03-20 19:23:14 +01:00
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2015-07-02 03:54:36 +02:00
2019-12-17 20:55:16 +01:00
if ( error )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2015-07-02 03:54:36 +02:00
}
2020-02-06 21:14:29 +01:00
if ( ! ctxt - > tropusr )
{
// TODO: May return SCE_NP_TROPHY_ERROR_UNKNOWN_TITLE for older sdk version
return SCE_NP_TROPHY_ERROR_CONTEXT_NOT_REGISTERED ;
}
2019-12-02 22:31:34 +01:00
if ( trophyId < 0 | | trophyId > = static_cast < s32 > ( ctxt - > tropusr - > GetTrophiesCount ( ) ) )
2020-01-06 09:47:11 +01:00
{
2014-03-20 19:23:14 +01:00
return SCE_NP_TROPHY_ERROR_INVALID_TROPHY_ID ;
2020-01-06 09:47:11 +01:00
}
if ( ctxt - > tropusr - > GetTrophyGrade ( trophyId ) = = SCE_NP_TROPHY_GRADE_PLATINUM )
{
return SCE_NP_TROPHY_ERROR_CANNOT_UNLOCK_PLATINUM ;
}
2015-07-02 03:54:36 +02:00
if ( ctxt - > tropusr - > GetTrophyUnlockState ( trophyId ) )
2020-01-06 09:47:11 +01:00
{
2014-03-20 19:23:14 +01:00
return SCE_NP_TROPHY_ERROR_ALREADY_UNLOCKED ;
2020-01-06 09:47:11 +01:00
}
2014-03-20 19:23:14 +01:00
2023-05-15 23:22:13 +02:00
vm : : var < CellRtcTick > tick ;
2024-04-18 17:11:59 +02:00
if ( error_code error = cellRtcGetCurrentTick ( ppu , tick ) )
2023-05-15 23:22:13 +02:00
{
sceNpTrophy . error ( " sceNpTrophyUnlockTrophy: Failed to get timestamp: 0x%x " , + error ) ;
}
2023-11-17 21:33:30 +01:00
if ( ctxt - > tropusr - > UnlockTrophy ( trophyId , tick - > tick , tick - > tick ) )
{
sceNpTrophy . notice ( " Trophy %d unlocked " , trophyId ) ;
}
2014-03-20 19:23:14 +01:00
2019-12-21 11:46:43 +01:00
// TODO: Make sure that unlocking platinum trophies is properly implemented and improve upon it
const std : : string & config_path = vfs : : get ( " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name + " /TROPCONF.SFM " ) ;
const u32 unlocked_platinum_id = ctxt - > tropusr - > GetUnlockedPlatinumID ( trophyId , config_path ) ;
2020-03-04 15:08:40 +01:00
if ( unlocked_platinum_id ! = SCE_NP_TROPHY_INVALID_TROPHY_ID )
2017-09-21 10:21:43 +02:00
{
2019-12-21 11:46:43 +01:00
sceNpTrophy . warning ( " sceNpTrophyUnlockTrophy: All requirements for unlocking the platinum trophy (ID = %d) were met.) " , unlocked_platinum_id ) ;
2023-05-15 23:22:13 +02:00
if ( ctxt - > tropusr - > UnlockTrophy ( unlocked_platinum_id , tick - > tick , tick - > tick ) )
2019-12-21 11:46:43 +01:00
{
sceNpTrophy . success ( " You unlocked a platinum trophy! Hooray!!! " ) ;
}
2017-09-21 10:21:43 +02:00
}
2019-12-21 11:46:43 +01:00
if ( platinumId )
2017-10-24 17:43:05 +02:00
{
2019-12-21 11:46:43 +01:00
* platinumId = unlocked_platinum_id ;
2020-02-14 17:10:06 +01:00
sceNpTrophy . warning ( " sceNpTrophyUnlockTrophy: platinumId was set to %d " , unlocked_platinum_id ) ;
2019-12-21 11:46:43 +01:00
}
2017-10-24 17:43:05 +02:00
2019-12-21 11:46:43 +01:00
const std : : string trophyPath = " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name + " /TROPUSR.DAT " ;
2023-11-17 21:33:30 +01:00
if ( ! ctxt - > tropusr - > Save ( trophyPath ) )
{
sceNpTrophy . error ( " sceNpTrophyUnlockTrophy: failed to save '%s' " , trophyPath ) ;
}
2017-10-24 17:43:05 +02:00
2019-12-21 11:46:43 +01:00
if ( g_cfg . misc . show_trophy_popups )
{
// Enqueue popup for the regular trophy
2020-02-14 17:10:06 +01:00
show_trophy_notification ( ctxt , trophyId ) ;
2018-01-17 17:14:00 +01:00
2020-03-04 15:08:40 +01:00
if ( unlocked_platinum_id ! = SCE_NP_TROPHY_INVALID_TROPHY_ID )
2019-06-20 15:51:50 +02:00
{
2019-12-21 11:46:43 +01:00
// Enqueue popup for the holy platinum trophy
2020-02-14 17:10:06 +01:00
show_trophy_notification ( ctxt , unlocked_platinum_id ) ;
2019-06-20 15:51:50 +02:00
}
2017-10-24 17:43:05 +02:00
}
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyGetTrophyUnlockState ( u32 context , u32 handle , vm : : ptr < SceNpTrophyFlagArray > flags , vm : : ptr < u32 > count )
2014-02-16 02:51:04 +01:00
{
2021-03-17 15:19:35 +01:00
sceNpTrophy . warning ( " sceNpTrophyGetTrophyUnlockState(context=0x%x, handle=0x%x, flags=*0x%x, count=*0x%x) " , context , handle , flags , count ) ;
2014-09-22 21:00:28 +02:00
2022-05-05 17:33:51 +02:00
if ( ! flags | | ! count )
2017-04-13 19:29:47 +02:00
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-09-08 14:11:39 +02:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2014-09-22 21:00:28 +02:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2015-07-02 03:54:36 +02:00
}
2014-02-16 02:51:04 +01:00
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2014-03-20 19:23:14 +01:00
2019-12-17 20:55:16 +01:00
if ( error )
2015-07-02 03:54:36 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2015-02-08 12:37:10 +01:00
}
2014-03-20 19:23:14 +01:00
2022-05-05 19:25:39 +02:00
TROPUSRLoader * tropusr = nullptr ;
TROPUSRLoader local_tropusr { } ;
if ( ctxt - > read_only )
2020-02-06 21:14:29 +01:00
{
2022-05-05 19:25:39 +02:00
const std : : string trophyPath = " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name ;
const std : : string trophyUsrPath = trophyPath + " /TROPUSR.DAT " ;
const std : : string trophyConfPath = trophyPath + " /TROPCONF.SFM " ;
if ( local_tropusr . Load ( trophyUsrPath , trophyConfPath ) . success )
{
tropusr = & local_tropusr ;
}
else
{
// TODO: confirm
* count = 0 ;
* flags = { } ;
return CELL_OK ;
}
}
else
{
if ( ! ctxt - > tropusr )
{
// TODO: May return SCE_NP_TROPHY_ERROR_UNKNOWN_TITLE for older sdk version
return SCE_NP_TROPHY_ERROR_CONTEXT_NOT_REGISTERED ;
}
tropusr = ctxt - > tropusr . get ( ) ;
2020-02-06 21:14:29 +01:00
}
2022-05-05 19:25:39 +02:00
ensure ( tropusr ) ;
const u32 count_ = tropusr - > GetTrophiesCount ( ) ;
2015-02-08 12:37:10 +01:00
* count = count_ ;
if ( count_ > 128 )
2016-01-12 22:57:16 +01:00
sceNpTrophy . error ( " sceNpTrophyGetTrophyUnlockState: More than 128 trophies detected! " ) ;
2014-03-20 19:23:14 +01:00
2019-12-17 18:21:29 +01:00
// Needs hw testing
* flags = { } ;
2014-03-20 19:23:14 +01:00
// Pack up to 128 bools in u32 flag_bits[4]
2015-02-08 12:37:10 +01:00
for ( u32 id = 0 ; id < count_ ; id + + )
2014-03-20 19:23:14 +01:00
{
2022-05-05 19:25:39 +02:00
if ( tropusr - > GetTrophyUnlockState ( id ) )
2017-04-13 19:29:47 +02:00
flags - > flag_bits [ id / 32 ] | = 1 < < ( id % 32 ) ;
2014-03-20 19:23:14 +01:00
else
2017-04-13 19:29:47 +02:00
flags - > flag_bits [ id / 32 ] & = ~ ( 1 < < ( id % 32 ) ) ;
2014-03-20 19:23:14 +01:00
}
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2019-04-10 19:34:44 +02:00
error_code sceNpTrophyGetTrophyDetails ( )
{
UNIMPLEMENTED_FUNC ( sceNpTrophy ) ;
return CELL_OK ;
}
2020-02-14 17:10:06 +01:00
static error_code NpTrophyGetTrophyInfo ( const trophy_context_t * ctxt , s32 trophyId , SceNpTrophyDetails * details , SceNpTrophyData * data )
2014-02-16 02:51:04 +01:00
{
2020-02-14 17:10:06 +01:00
if ( ! details & & ! data )
2015-07-02 03:54:36 +02:00
{
2020-02-14 17:10:06 +01:00
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
2015-07-02 03:54:36 +02:00
}
2017-04-13 19:29:47 +02:00
2020-02-06 21:14:29 +01:00
if ( ! ctxt - > tropusr )
{
// TODO: May return SCE_NP_TROPHY_ERROR_UNKNOWN_TITLE for older sdk version
return SCE_NP_TROPHY_ERROR_CONTEXT_NOT_REGISTERED ;
}
2022-03-17 23:18:33 +01:00
const std : : string config_path = vfs : : get ( " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name + " /TROPCONF.SFM " ) ;
fs : : file config ( config_path ) ;
2017-09-03 21:29:20 +02:00
if ( ! config )
{
2022-03-17 23:18:33 +01:00
return { SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST , config_path } ;
2017-09-03 21:29:20 +02:00
}
2016-03-21 20:42:14 +01:00
2020-02-15 18:46:29 +01:00
SceNpTrophyDetails tmp_details { } ;
SceNpTrophyData tmp_data { } ;
2018-06-15 10:28:32 +02:00
2022-03-17 23:18:33 +01:00
trophy_xml_document doc { } ;
pugi : : xml_parse_result res = doc . Read ( config . to_string ( ) ) ;
if ( ! res )
2017-09-02 13:43:44 +02:00
{
2022-03-17 23:18:33 +01:00
sceNpTrophy . error ( " sceNpTrophyGetGameInfo: Failed to read TROPCONF.SFM: %s " , config_path ) ;
// TODO: return some error
2017-09-02 13:43:44 +02:00
}
2014-03-20 19:23:14 +01:00
2022-03-17 23:18:33 +01:00
auto trophy_base = doc . GetRoot ( ) ;
2023-05-28 13:36:45 +02:00
if ( ! trophy_base )
{
sceNpTrophy . error ( " sceNpTrophyGetGameInfo: Failed to read TROPCONF.SFM (root is null): %s " , config_path ) ;
// TODO: return some error
}
2022-03-17 23:18:33 +01:00
2017-04-13 19:29:47 +02:00
bool found = false ;
2023-05-28 13:36:45 +02:00
for ( std : : shared_ptr < rXmlNode > n = trophy_base ? trophy_base - > GetChildren ( ) : nullptr ; n ; n = n - > GetNext ( ) )
2017-09-03 21:29:20 +02:00
{
2014-05-02 08:30:32 +02:00
if ( n - > GetName ( ) = = " trophy " & & ( trophyId = = atoi ( n - > GetAttribute ( " id " ) . c_str ( ) ) ) )
2014-03-20 19:23:14 +01:00
{
2017-04-13 19:29:47 +02:00
found = true ;
2019-12-19 02:57:40 +01:00
const bool hidden = n - > GetAttribute ( " hidden " ) [ 0 ] = = ' y ' ;
const bool unlocked = ! ! ctxt - > tropusr - > GetTrophyUnlockState ( trophyId ) ;
if ( hidden & & ! unlocked ) // Trophy is hidden
2017-04-13 19:29:47 +02:00
{
return SCE_NP_TROPHY_ERROR_HIDDEN ;
2014-03-20 19:23:14 +01:00
}
2017-04-13 19:29:47 +02:00
if ( details )
{
2020-02-15 18:46:29 +01:00
tmp_details . trophyId = trophyId ;
tmp_details . hidden = hidden ;
2019-12-19 02:57:40 +01:00
2018-03-21 23:04:05 +01:00
switch ( n - > GetAttribute ( " ttype " ) [ 0 ] )
{
2020-02-15 18:46:29 +01:00
case ' B ' : tmp_details . trophyGrade = SCE_NP_TROPHY_GRADE_BRONZE ; break ;
case ' S ' : tmp_details . trophyGrade = SCE_NP_TROPHY_GRADE_SILVER ; break ;
case ' G ' : tmp_details . trophyGrade = SCE_NP_TROPHY_GRADE_GOLD ; break ;
case ' P ' : tmp_details . trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM ; break ;
2021-04-09 21:12:47 +02:00
default : break ;
2017-04-13 19:29:47 +02:00
}
2018-03-21 23:04:05 +01:00
for ( std : : shared_ptr < rXmlNode > n2 = n - > GetChildren ( ) ; n2 ; n2 = n2 - > GetNext ( ) )
{
const std : : string n2_name = n2 - > GetName ( ) ;
if ( n2_name = = " name " )
2017-04-13 19:29:47 +02:00
{
2020-02-15 18:46:29 +01:00
strcpy_trunc ( tmp_details . name , n2 - > GetNodeContent ( ) ) ;
2017-04-13 19:29:47 +02:00
}
2018-03-21 23:04:05 +01:00
else if ( n2_name = = " detail " )
2017-04-13 19:29:47 +02:00
{
2020-02-15 18:46:29 +01:00
strcpy_trunc ( tmp_details . description , n2 - > GetNodeContent ( ) ) ;
2017-04-13 19:29:47 +02:00
}
}
2014-03-20 19:23:14 +01:00
}
2017-04-13 19:29:47 +02:00
if ( data )
{
2020-02-15 18:46:29 +01:00
tmp_data . trophyId = trophyId ;
tmp_data . unlocked = unlocked ;
tmp_data . timestamp = ctxt - > tropusr - > GetTrophyTimestamp ( trophyId ) ;
2014-03-20 19:23:14 +01:00
}
2020-02-14 17:10:06 +01:00
2017-04-13 19:29:47 +02:00
break ;
}
}
2014-03-20 19:23:14 +01:00
2017-04-13 19:29:47 +02:00
if ( ! found )
{
2020-02-15 18:46:29 +01:00
return SCE_NP_TROPHY_ERROR_INVALID_TROPHY_ID ;
}
if ( details )
{
* details = tmp_details ;
}
if ( data )
{
* data = tmp_data ;
2014-03-20 19:23:14 +01:00
}
2014-03-09 04:57:19 +01:00
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2020-02-15 09:25:03 +01:00
2020-02-14 17:10:06 +01:00
error_code sceNpTrophyGetTrophyInfo ( u32 context , u32 handle , s32 trophyId , vm : : ptr < SceNpTrophyDetails > details , vm : : ptr < SceNpTrophyData > data )
{
sceNpTrophy . warning ( " sceNpTrophyGetTrophyInfo(context=0x%x, handle=0x%x, trophyId=%d, details=*0x%x, data=*0x%x) " , context , handle , trophyId , details , data ) ;
if ( trophyId < 0 | | trophyId > 127 ) // max 128 trophies
{
return SCE_NP_TROPHY_ERROR_INVALID_TROPHY_ID ;
}
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2020-02-14 17:10:06 +01:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2020-02-14 17:10:06 +01:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2020-02-14 17:10:06 +01:00
{
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
}
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2020-02-14 17:10:06 +01:00
if ( error )
{
return error ;
}
return NpTrophyGetTrophyInfo ( ctxt , trophyId , details ? details . get_ptr ( ) : nullptr , data ? data . get_ptr ( ) : nullptr ) ;
}
2014-02-16 02:51:04 +01:00
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyGetGameProgress ( u32 context , u32 handle , vm : : ptr < s32 > percentage )
2014-02-16 02:51:04 +01:00
{
2017-04-13 19:29:47 +02:00
sceNpTrophy . warning ( " sceNpTrophyGetGameProgress(context=0x%x, handle=0x%x, percentage=*0x%x) " , context , handle , percentage ) ;
if ( ! percentage )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2019-09-08 14:11:39 +02:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2017-04-13 19:29:47 +02:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2017-04-13 19:29:47 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2017-04-13 19:29:47 +02:00
}
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2017-04-13 19:29:47 +02:00
2019-12-17 20:55:16 +01:00
if ( error )
2017-04-13 19:29:47 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2017-04-13 19:29:47 +02:00
}
2020-02-06 21:14:29 +01:00
if ( ! ctxt - > tropusr )
{
// TODO: May return SCE_NP_TROPHY_ERROR_UNKNOWN_TITLE for older sdk version
return SCE_NP_TROPHY_ERROR_CONTEXT_NOT_REGISTERED ;
}
2019-12-17 23:43:00 +01:00
const u32 unlocked = ctxt - > tropusr - > GetUnlockedTrophiesCount ( ) ;
const u32 trp_count = ctxt - > tropusr - > GetTrophiesCount ( ) ;
2017-04-13 19:29:47 +02:00
2020-02-06 20:52:10 +01:00
// Round result to nearest (TODO: Check 0 trophies)
2020-12-18 15:43:34 +01:00
* percentage = trp_count ? utils : : rounded_div ( unlocked * 100 , trp_count ) : 0 ;
2019-12-16 20:56:14 +01:00
2020-02-06 20:52:10 +01:00
if ( trp_count = = 0 | | trp_count > 128 )
{
sceNpTrophy . warning ( " sceNpTrophyGetGameProgress(): Trophies count may be invalid or untested (%d) " , trp_count ) ;
}
2015-07-02 03:54:36 +02:00
2014-02-16 02:51:04 +01:00
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyGetGameIcon ( u32 context , u32 handle , vm : : ptr < void > buffer , vm : : ptr < u32 > size )
2014-02-16 02:51:04 +01:00
{
2017-04-13 19:29:47 +02:00
sceNpTrophy . warning ( " sceNpTrophyGetGameIcon(context=0x%x, handle=0x%x, buffer=*0x%x, size=*0x%x) " , context , handle , buffer , size ) ;
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2017-04-13 19:29:47 +02:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2017-04-13 19:29:47 +02:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2017-04-13 19:29:47 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2017-04-13 19:29:47 +02:00
}
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2017-04-13 19:29:47 +02:00
2019-12-17 20:55:16 +01:00
if ( error )
2017-04-13 19:29:47 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2017-04-13 19:29:47 +02:00
}
2019-09-08 14:11:39 +02:00
if ( ! size )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2018-06-14 00:11:51 +02:00
fs : : file icon_file ( vfs : : get ( " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name + " /ICON0.PNG " ) ) ;
2017-04-13 19:29:47 +02:00
2017-09-03 21:29:20 +02:00
if ( ! icon_file )
2017-04-13 19:29:47 +02:00
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_FILE ;
}
2017-09-03 21:29:20 +02:00
const u32 icon_size = : : size32 ( icon_file ) ;
2017-04-13 19:29:47 +02:00
2017-09-03 21:29:20 +02:00
if ( buffer & & * size > = icon_size )
2017-04-13 19:29:47 +02:00
{
2021-03-12 07:33:38 +01:00
lv2_file : : op_read ( icon_file , buffer , icon_size ) ;
2017-04-13 19:29:47 +02:00
}
2017-09-03 21:29:20 +02:00
* size = icon_size ;
2014-02-16 02:51:04 +01:00
2015-07-02 03:54:36 +02:00
return CELL_OK ;
}
2019-04-05 20:14:01 +02:00
error_code sceNpTrophyGetUserInfo ( )
{
UNIMPLEMENTED_FUNC ( sceNpTrophy ) ;
return CELL_OK ;
}
2017-05-15 13:30:14 +02:00
error_code sceNpTrophyGetTrophyIcon ( u32 context , u32 handle , s32 trophyId , vm : : ptr < void > buffer , vm : : ptr < u32 > size )
2015-07-02 03:54:36 +02:00
{
2017-04-13 19:29:47 +02:00
sceNpTrophy . warning ( " sceNpTrophyGetTrophyIcon(context=0x%x, handle=0x%x, trophyId=%d, buffer=*0x%x, size=*0x%x) " , context , handle , trophyId , buffer , size ) ;
2021-03-02 12:59:19 +01:00
auto & trophy_manager = g_fxo - > get < sce_np_trophy_manager > ( ) ;
2017-04-13 19:29:47 +02:00
2021-03-02 12:59:19 +01:00
reader_lock lock ( trophy_manager . mtx ) ;
2017-04-13 19:29:47 +02:00
2021-03-02 12:59:19 +01:00
if ( ! trophy_manager . is_initialized )
2017-04-13 19:29:47 +02:00
{
2019-12-17 20:55:16 +01:00
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED ;
2017-04-13 19:29:47 +02:00
}
2021-03-02 12:59:19 +01:00
const auto [ ctxt , error ] = trophy_manager . get_context_ex ( context , handle ) ;
2017-04-13 19:29:47 +02:00
2019-12-17 20:55:16 +01:00
if ( error )
2017-04-13 19:29:47 +02:00
{
2019-12-17 20:55:16 +01:00
return error ;
2017-04-13 19:29:47 +02:00
}
2019-09-08 14:11:39 +02:00
if ( ! size )
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT ;
}
2020-02-06 21:14:29 +01:00
if ( ! ctxt - > tropusr )
{
// TODO: May return SCE_NP_TROPHY_ERROR_UNKNOWN_TITLE for older sdk version
return SCE_NP_TROPHY_ERROR_CONTEXT_NOT_REGISTERED ;
}
2019-12-02 22:31:34 +01:00
if ( ctxt - > tropusr - > GetTrophiesCount ( ) < = static_cast < u32 > ( trophyId ) )
2017-04-13 19:29:47 +02:00
{
return SCE_NP_TROPHY_ERROR_INVALID_TROPHY_ID ;
}
if ( ! ctxt - > tropusr - > GetTrophyUnlockState ( trophyId ) )
{
2022-03-17 23:18:33 +01:00
const std : : string config_path = vfs : : get ( " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name + " /TROPCONF.SFM " ) ;
fs : : file config ( config_path ) ;
if ( config )
{
trophy_xml_document doc { } ;
pugi : : xml_parse_result res = doc . Read ( config . to_string ( ) ) ;
if ( ! res )
{
2023-05-28 13:36:45 +02:00
sceNpTrophy . error ( " sceNpTrophyGetTrophyIcon: Failed to read TROPCONF.SFM: %s " , config_path ) ;
2022-03-17 23:18:33 +01:00
// TODO: return some error
}
auto trophy_base = doc . GetRoot ( ) ;
2023-05-28 13:36:45 +02:00
if ( ! trophy_base )
{
sceNpTrophy . error ( " sceNpTrophyGetTrophyIcon: Failed to read TROPCONF.SFM (root is null): %s " , config_path ) ;
// TODO: return some error
}
2022-03-17 23:18:33 +01:00
2023-05-28 13:36:45 +02:00
for ( std : : shared_ptr < rXmlNode > n = trophy_base ? trophy_base - > GetChildren ( ) : nullptr ; n ; n = n - > GetNext ( ) )
2022-03-17 23:18:33 +01:00
{
if ( n - > GetName ( ) = = " trophy " & & trophyId = = atoi ( n - > GetAttribute ( " id " ) . c_str ( ) ) & & n - > GetAttribute ( " hidden " ) [ 0 ] = = ' y ' )
{
return SCE_NP_TROPHY_ERROR_HIDDEN ;
}
}
}
else
{
// TODO: Maybe return SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST
}
return SCE_NP_TROPHY_ERROR_LOCKED ;
2017-04-13 19:29:47 +02:00
}
2018-06-14 00:11:51 +02:00
fs : : file icon_file ( vfs : : get ( " /dev_hdd0/home/ " + Emu . GetUsr ( ) + " /trophy/ " + ctxt - > trp_name + fmt : : format ( " /TROP%03d.PNG " , trophyId ) ) ) ;
2017-04-13 19:29:47 +02:00
2017-09-03 21:29:20 +02:00
if ( ! icon_file )
2017-04-13 19:29:47 +02:00
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_FILE ;
}
2017-09-03 21:29:20 +02:00
const u32 icon_size = : : size32 ( icon_file ) ;
2017-04-13 19:29:47 +02:00
2017-09-03 21:29:20 +02:00
if ( buffer & & * size > = icon_size )
2017-04-13 19:29:47 +02:00
{
2021-03-12 07:33:38 +01:00
lv2_file : : op_read ( icon_file , buffer , icon_size ) ;
2017-04-13 19:29:47 +02:00
}
2017-09-03 21:29:20 +02:00
* size = icon_size ;
2015-07-02 03:54:36 +02:00
return CELL_OK ;
}
2016-03-21 20:42:14 +01:00
DECLARE ( ppu_module_manager : : sceNpTrophy ) ( " sceNpTrophy " , [ ] ( )
2015-07-02 03:54:36 +02:00
{
2015-02-20 14:58:40 +01:00
REG_FUNC ( sceNpTrophy , sceNpTrophyGetGameProgress ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyRegisterContext ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyCreateHandle ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophySetSoundLevel ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyGetRequiredDiskSpace ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyDestroyContext ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyInit ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyAbortHandle ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyGetGameInfo ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyDestroyHandle ) ;
2019-04-10 19:34:44 +02:00
REG_FUNC ( sceNpTrophy , sceNpTrophyGetGameDetails ) ;
2015-02-20 14:58:40 +01:00
REG_FUNC ( sceNpTrophy , sceNpTrophyUnlockTrophy ) ;
2019-04-10 19:34:44 +02:00
REG_FUNC ( sceNpTrophy , sceNpTrophyGetLatestTrophies ) ;
2015-02-20 14:58:40 +01:00
REG_FUNC ( sceNpTrophy , sceNpTrophyTerm ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyGetTrophyUnlockState ) ;
2019-04-05 20:14:01 +02:00
REG_FUNC ( sceNpTrophy , sceNpTrophyGetUserInfo ) ;
2015-02-20 14:58:40 +01:00
REG_FUNC ( sceNpTrophy , sceNpTrophyGetTrophyIcon ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyCreateContext ) ;
2019-04-10 19:34:44 +02:00
REG_FUNC ( sceNpTrophy , sceNpTrophyGetTrophyDetails ) ;
2015-02-20 14:58:40 +01:00
REG_FUNC ( sceNpTrophy , sceNpTrophyGetTrophyInfo ) ;
REG_FUNC ( sceNpTrophy , sceNpTrophyGetGameIcon ) ;
2015-02-18 17:22:06 +01:00
} ) ;