From a5fd7abcf7808a33523fab740ae35affbd865bac Mon Sep 17 00:00:00 2001 From: Ofek Date: Thu, 13 Apr 2017 20:29:47 +0300 Subject: [PATCH] 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 --- rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp | 286 +++++++++++++++++++------ rpcs3/Emu/Cell/PPUModule.cpp | 1 + rpcs3/Emu/Cell/PPUThread.cpp | 5 +- rpcs3/Emu/RSX/RSXThread.cpp | 8 +- rpcs3/Gui/RSXDebugger.cpp | 4 +- rpcs3/Loader/TROPUSR.h | 2 + rpcs3/Loader/TRP.cpp | 26 +++ rpcs3/Loader/TRP.h | 2 +- 8 files changed, 265 insertions(+), 69 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp index 63a73ba49c..301538753c 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp @@ -92,7 +92,7 @@ s32 sceNpTrophyAbortHandle(u32 handle) return CELL_OK; } -void deleteTerminateChar(char* myStr,char _char) { +void deleteTerminateChar(char* myStr, char _char) { char *del = &myStr[strlen(myStr)]; @@ -120,15 +120,16 @@ s32 sceNpTrophyCreateContext(vm::ptr context, vm::cptrterm) { char trimchar[9]; - strcpy(trimchar, commId->data); - deleteTerminateChar(trimchar,commId->term); + memcpy(trimchar, commId->data, sizeof(trimchar)); + trimchar[8] = 0; + deleteTerminateChar(trimchar, commId->term); name = fmt::format("%s_%02d", trimchar, commId->num); } else { - name = fmt::format("%s_%02d", commId->data,commId->num); + name = fmt::format("%s_%02d", commId->data, commId->num); } - + // open trophy pack file fs::file stream(vfs::get("/app_home/../TROPDIR/" + name + "/TROPHY.TRP")); @@ -232,7 +233,7 @@ s32 sceNpTrophyRegisterContext(ppu_thread& CPU, u32 context, u32 handle, vm::ptr sceNpTrophy.error("sceNpTrophyRegisterContext(): SCE_NP_TROPHY_ERROR_ILLEGAL_UPDATE"); return SCE_NP_TROPHY_ERROR_ILLEGAL_UPDATE; } - + TROPUSRLoader* tropusr = new TROPUSRLoader(); std::string trophyUsrPath = trophyPath + "/TROPUSR.DAT"; std::string trophyConfPath = trophyPath + "/TROPCONF.SFM"; @@ -242,7 +243,7 @@ s32 sceNpTrophyRegisterContext(ppu_thread& CPU, u32 context, u32 handle, vm::ptr // TODO: Callbacks statusCb(CPU, context, SCE_NP_TROPHY_STATUS_INSTALLED, 100, 100, arg); statusCb(CPU, context, SCE_NP_TROPHY_STATUS_PROCESSING_COMPLETE, 100, 100, arg); - + return CELL_OK; } @@ -265,6 +266,8 @@ s32 sceNpTrophyGetRequiredDiskSpace(u32 context, u32 handle, vm::ptr reqspa } // 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(); return CELL_OK; @@ -281,6 +284,11 @@ s32 sceNpTrophyGetGameInfo(u32 context, u32 handle, vm::ptr(context); if (!ctxt) @@ -297,47 +305,60 @@ s32 sceNpTrophyGetGameInfo(u32 context, u32 handle, vm::ptrtrp_name + "/TROPCONF.SFM"); - + // TODO: rXmlDocument can open only real file - verify(HERE), !fs::get_virtual_device(path); + verify(HERE), !fs::get_virtual_device(path); rXmlDocument doc; doc.Load(path); - std::string titleName; - std::string titleDetail; for (std::shared_ptr n = doc.GetRoot()->GetChildren(); n; n = n->GetNext()) { - if (n->GetName() == "title-name") - titleName = n->GetNodeContent(); - if (n->GetName() == "title-detail") - titleDetail = n->GetNodeContent(); + if (details) + { + if (n->GetName() == "title-name") + { + std::string titleName = n->GetNodeContent(); + memcpy(details->title, titleName.c_str(), titleName.size()); + } + + if (n->GetName() == "title-detail") + { + std::string titleDetail = n->GetNodeContent(); + memcpy(details->description, titleDetail.c_str(), titleDetail.size()); + } + } + if (n->GetName() == "trophy") { u32 trophy_id = atoi(n->GetAttribute("id").c_str()); - - details->numTrophies++; - switch (n->GetAttribute("ttype")[0]) { - case 'B': details->numBronze++; break; - case 'S': details->numSilver++; break; - case 'G': details->numGold++; break; - case 'P': details->numPlatinum++; break; - } - - if (ctxt->tropusr->GetTrophyUnlockState(trophy_id)) + + if (details) { - data->unlockedTrophies++; + details->numTrophies++; switch (n->GetAttribute("ttype")[0]) { - case 'B': data->unlockedBronze++; break; - case 'S': data->unlockedSilver++; break; - case 'G': data->unlockedGold++; break; - case 'P': data->unlockedPlatinum++; break; + case 'B': details->numBronze++; break; + case 'S': details->numSilver++; break; + case 'G': details->numGold++; break; + case 'P': details->numPlatinum++; break; + } + } + + if (data) + { + if (ctxt->tropusr->GetTrophyUnlockState(trophy_id)) + { + data->unlockedTrophies++; + switch (n->GetAttribute("ttype")[0]) { + case 'B': data->unlockedBronze++; break; + case 'S': data->unlockedSilver++; break; + case 'G': data->unlockedGold++; break; + case 'P': data->unlockedPlatinum++; break; + } } } } } - strcpy_trunc(details->title, titleName); - strcpy_trunc(details->description, titleDetail); return CELL_OK; } @@ -376,6 +397,11 @@ s32 sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, vm::ptr(context); if (!ctxt) @@ -399,9 +425,9 @@ s32 sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, vm::ptrtropusr->GetTrophyUnlockState(id)) - flags->flag_bits[id/32] |= 1<<(id%32); + flags->flag_bits[id / 32] |= 1 << (id % 32); else - flags->flag_bits[id/32] &= ~(1<<(id%32)); + flags->flag_bits[id / 32] &= ~(1 << (id % 32)); } return CELL_OK; @@ -411,6 +437,11 @@ s32 sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::ptr(context); if (!ctxt) @@ -424,66 +455,201 @@ s32 sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::ptrtrp_name + "/TROPCONF.SFM"); // TODO: rXmlDocument can open only real file verify(HERE), !fs::get_virtual_device(path); - rXmlDocument doc; + rXmlDocument doc; doc.Load(path); - std::string name; - std::string detail; + bool found = false; for (std::shared_ptr n = doc.GetRoot()->GetChildren(); n; n = n->GetNext()) { if (n->GetName() == "trophy" && (trophyId == atoi(n->GetAttribute("id").c_str()))) { - details->trophyId = trophyId; - switch (n->GetAttribute("ttype")[0]) { - case 'B': details->trophyGrade = SCE_NP_TROPHY_GRADE_BRONZE; break; - case 'S': details->trophyGrade = SCE_NP_TROPHY_GRADE_SILVER; break; - case 'G': details->trophyGrade = SCE_NP_TROPHY_GRADE_GOLD; break; - case 'P': details->trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM; break; + found = true; + + if (n->GetAttribute("hidden")[0] == 'y' && !ctxt->tropusr->GetTrophyUnlockState(trophyId)) // Trophy is hidden + { + return SCE_NP_TROPHY_ERROR_HIDDEN; } - switch (n->GetAttribute("ttype")[0]) { - case 'y': details->hidden = true; break; - case 'n': details->hidden = false; break; + if (details) + { + details->trophyId = trophyId; + switch (n->GetAttribute("ttype")[0]) { + case 'B': details->trophyGrade = SCE_NP_TROPHY_GRADE_BRONZE; break; + case 'S': details->trophyGrade = SCE_NP_TROPHY_GRADE_SILVER; break; + case 'G': details->trophyGrade = SCE_NP_TROPHY_GRADE_GOLD; break; + case 'P': details->trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM; break; + } + + switch (n->GetAttribute("hidden")[0]) { + case 'y': details->hidden = true; break; + case 'n': details->hidden = false; break; + } + + for (std::shared_ptr n2 = n->GetChildren(); n2; n2 = n2->GetNext()) { + if (n2->GetName() == "name") + { + 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)); + } + } } - for (std::shared_ptr n2 = n->GetChildren(); n2; n2 = n2->GetNext()) { - if (n2->GetName() == "name") name = n2->GetNodeContent(); - if (n2->GetName() == "detail") detail = n2->GetNodeContent(); + if (data) + { + data->trophyId = trophyId; + data->unlocked = ctxt->tropusr->GetTrophyUnlockState(trophyId) != 0; // ??? + data->timestamp = ctxt->tropusr->GetTrophyTimestamp(trophyId); } - - data->trophyId = trophyId; - data->unlocked = ctxt->tropusr->GetTrophyUnlockState(trophyId) != 0; // ??? - 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; } s32 sceNpTrophyGetGameProgress(u32 context, u32 handle, vm::ptr 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(context); + + if (!ctxt) + { + return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT; + } + + const auto hndl = idm::get(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; } s32 sceNpTrophyGetGameIcon(u32 context, u32 handle, vm::ptr buffer, vm::ptr 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(context); + + if (!ctxt) + { + return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT; + } + + const auto hndl = idm::get(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; } s32 sceNpTrophyGetTrophyIcon(u32 context, u32 handle, s32 trophyId, vm::ptr buffer, vm::ptr 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(context); + + if (!ctxt) + { + return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT; + } + + const auto hndl = idm::get(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; } diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 80352c32a9..bf6a20e21f 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "Utilities/Config.h" #include "Utilities/AutoPause.h" +#include "Utilities/VirtualMemory.h" #include "Crypto/sha1.h" #include "Crypto/unself.h" #include "Loader/ELF.h" diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index a0ac9ed4a9..2b6712eb2f 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -179,10 +179,11 @@ extern void ppu_register_range(u32 addr, u32 size) } // 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(reinterpret_cast(ppu_fallback)); + size &= ~3; // Loop assumes `size = n * 4`, enforce that by rounding down while (size) { 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); 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) { diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index da6eff76b6..f5fe89830f 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -135,9 +135,9 @@ namespace rsx 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("Invalid location (offset=0x%x, location=0x%x)" HERE, offset, location); + default: + 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) @@ -200,7 +200,7 @@ namespace rsx { case CELL_GCM_COMPMODE_C32_2X1: 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); } @@ -253,7 +253,7 @@ namespace rsx { case CELL_GCM_COMPMODE_C32_2X1: 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); } diff --git a/rpcs3/Gui/RSXDebugger.cpp b/rpcs3/Gui/RSXDebugger.cpp index 7d0e213b3a..0b9d24040a 100644 --- a/rpcs3/Gui/RSXDebugger.cpp +++ b/rpcs3/Gui/RSXDebugger.cpp @@ -287,14 +287,14 @@ void RSXDebugger::OnChangeToolsAddr(wxCommandEvent& 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; for(int i=0; i timestamp1; be_t timestamp2; char unk6[64]; // Just zeroes? + + //Note: One of the fields should hold a flag showing whether the trophy is hidden or not }; class TROPUSRLoader diff --git a/rpcs3/Loader/TRP.cpp b/rpcs3/Loader/TRP.cpp index be78fb7c63..da6bea24bb 100644 --- a/rpcs3/Loader/TRP.cpp +++ b/rpcs3/Loader/TRP.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "Emu/System.h" #include "TRP.h" +#include "Crypto/sha1.h" TRPLoader::TRPLoader(const fs::file& f) : trp_f(f) @@ -58,6 +59,31 @@ bool TRPLoader::LoadHeader(bool show) LOG_NOTICE(LOADER, "TRP version: 0x%x", m_header.trp_version); } + if (m_header.trp_version >= 2) + { + unsigned char hash[20]; + std::vector 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(file_contents.data()))->sha1, 0, 20); + sha1(reinterpret_cast(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.resize(m_header.trp_files_count); diff --git a/rpcs3/Loader/TRP.h b/rpcs3/Loader/TRP.h index 46c687058a..4418e805b0 100644 --- a/rpcs3/Loader/TRP.h +++ b/rpcs3/Loader/TRP.h @@ -7,7 +7,7 @@ struct TRPHeader be_t trp_file_size; be_t trp_files_count; be_t trp_element_size; - be_t trp_unknown; + be_t trp_dev_flag; unsigned char sha1[20]; unsigned char padding[16]; };