Trophy update (#2655)

* Added checksum check to TROPHY.TRP loader

* Implemented sceNpTrophyGetGameProgress, sceNpTrophyGetGameIcon & sceNpTrophyGetTrophyIcon

* Updates to up to date APIs and tiny changes

* Code style fixes for checksum verifier, and another fix for trophy functions

* Format fix
This commit is contained in:
Ofek 2017-04-13 20:29:47 +03:00 committed by Ivan
parent 94ab3ae7cf
commit a5fd7abcf7
8 changed files with 265 additions and 69 deletions

View File

@ -92,7 +92,7 @@ s32 sceNpTrophyAbortHandle(u32 handle)
return CELL_OK; return CELL_OK;
} }
void deleteTerminateChar(char* myStr,char _char) { void deleteTerminateChar(char* myStr, char _char) {
char *del = &myStr[strlen(myStr)]; char *del = &myStr[strlen(myStr)];
@ -120,13 +120,14 @@ s32 sceNpTrophyCreateContext(vm::ptr<u32> context, vm::cptr<SceNpCommunicationId
if (commId->term) if (commId->term)
{ {
char trimchar[9]; char trimchar[9];
strcpy(trimchar, commId->data); memcpy(trimchar, commId->data, sizeof(trimchar));
deleteTerminateChar(trimchar,commId->term); trimchar[8] = 0;
deleteTerminateChar(trimchar, commId->term);
name = fmt::format("%s_%02d", trimchar, commId->num); name = fmt::format("%s_%02d", trimchar, commId->num);
} }
else else
{ {
name = fmt::format("%s_%02d", commId->data,commId->num); name = fmt::format("%s_%02d", commId->data, commId->num);
} }
// open trophy pack file // open trophy pack file
@ -265,6 +266,8 @@ s32 sceNpTrophyGetRequiredDiskSpace(u32 context, u32 handle, vm::ptr<u64> reqspa
} }
// TODO: This is not accurate. It's just an approximation of the real value // TODO: This is not accurate. It's just an approximation of the real value
// The real value can be obtained in TRP.cpp:
// m_headers.trp_file_size - sizeof(m_headers) - (m_headers.trp_files_count * m_headers.trp_element_size);
*reqspace = ctxt->trp_stream.size(); *reqspace = ctxt->trp_stream.size();
return CELL_OK; return CELL_OK;
@ -281,6 +284,11 @@ s32 sceNpTrophyGetGameInfo(u32 context, u32 handle, vm::ptr<SceNpTrophyGameDetai
{ {
sceNpTrophy.error("sceNpTrophyGetGameInfo(context=0x%x, handle=0x%x, details=*0x%x, data=*0x%x)", context, handle, details, data); sceNpTrophy.error("sceNpTrophyGetGameInfo(context=0x%x, handle=0x%x, details=*0x%x, data=*0x%x)", context, handle, details, data);
if (!details && !data)
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
const auto ctxt = idm::get<trophy_context_t>(context); const auto ctxt = idm::get<trophy_context_t>(context);
if (!ctxt) if (!ctxt)
@ -303,18 +311,29 @@ s32 sceNpTrophyGetGameInfo(u32 context, u32 handle, vm::ptr<SceNpTrophyGameDetai
rXmlDocument doc; rXmlDocument doc;
doc.Load(path); doc.Load(path);
std::string titleName;
std::string titleDetail;
for (std::shared_ptr<rXmlNode> n = doc.GetRoot()->GetChildren(); n; n = n->GetNext()) for (std::shared_ptr<rXmlNode> n = doc.GetRoot()->GetChildren(); n; n = n->GetNext())
{
if (details)
{ {
if (n->GetName() == "title-name") if (n->GetName() == "title-name")
titleName = n->GetNodeContent(); {
std::string titleName = n->GetNodeContent();
memcpy(details->title, titleName.c_str(), titleName.size());
}
if (n->GetName() == "title-detail") if (n->GetName() == "title-detail")
titleDetail = n->GetNodeContent(); {
std::string titleDetail = n->GetNodeContent();
memcpy(details->description, titleDetail.c_str(), titleDetail.size());
}
}
if (n->GetName() == "trophy") if (n->GetName() == "trophy")
{ {
u32 trophy_id = atoi(n->GetAttribute("id").c_str()); u32 trophy_id = atoi(n->GetAttribute("id").c_str());
if (details)
{
details->numTrophies++; details->numTrophies++;
switch (n->GetAttribute("ttype")[0]) { switch (n->GetAttribute("ttype")[0]) {
case 'B': details->numBronze++; break; case 'B': details->numBronze++; break;
@ -322,7 +341,10 @@ s32 sceNpTrophyGetGameInfo(u32 context, u32 handle, vm::ptr<SceNpTrophyGameDetai
case 'G': details->numGold++; break; case 'G': details->numGold++; break;
case 'P': details->numPlatinum++; break; case 'P': details->numPlatinum++; break;
} }
}
if (data)
{
if (ctxt->tropusr->GetTrophyUnlockState(trophy_id)) if (ctxt->tropusr->GetTrophyUnlockState(trophy_id))
{ {
data->unlockedTrophies++; data->unlockedTrophies++;
@ -335,9 +357,8 @@ s32 sceNpTrophyGetGameInfo(u32 context, u32 handle, vm::ptr<SceNpTrophyGameDetai
} }
} }
} }
}
strcpy_trunc(details->title, titleName);
strcpy_trunc(details->description, titleDetail);
return CELL_OK; return CELL_OK;
} }
@ -376,6 +397,11 @@ s32 sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, vm::ptr<SceNpTrophy
{ {
sceNpTrophy.error("sceNpTrophyGetTrophyUnlockState(context=0x%x, handle=0x%x, flags=*0x%x, count=*0x%x)", context, handle, flags, count); sceNpTrophy.error("sceNpTrophyGetTrophyUnlockState(context=0x%x, handle=0x%x, flags=*0x%x, count=*0x%x)", context, handle, flags, count);
if (!flags || !count)
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
const auto ctxt = idm::get<trophy_context_t>(context); const auto ctxt = idm::get<trophy_context_t>(context);
if (!ctxt) if (!ctxt)
@ -399,9 +425,9 @@ s32 sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, vm::ptr<SceNpTrophy
for (u32 id = 0; id < count_; id++) for (u32 id = 0; id < count_; id++)
{ {
if (ctxt->tropusr->GetTrophyUnlockState(id)) if (ctxt->tropusr->GetTrophyUnlockState(id))
flags->flag_bits[id/32] |= 1<<(id%32); flags->flag_bits[id / 32] |= 1 << (id % 32);
else else
flags->flag_bits[id/32] &= ~(1<<(id%32)); flags->flag_bits[id / 32] &= ~(1 << (id % 32));
} }
return CELL_OK; return CELL_OK;
@ -411,6 +437,11 @@ s32 sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::ptr<SceN
{ {
sceNpTrophy.warning("sceNpTrophyGetTrophyInfo(context=0x%x, handle=0x%x, trophyId=%d, details=*0x%x, data=*0x%x)", context, handle, trophyId, details, data); sceNpTrophy.warning("sceNpTrophyGetTrophyInfo(context=0x%x, handle=0x%x, trophyId=%d, details=*0x%x, data=*0x%x)", context, handle, trophyId, details, data);
if (!details && !data)
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
const auto ctxt = idm::get<trophy_context_t>(context); const auto ctxt = idm::get<trophy_context_t>(context);
if (!ctxt) if (!ctxt)
@ -433,10 +464,18 @@ s32 sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::ptr<SceN
rXmlDocument doc; rXmlDocument doc;
doc.Load(path); doc.Load(path);
std::string name; bool found = false;
std::string detail;
for (std::shared_ptr<rXmlNode> n = doc.GetRoot()->GetChildren(); n; n = n->GetNext()) { for (std::shared_ptr<rXmlNode> n = doc.GetRoot()->GetChildren(); n; n = n->GetNext()) {
if (n->GetName() == "trophy" && (trophyId == atoi(n->GetAttribute("id").c_str()))) if (n->GetName() == "trophy" && (trophyId == atoi(n->GetAttribute("id").c_str())))
{
found = true;
if (n->GetAttribute("hidden")[0] == 'y' && !ctxt->tropusr->GetTrophyUnlockState(trophyId)) // Trophy is hidden
{
return SCE_NP_TROPHY_ERROR_HIDDEN;
}
if (details)
{ {
details->trophyId = trophyId; details->trophyId = trophyId;
switch (n->GetAttribute("ttype")[0]) { switch (n->GetAttribute("ttype")[0]) {
@ -446,44 +485,171 @@ s32 sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::ptr<SceN
case 'P': details->trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM; break; case 'P': details->trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM; break;
} }
switch (n->GetAttribute("ttype")[0]) { switch (n->GetAttribute("hidden")[0]) {
case 'y': details->hidden = true; break; case 'y': details->hidden = true; break;
case 'n': details->hidden = false; break; case 'n': details->hidden = false; break;
} }
for (std::shared_ptr<rXmlNode> n2 = n->GetChildren(); n2; n2 = n2->GetNext()) { for (std::shared_ptr<rXmlNode> n2 = n->GetChildren(); n2; n2 = n2->GetNext()) {
if (n2->GetName() == "name") name = n2->GetNodeContent(); if (n2->GetName() == "name")
if (n2->GetName() == "detail") detail = n2->GetNodeContent(); {
std::string name = n2->GetNodeContent();
memcpy(details->name, name.c_str(), std::min((size_t)SCE_NP_TROPHY_NAME_MAX_SIZE, name.length() + 1));
}
if (n2->GetName() == "detail")
{
std::string detail = n2->GetNodeContent();
memcpy(details->description, detail.c_str(), std::min((size_t)SCE_NP_TROPHY_DESCR_MAX_SIZE, detail.length() + 1));
}
}
} }
if (data)
{
data->trophyId = trophyId; data->trophyId = trophyId;
data->unlocked = ctxt->tropusr->GetTrophyUnlockState(trophyId) != 0; // ??? data->unlocked = ctxt->tropusr->GetTrophyUnlockState(trophyId) != 0; // ???
data->timestamp = ctxt->tropusr->GetTrophyTimestamp(trophyId); data->timestamp = ctxt->tropusr->GetTrophyTimestamp(trophyId);
} }
break;
}
}
if (!found)
{
return SCE_NP_TROPHY_INVALID_TROPHY_ID;
} }
memcpy(details->name, name.c_str(), std::min((size_t) SCE_NP_TROPHY_NAME_MAX_SIZE, name.length() + 1));
memcpy(details->description, detail.c_str(), std::min((size_t) SCE_NP_TROPHY_DESCR_MAX_SIZE, detail.length() + 1));
return CELL_OK; return CELL_OK;
} }
s32 sceNpTrophyGetGameProgress(u32 context, u32 handle, vm::ptr<s32> percentage) s32 sceNpTrophyGetGameProgress(u32 context, u32 handle, vm::ptr<s32> percentage)
{ {
sceNpTrophy.todo("sceNpTrophyGetGameProgress(context=0x%x, handle=0x%x, percentage=*0x%x)", context, handle, percentage); sceNpTrophy.warning("sceNpTrophyGetGameProgress(context=0x%x, handle=0x%x, percentage=*0x%x)", context, handle, percentage);
if (!percentage)
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
const auto ctxt = idm::get<trophy_context_t>(context);
if (!ctxt)
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT;
}
const auto hndl = idm::get<trophy_handle_t>(handle);
if (!hndl)
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
double accuratePercentage = 0;
for (int i = ctxt->tropusr->GetTrophiesCount() - 1; i >= 0; i--)
{
if (ctxt->tropusr->GetTrophyUnlockState(i))
{
accuratePercentage++;
}
}
*percentage = (s32)(accuratePercentage / ctxt->tropusr->GetTrophiesCount());
return CELL_OK; return CELL_OK;
} }
s32 sceNpTrophyGetGameIcon(u32 context, u32 handle, vm::ptr<void> buffer, vm::ptr<u32> size) s32 sceNpTrophyGetGameIcon(u32 context, u32 handle, vm::ptr<void> buffer, vm::ptr<u32> size)
{ {
sceNpTrophy.todo("sceNpTrophyGetGameIcon(context=0x%x, handle=0x%x, buffer=*0x%x, size=*0x%x)", context, handle, buffer, size); sceNpTrophy.warning("sceNpTrophyGetGameIcon(context=0x%x, handle=0x%x, buffer=*0x%x, size=*0x%x)", context, handle, buffer, size);
if (!size)
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
const auto ctxt = idm::get<trophy_context_t>(context);
if (!ctxt)
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT;
}
const auto hndl = idm::get<trophy_handle_t>(handle);
if (!hndl)
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
const std::string& path = vfs::get("/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + "/ICON0.PNG");
if (!fs::exists(path))
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_FILE;
}
fs::file gameIconFile(path);
if (buffer && *size >= gameIconFile.size())
{
gameIconFile.read(buffer.get_ptr(), gameIconFile.size());
}
*size = gameIconFile.size();
return CELL_OK; return CELL_OK;
} }
s32 sceNpTrophyGetTrophyIcon(u32 context, u32 handle, s32 trophyId, vm::ptr<void> buffer, vm::ptr<u32> size) s32 sceNpTrophyGetTrophyIcon(u32 context, u32 handle, s32 trophyId, vm::ptr<void> buffer, vm::ptr<u32> size)
{ {
sceNpTrophy.todo("sceNpTrophyGetTrophyIcon(context=0x%x, handle=0x%x, trophyId=%d, buffer=*0x%x, size=*0x%x)", context, handle, trophyId, buffer, size); sceNpTrophy.warning("sceNpTrophyGetTrophyIcon(context=0x%x, handle=0x%x, trophyId=%d, buffer=*0x%x, size=*0x%x)", context, handle, trophyId, buffer, size);
if (!size)
{
return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
}
const auto ctxt = idm::get<trophy_context_t>(context);
if (!ctxt)
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT;
}
const auto hndl = idm::get<trophy_handle_t>(handle);
if (!hndl)
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
if (ctxt->tropusr->GetTrophiesCount() <= (u32)trophyId)
{
return SCE_NP_TROPHY_ERROR_INVALID_TROPHY_ID;
}
if (!ctxt->tropusr->GetTrophyUnlockState(trophyId))
{
bool hidden = false; // TODO obtain this value
return hidden ? SCE_NP_TROPHY_ERROR_HIDDEN : SCE_NP_TROPHY_ERROR_LOCKED;
}
const std::string& path = vfs::get("/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + fmt::format("/TROP%03d.PNG", trophyId));
if (!fs::exists(path))
{
return SCE_NP_TROPHY_ERROR_UNKNOWN_FILE;
}
fs::file trophyIconFile(path);
if (buffer && *size >= trophyIconFile.size())
{
trophyIconFile.read(buffer.get_ptr(), trophyIconFile.size());
}
*size = trophyIconFile.size();
return CELL_OK; return CELL_OK;
} }

View File

@ -1,6 +1,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "Utilities/Config.h" #include "Utilities/Config.h"
#include "Utilities/AutoPause.h" #include "Utilities/AutoPause.h"
#include "Utilities/VirtualMemory.h"
#include "Crypto/sha1.h" #include "Crypto/sha1.h"
#include "Crypto/unself.h" #include "Crypto/unself.h"
#include "Loader/ELF.h" #include "Loader/ELF.h"

View File

@ -179,10 +179,11 @@ extern void ppu_register_range(u32 addr, u32 size)
} }
// Register executable range at // Register executable range at
utils::memory_commit(&ppu_ref(addr), size); utils::memory_commit(&ppu_ref(addr), size, utils::protection::rw);
const u32 fallback = ::narrow<u32>(reinterpret_cast<std::uintptr_t>(ppu_fallback)); const u32 fallback = ::narrow<u32>(reinterpret_cast<std::uintptr_t>(ppu_fallback));
size &= ~3; // Loop assumes `size = n * 4`, enforce that by rounding down
while (size) while (size)
{ {
ppu_ref(addr) = fallback; ppu_ref(addr) = fallback;
@ -707,7 +708,7 @@ u32 ppu_thread::stack_push(u32 size, u32 align_v)
const u32 old_pos = vm::cast(context.gpr[1], HERE); const u32 old_pos = vm::cast(context.gpr[1], HERE);
context.gpr[1] -= align(size + 4, 8); // room minimal possible size context.gpr[1] -= align(size + 4, 8); // room minimal possible size
context.gpr[1] &= ~(align_v - 1); // fix stack alignment context.gpr[1] &= ~((u64)align_v - 1); // fix stack alignment
if (old_pos >= context.stack_addr && old_pos < context.stack_addr + context.stack_size && context.gpr[1] < context.stack_addr) if (old_pos >= context.stack_addr && old_pos < context.stack_addr + context.stack_size && context.gpr[1] < context.stack_addr)
{ {

View File

@ -135,9 +135,9 @@ namespace rsx
case CELL_GCM_CONTEXT_DMA_DEVICE_R: case CELL_GCM_CONTEXT_DMA_DEVICE_R:
fmt::throw_exception("Unimplemented CELL_GCM_CONTEXT_DMA_DEVICE_R (offset=0x%x, location=0x%x)" HERE, offset, location); fmt::throw_exception("Unimplemented CELL_GCM_CONTEXT_DMA_DEVICE_R (offset=0x%x, location=0x%x)" HERE, offset, location);
default:
fmt::throw_exception("Invalid location (offset=0x%x, location=0x%x)" HERE, offset, location); fmt::throw_exception("Invalid location (offset=0x%x, location=0x%x)" HERE, offset, location);
} }
} }
u32 get_vertex_type_size_on_host(vertex_base_type type, u32 size) u32 get_vertex_type_size_on_host(vertex_base_type type, u32 size)
@ -200,7 +200,7 @@ namespace rsx
{ {
case CELL_GCM_COMPMODE_C32_2X1: case CELL_GCM_COMPMODE_C32_2X1:
case CELL_GCM_COMPMODE_DISABLED: case CELL_GCM_COMPMODE_DISABLED:
for (int y = 0; y < height; ++y) for (u32 y = 0; y < height; ++y)
{ {
memcpy(ptr + (offset_y + y) * tile->pitch + offset_x, (u8*)src + pitch * y, pitch); memcpy(ptr + (offset_y + y) * tile->pitch + offset_x, (u8*)src + pitch * y, pitch);
} }
@ -253,7 +253,7 @@ namespace rsx
{ {
case CELL_GCM_COMPMODE_C32_2X1: case CELL_GCM_COMPMODE_C32_2X1:
case CELL_GCM_COMPMODE_DISABLED: case CELL_GCM_COMPMODE_DISABLED:
for (int y = 0; y < height; ++y) for (u32 y = 0; y < height; ++y)
{ {
memcpy((u8*)dst + pitch * y, ptr + (offset_y + y) * tile->pitch + offset_x, pitch); memcpy((u8*)dst + pitch * y, ptr + (offset_y + y) * tile->pitch + offset_x, pitch);
} }

View File

@ -287,14 +287,14 @@ void RSXDebugger::OnChangeToolsAddr(wxCommandEvent& event)
void RSXDebugger::OnScrollMemory(wxMouseEvent& event) void RSXDebugger::OnScrollMemory(wxMouseEvent& event)
{ {
if(vm::check_addr(m_addr)) if(vm::check_addr(m_addr, 4))
{ {
int items = event.ControlDown() ? m_item_count : 1; int items = event.ControlDown() ? m_item_count : 1;
for(int i=0; i<items; ++i) for(int i=0; i<items; ++i)
{ {
u32 offset; u32 offset;
if(vm::check_addr(m_addr)) if(vm::check_addr(m_addr, 4))
{ {
u32 cmd = vm::ps3::read32(m_addr); u32 cmd = vm::ps3::read32(m_addr);
u32 count = ((cmd & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD) u32 count = ((cmd & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD)

View File

@ -50,6 +50,8 @@ struct TROPUSREntry6
be_t<u64> timestamp1; be_t<u64> timestamp1;
be_t<u64> timestamp2; be_t<u64> timestamp2;
char unk6[64]; // Just zeroes? char unk6[64]; // Just zeroes?
//Note: One of the fields should hold a flag showing whether the trophy is hidden or not
}; };
class TROPUSRLoader class TROPUSRLoader

View File

@ -1,6 +1,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "TRP.h" #include "TRP.h"
#include "Crypto/sha1.h"
TRPLoader::TRPLoader(const fs::file& f) TRPLoader::TRPLoader(const fs::file& f)
: trp_f(f) : trp_f(f)
@ -58,6 +59,31 @@ bool TRPLoader::LoadHeader(bool show)
LOG_NOTICE(LOADER, "TRP version: 0x%x", m_header.trp_version); LOG_NOTICE(LOADER, "TRP version: 0x%x", m_header.trp_version);
} }
if (m_header.trp_version >= 2)
{
unsigned char hash[20];
std::vector<unsigned char> file_contents(m_header.trp_file_size);
trp_f.seek(0);
if (!trp_f.read(file_contents))
{
LOG_NOTICE(LOADER, "Failed verifying checksum");
}
else
{
memset(&(reinterpret_cast<TRPHeader*>(file_contents.data()))->sha1, 0, 20);
sha1(reinterpret_cast<const unsigned char*>(file_contents.data()), m_header.trp_file_size, hash);
if (memcmp(hash, m_header.sha1, 20) != 0)
{
LOG_ERROR(LOADER, "Invalid checksum of TROPHY.TRP file");
return false;
}
}
trp_f.seek(sizeof(m_header));
}
m_entries.clear(); m_entries.clear();
m_entries.resize(m_header.trp_files_count); m_entries.resize(m_header.trp_files_count);

View File

@ -7,7 +7,7 @@ struct TRPHeader
be_t<u64> trp_file_size; be_t<u64> trp_file_size;
be_t<u32> trp_files_count; be_t<u32> trp_files_count;
be_t<u32> trp_element_size; be_t<u32> trp_element_size;
be_t<u32> trp_unknown; be_t<u32> trp_dev_flag;
unsigned char sha1[20]; unsigned char sha1[20];
unsigned char padding[16]; unsigned char padding[16];
}; };