mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-26 12:42:41 +01:00
Fully implement sceNpDrmVerifyUpgradeLicense
This commit is contained in:
parent
1a2fad4eac
commit
4fcc1c0f31
@ -1,4 +1,4 @@
|
|||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "Emu/System.h"
|
#include "Emu/System.h"
|
||||||
#include "Emu/system_utils.hpp"
|
#include "Emu/system_utils.hpp"
|
||||||
#include "Emu/VFS.h"
|
#include "Emu/VFS.h"
|
||||||
@ -543,52 +543,34 @@ error_code sceNpDrmIsAvailable2(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm
|
|||||||
return npDrmIsAvailable(k_licensee_addr, drm_path);
|
return npDrmIsAvailable(k_licensee_addr, drm_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_code sceNpDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
|
error_code npDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
|
||||||
{
|
{
|
||||||
sceNp.warning("sceNpDrmVerifyUpgradeLicense(content_id=*0x%x)", content_id);
|
|
||||||
|
|
||||||
if (!content_id)
|
if (!content_id)
|
||||||
{
|
{
|
||||||
return SCE_NP_DRM_ERROR_INVALID_PARAM;
|
return SCE_NP_DRM_ERROR_INVALID_PARAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string content_str(content_id.get_ptr(), std::find(content_id.get_ptr(), content_id.get_ptr() + 0x2f, '\0'));
|
const std::string content_str(content_id.get_ptr(), std::find(content_id.get_ptr(), content_id.get_ptr() + 0x2f, '\0'));
|
||||||
const std::string rap_file = rpcs3::utils::get_rap_file_path(content_str);
|
sceNp.warning("npDrmVerifyUpgradeLicense(): content_id='%s'", content_id);
|
||||||
|
|
||||||
sceNp.warning(u8"sceNpDrmVerifyUpgradeLicense(): content_id=“%s”", content_id);
|
if (!rpcs3::utils::verify_c00_unlock_edat(Emu.GetTitleID(), content_str))
|
||||||
|
|
||||||
if (fs::stat_t s{}; !fs::stat(rap_file, s) || s.is_directory || s.size < 0x10)
|
|
||||||
{
|
|
||||||
// Game hasn't been purchased therefore no RAP file present
|
|
||||||
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;
|
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;
|
||||||
}
|
|
||||||
|
|
||||||
// Game has been purchased and there's a RAP file present
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_code sceNpDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
|
||||||
|
{
|
||||||
|
sceNp.warning("sceNpDrmVerifyUpgradeLicense(content_id=*0x%x)", content_id);
|
||||||
|
|
||||||
|
return npDrmVerifyUpgradeLicense(content_id);
|
||||||
|
}
|
||||||
|
|
||||||
error_code sceNpDrmVerifyUpgradeLicense2(vm::cptr<char> content_id)
|
error_code sceNpDrmVerifyUpgradeLicense2(vm::cptr<char> content_id)
|
||||||
{
|
{
|
||||||
sceNp.warning("sceNpDrmVerifyUpgradeLicense2(content_id=*0x%x)", content_id);
|
sceNp.warning("sceNpDrmVerifyUpgradeLicense2(content_id=*0x%x)", content_id);
|
||||||
|
|
||||||
if (!content_id)
|
return npDrmVerifyUpgradeLicense(content_id);
|
||||||
{
|
|
||||||
return SCE_NP_DRM_ERROR_INVALID_PARAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string content_str(content_id.get_ptr(), std::find(content_id.get_ptr(), content_id.get_ptr() + 0x2f, '\0'));
|
|
||||||
const std::string rap_file = rpcs3::utils::get_rap_file_path(content_str);
|
|
||||||
|
|
||||||
sceNp.warning(u8"sceNpDrmVerifyUpgradeLicense2(): content_id=“%s”", content_id);
|
|
||||||
|
|
||||||
if (fs::stat_t s{}; !fs::stat(rap_file, s) || s.is_directory || s.size < 0x10)
|
|
||||||
{
|
|
||||||
// Game hasn't been purchased therefore no RAP file present
|
|
||||||
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Game has been purchased and there's a RAP file present
|
|
||||||
return CELL_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error_code sceNpDrmExecuteGamePurchase()
|
error_code sceNpDrmExecuteGamePurchase()
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "Utilities/StrUtil.h"
|
#include "Utilities/StrUtil.h"
|
||||||
#include "Utilities/Thread.h"
|
#include "Utilities/Thread.h"
|
||||||
#include "Crypto/unpkg.h"
|
#include "Crypto/unpkg.h"
|
||||||
|
#include "Crypto/unself.h"
|
||||||
|
#include "Crypto/unedat.h"
|
||||||
|
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -133,9 +135,9 @@ namespace rpcs3::utils
|
|||||||
return fs::get_cache_dir() + "cache/";
|
return fs::get_cache_dir() + "cache/";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_rap_file_path(const std::string& rap)
|
std::string get_rap_file_path(const std::string_view& rap)
|
||||||
{
|
{
|
||||||
const std::string home_dir = get_hdd0_dir() + "/home";
|
const std::string home_dir = get_hdd0_dir() + "home";
|
||||||
|
|
||||||
std::string rap_path;
|
std::string rap_path;
|
||||||
|
|
||||||
@ -155,6 +157,95 @@ namespace rpcs3::utils
|
|||||||
return rap_path;
|
return rap_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_c00_unlock_edat_path(const std::string_view& title_id, const std::string_view& content_id)
|
||||||
|
{
|
||||||
|
const std::string home_dir = get_hdd0_dir() + "home";
|
||||||
|
|
||||||
|
std::string edat_path;
|
||||||
|
|
||||||
|
for (auto&& entry : fs::dir(home_dir))
|
||||||
|
{
|
||||||
|
if (entry.is_directory && check_user(entry.name))
|
||||||
|
{
|
||||||
|
edat_path = fmt::format("%s/%s/exdata/%s/%s.edat", home_dir, entry.name, title_id, content_id);
|
||||||
|
if (fs::is_file(edat_path))
|
||||||
|
{
|
||||||
|
return edat_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a sample path tested for logging purposes
|
||||||
|
return edat_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verify_c00_unlock_edat(const std::string_view& title_id, const std::string_view& content_id)
|
||||||
|
{
|
||||||
|
const std::string edat_path = rpcs3::utils::get_c00_unlock_edat_path(title_id, content_id);
|
||||||
|
|
||||||
|
// Check if user has unlock EDAT installed
|
||||||
|
if (!fs::is_file(edat_path))
|
||||||
|
{
|
||||||
|
sys_log.notice("verify_c00_unlock_edat(): '%s' not found", edat_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fs::file enc_file(edat_path);
|
||||||
|
u128 k_licensee = get_default_self_klic();
|
||||||
|
std::string edat_content_id;
|
||||||
|
|
||||||
|
if (!VerifyEDATHeaderWithKLicense(enc_file, edat_path, reinterpret_cast<u8*>(&k_licensee), &edat_content_id))
|
||||||
|
{
|
||||||
|
sys_log.error("verify_c00_unlock_edat(): Failed to verify npd file '%s'", edat_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edat_content_id != content_id)
|
||||||
|
{
|
||||||
|
sys_log.error("verify_c00_unlock_edat(): Content ID mismatch in npd header of '%s'", edat_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if required RAP is present
|
||||||
|
std::string rap_path = rpcs3::utils::get_rap_file_path(content_id);
|
||||||
|
|
||||||
|
if (!fs::is_file(rap_path))
|
||||||
|
{
|
||||||
|
// Not necessarily an error
|
||||||
|
sys_log.warning("verify_c00_unlock_edat(): RAP file not found: '%s'", rap_path);
|
||||||
|
rap_path.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt EDAT and verify its contents
|
||||||
|
fs::file dec_file = DecryptEDAT(enc_file, edat_path, 8, rap_path, reinterpret_cast<u8*>(&k_licensee), false);
|
||||||
|
if (!dec_file)
|
||||||
|
{
|
||||||
|
sys_log.error("verify_c00_unlock_edat(): Failed to decrypt '%s'", edat_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 magic{};
|
||||||
|
dec_file.read<u32>(magic);
|
||||||
|
if (magic != "GOMA"_u32)
|
||||||
|
{
|
||||||
|
sys_log.error("verify_c00_unlock_edat(): Bad header magic in unlock EDAT '%s'", edat_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read null-terminated string
|
||||||
|
dec_file.seek(0x10);
|
||||||
|
dec_file.read<true>(edat_content_id, 0x30);
|
||||||
|
edat_content_id.resize(std::min<usz>(0x30, edat_content_id.find_first_of('\0')));
|
||||||
|
if (edat_content_id != content_id)
|
||||||
|
{
|
||||||
|
sys_log.error("verify_c00_unlock_edat(): Content ID mismatch in unlock EDAT '%s'", edat_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game has been purchased and EDAT is verified
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string get_sfo_dir_from_game_path(const std::string& game_path, const std::string& title_id)
|
std::string get_sfo_dir_from_game_path(const std::string& game_path, const std::string& title_id)
|
||||||
{
|
{
|
||||||
if (fs::is_file(game_path + "/PS3_DISC.SFB"))
|
if (fs::is_file(game_path + "/PS3_DISC.SFB"))
|
||||||
@ -189,15 +280,17 @@ namespace rpcs3::utils
|
|||||||
const auto psf = psf::load_object(fs::file(game_path + "/PARAM.SFO"));
|
const auto psf = psf::load_object(fs::file(game_path + "/PARAM.SFO"));
|
||||||
|
|
||||||
const auto category = psf::get_string(psf, "CATEGORY");
|
const auto category = psf::get_string(psf, "CATEGORY");
|
||||||
const auto content_id = std::string(psf::get_string(psf, "CONTENT_ID"));
|
const auto content_id = psf::get_string(psf, "CONTENT_ID");
|
||||||
|
|
||||||
if (category == "HG" && !content_id.empty())
|
if (category == "HG" && !content_id.empty())
|
||||||
{
|
{
|
||||||
// This is a trial game. Check if the user has a RAP file to unlock it.
|
// This is a trial game. Check if the user has EDAT file to unlock it.
|
||||||
if (fs::is_file(game_path + "/C00/PARAM.SFO") && fs::is_file(get_rap_file_path(content_id)))
|
const auto c00_title_id = psf::get_string(psf, "TITLE_ID");
|
||||||
|
|
||||||
|
if (fs::is_file(game_path + "/C00/PARAM.SFO") && verify_c00_unlock_edat(c00_title_id, content_id))
|
||||||
{
|
{
|
||||||
// Load full game data.
|
// Load full game data.
|
||||||
sys_log.notice("Found RAP file %s.rap for trial game %s", content_id, title_id);
|
sys_log.notice("Verified EDAT file %s.edat for trial game %s", content_id, c00_title_id);
|
||||||
return game_path + "/C00";
|
return game_path + "/C00";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,8 @@ namespace rpcs3::utils
|
|||||||
std::string get_hdd1_dir();
|
std::string get_hdd1_dir();
|
||||||
std::string get_cache_dir();
|
std::string get_cache_dir();
|
||||||
|
|
||||||
std::string get_rap_file_path(const std::string& rap);
|
std::string get_rap_file_path(const std::string_view& rap);
|
||||||
|
bool verify_c00_unlock_edat(const std::string_view& title_id, const std::string_view& content_id);
|
||||||
std::string get_sfo_dir_from_game_path(const std::string& game_path, const std::string& title_id = "");
|
std::string get_sfo_dir_from_game_path(const std::string& game_path, const std::string& title_id = "");
|
||||||
|
|
||||||
std::string get_custom_config_dir();
|
std::string get_custom_config_dir();
|
||||||
|
Loading…
Reference in New Issue
Block a user