This commit is contained in:
RipleyTom
2026-08-22 14:56:31 +03:00
committed by Elad
parent b97f4bd8da
commit 8bbcd5936d
13 changed files with 115 additions and 29 deletions
+1 -1
View File
@@ -839,7 +839,7 @@ bool EDATADecrypter::ReadHeader()
// Type 2: Use key from RAP file (RIF key). (also used for type 1 at the moment)
else
{
const std::string rap_path = rpcs3::utils::get_rap_file_path(npdHeader.content_id);
const std::string rap_path = rpcs3::utils::get_rap_file_path(npdHeader.get_content_id());
if (fs::file rap{rap_path}; rap && rap.size() >= sizeof(dec_key))
{
+6
View File
@@ -49,6 +49,12 @@ struct NPD_HEADER
u8 dev_hash[0x10];
s64 activate_time;
s64 expire_time;
std::string get_content_id() const
{
const std::string_view id{content_id, sizeof(content_id)};
return std::string{id.substr(0, id.find_first_of('\0'))};
}
};
struct EDAT_HEADER
+6 -7
View File
@@ -323,7 +323,7 @@ void supplemental_header::Show() const
self_log.notice("Version: 0x%08x", PS3_npdrm_header.npd.version);
self_log.notice("License: 0x%08x", PS3_npdrm_header.npd.license);
self_log.notice("Type: 0x%08x", PS3_npdrm_header.npd.type);
self_log.notice("ContentID: %s", PS3_npdrm_header.npd.content_id);
self_log.notice("ContentID: %s", PS3_npdrm_header.npd.get_content_id());
self_log.notice("Digest: %s", PS3_npdrm_header.npd.digest);
self_log.notice("Inverse digest: %s", PS3_npdrm_header.npd.title_hash);
self_log.notice("XOR digest: %s", PS3_npdrm_header.npd.dev_hash);
@@ -1065,7 +1065,7 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
if (npd->license == 1) // Network license.
{
// Try to find a RAP file to get the key.
if (!GetKeyFromRap(npd->content_id, npdrm_key))
if (!GetKeyFromRap(npd->get_content_id(), npdrm_key))
{
self_log.error("Can't decrypt network NPDRM!");
return false;
@@ -1074,7 +1074,7 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
else if (npd->license == 2) // Local license.
{
// Try to find a RAP file to get the key.
if (!GetKeyFromRap(npd->content_id, npdrm_key))
if (!GetKeyFromRap(npd->get_content_id(), npdrm_key))
{
self_log.error("Can't find RAP file for NPDRM decryption!");
return false;
@@ -1283,14 +1283,13 @@ fs::file SELFDecrypter::MakeElf(bool isElf32)
return e;
}
bool SELFDecrypter::GetKeyFromRap(const char* content_id, u8* npdrm_key)
bool SELFDecrypter::GetKeyFromRap(std::string_view content_id, u8* npdrm_key)
{
// Set empty RAP key.
std::array<u8, 0x10> rap_key {};
// Try to find a matching RAP file under exdata folder.
const std::string ci_str = content_id;
const std::string rap_path = rpcs3::utils::get_rap_file_path(ci_str);
const std::string rap_path = rpcs3::utils::get_rap_file_path(content_id);
// Open the RAP file and read the key.
const fs::file rap_file(rap_path);
@@ -1303,7 +1302,7 @@ bool SELFDecrypter::GetKeyFromRap(const char* content_id, u8* npdrm_key)
return false;
}
self_log.notice("Loading RAP file %s.rap", ci_str);
self_log.notice("Loading RAP file %s.rap", content_id);
if (rap_file.read(rap_key.data(), rap_key.size()) != rap_key.size())
{
+1 -1
View File
@@ -478,7 +478,7 @@ public:
bool DecryptData();
bool DecryptNPDRM(u8 *metadata, u32 metadata_size);
const NPD_HEADER* GetNPDHeader() const;
static bool GetKeyFromRap(const char *content_id, u8 *npdrm_key);
static bool GetKeyFromRap(std::string_view content_id, u8 *npdrm_key);
private:
template<typename EHdr, typename SHdr, typename PHdr>
+1 -1
View File
@@ -1313,7 +1313,7 @@ error_code cellGameDeleteGameData(vm::cptr<char> dirName)
{
cellGame.warning("cellGameDeleteGameData(dirName=%s)", dirName);
if (!dirName)
if (!dirName || sysutil_check_name_string(dirName.get_ptr(), 1, CELL_GAME_DIRNAME_SIZE) != 0)
{
return CELL_GAME_ERROR_PARAM;
}
@@ -17,7 +17,22 @@ bool music_selection_context::set(const CellMusicSelectionContext& in)
}
constexpr u32 pos = sizeof(magic);
hash = &in.data[pos];
const std::string_view data{&in.data[pos], sizeof(in.data) - pos};
const std::string_view new_hash = data.substr(0, data.find_first_of('\0'));
const bool is_valid_hash = !new_hash.empty() && std::all_of(new_hash.begin(), new_hash.end(), [](char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_';
});
if (!is_valid_hash)
{
cellMusicSelectionContext.error("Invalid music selection context hash, context = %s", context_to_hex(in));
return false;
}
hash = std::string{new_hash};
return load_playlist();
}
@@ -35,7 +50,7 @@ CellMusicSelectionContext music_selection_context::get() const
std::memset(out.data, 0, CELL_MUSIC_SELECTION_CONTEXT_SIZE);
std::memcpy(out.data, magic, sizeof(magic));
pos += sizeof(magic);
std::memcpy(&out.data[pos], hash.c_str(), hash.size());
std::memcpy(&out.data[pos], hash.data(), hash.size());
return out;
}
+3 -3
View File
@@ -1333,13 +1333,13 @@ error_code cellRecClose(s32 isDiscard)
rec.sink->stop(true);
}
if (fs::is_file(rec.param.filename))
if (const std::string local_path = vfs::get(rec.param.filename); fs::is_file(local_path))
{
cellRec.warning("cellRecClose: removing discarded recording '%s'", rec.param.filename);
if (!fs::remove_file(rec.param.filename))
if (!fs::remove_file(local_path))
{
cellRec.error("cellRecClose: failed to remove recording '%s'", rec.param.filename);
cellRec.error("cellRecClose: failed to remove recording '%s' (%s)", rec.param.filename, fs::g_tls_error);
}
}
}
+29 -1
View File
@@ -214,6 +214,16 @@ int check_filename(std::string_view file_path, bool disallow_system_files, bool
return 0;
}
static bool is_valid_dir_name(const std::string& dir_name)
{
if (dir_name.empty() || dir_name.find_first_of('\0') != umax)
{
return false;
}
return sysutil_check_name_string(dir_name.c_str(), 1, CELL_SAVEDATA_DIRNAME_SIZE) == 0;
}
static std::vector<SaveDataEntry> get_save_entries(const std::string& base_dir, const std::string& prefix)
{
std::vector<SaveDataEntry> save_entries;
@@ -251,6 +261,12 @@ static std::vector<SaveDataEntry> get_save_entries(const std::string& base_dir,
save_entry.subtitle = psf::get_string(psf, "SUB_TITLE");
save_entry.details = psf::get_string(psf, "DETAIL");
if (!is_valid_dir_name(save_entry.dirName))
{
cellSaveData.error("Savedata '%s' has an invalid SAVEDATA_DIRECTORY entry ('%s')", entry.name, save_entry.dirName);
continue;
}
for (const auto& entry2 : fs::dir(base_dir + entry.name))
{
if (entry2.is_directory || check_filename(vfs::unescape(entry2.name), false, true))
@@ -848,6 +864,12 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
save_entry2.subtitle = psf::get_string(psf, "SUB_TITLE");
save_entry2.details = psf::get_string(psf, "DETAIL");
if (!is_valid_dir_name(save_entry2.dirName))
{
cellSaveData.error("Savedata '%s' has an invalid SAVEDATA_DIRECTORY entry ('%s')", entry.name, save_entry2.dirName);
break;
}
for (const auto& entry2 : fs::dir(base_dir + entry.name))
{
if (entry2.is_directory || check_filename(vfs::unescape(entry2.name), false, true))
@@ -1468,6 +1490,12 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
save_entry.escaped = vfs::escape(save_entry.dirName);
}
if (!is_valid_dir_name(save_entry.dirName))
{
cellSaveData.error("savedata_op(): invalid savedata directory name ('%s')", save_entry.dirName);
return {CELL_SAVEDATA_ERROR_BROKEN, save_entry.dirName};
}
const std::string dir_path = base_dir + save_entry.escaped + "/";
const std::string old_path = base_dir + ".backup_" + save_entry.escaped + "/";
const std::string new_path = base_dir + ".working_" + save_entry.escaped + "/";
@@ -2215,7 +2243,7 @@ static NEVER_INLINE error_code savedata_get_list_item(vm::cptr<char> dirName, vm
return {CELL_SAVEDATA_ERROR_PARAM, "107"};
}
switch (sysutil_check_name_string(dirName.get_ptr(), 1, CELL_SAVEDATA_DIRLIST_MAX))
switch (sysutil_check_name_string(dirName.get_ptr(), 1, CELL_SAVEDATA_DIRNAME_SIZE))
{
case -1:
{
+1 -1
View File
@@ -699,7 +699,7 @@ error_code npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_pat
}
else
{
const std::string rap_file = rpcs3::utils::get_rap_file_path(npd.content_id);
const std::string rap_file = rpcs3::utils::get_rap_file_path(npd.get_content_id());
if (fs::file rap_fd{rap_file})
{
+1 -4
View File
@@ -460,7 +460,7 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
filename.remove_prefix(cell_fs_path.size());
const bool is_path = filename.starts_with("/"sv);
std::string mp_name = is_path ? std::string{get_path_root_and_trail(filename).first} : std::string(filename);
std::string mp_name = is_path ? "/" + std::string{get_path_root_and_trail(filename).first} : std::string(filename);
const auto check_mp = [&]()
{
@@ -521,9 +521,6 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
*vfs_path = g_cfg_vfs.get_dev_flash3();
else
*vfs_path = {};
if (is_path && !is_cell_fs_path && !vfs_path->empty())
vfs_path->append(filename.substr(mp_name.size()));
}
return result;
+2 -2
View File
@@ -246,8 +246,8 @@ public:
lv2_fs_object& operator=(const lv2_fs_object&) = delete;
// Get the device's root path (e.g. "/dev_hdd0") from a given path
// Cut the trail and return it in seccond argument
// Get the device's root path (e.g. "dev_hdd0") from a given path
// Cut the trail and return it in second argument
static std::pair<std::string_view, std::string> get_path_root_and_trail(std::string_view path);
// Get the device's root path (e.g. "/dev_hdd0") from a given path
+26 -1
View File
@@ -378,8 +378,27 @@ namespace rpcs3::utils
return file_list;
}
static bool is_valid_content_id(std::string_view content_id)
{
if (content_id.empty() || content_id.size() > 0x30)
{
return false;
}
return std::all_of(content_id.begin(), content_id.end(), [](char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_';
});
}
std::string get_rap_file_path(const std::string_view& rap)
{
if (!is_valid_content_id(rap))
{
sys_log.error("get_rap_file_path(): invalid content id '%s'", rap);
return {};
}
const std::string home_dir = get_hdd0_dir() + "home";
std::string rap_path;
@@ -402,6 +421,12 @@ namespace rpcs3::utils
std::string get_c00_unlock_edat_path(const std::string_view& content_id)
{
if (!is_valid_content_id(content_id))
{
sys_log.error("get_c00_unlock_edat_path(): invalid content id '%s'", content_id);
return {};
}
const std::string home_dir = get_hdd0_dir() + "home";
std::string edat_path;
@@ -448,7 +473,7 @@ namespace rpcs3::utils
return false;
}
std::string edat_content_id = npd.content_id;
std::string edat_content_id{npd.get_content_id()};
if (edat_content_id != content_id)
{
+21 -5
View File
@@ -1,11 +1,18 @@
#include "stdafx.h"
#include "Emu/VFS.h"
#include "Emu/System.h"
#include "TRP.h"
#include "Crypto/sha1.h"
#include "Utilities/StrUtil.h"
LOG_CHANNEL(trp_log, "Trophy");
static std::string get_entry_name(const TRPEntry& entry)
{
const std::string_view name{entry.name, sizeof(entry.name)};
return std::string{name.substr(0, name.find_first_of('\0'))};
}
TRPLoader::TRPLoader(const fs::file& f)
: m_file(f)
{
@@ -51,7 +58,16 @@ bool TRPLoader::Install(std::string_view dest, bool /*show*/)
}
// Create the file in the temporary directory
const std::string filename = temp + '/' + vfs::escape(entry.name);
const std::string entry_name = get_entry_name(entry);
const std::string filename = temp + '/' + vfs::escape(entry_name, true);
if (!Emu.IsPathInsideDir(filename, temp, false))
{
trp_log.error("Error extracting %s from TRP: target path '%s' would be extracted outside of '%s'", entry_name, filename, temp);
success = false;
break;
}
success = fs::write_file<true>(filename, fs::create + fs::excl, buffer);
if (!success)
{
@@ -160,7 +176,7 @@ bool TRPLoader::LoadHeader(bool show)
{
for (const auto& entry : m_entries)
{
trp_log.notice("TRP entry #%u: %s", &entry - m_entries.data(), entry.name);
trp_log.notice("TRP entry #%u: %s", &entry - m_entries.data(), get_entry_name(entry));
}
}
@@ -184,7 +200,7 @@ bool TRPLoader::ContainsEntry(std::string_view filename)
for (const TRPEntry& entry : m_entries)
{
if (entry.name == filename)
if (get_entry_name(entry) == filename)
{
return true;
}
@@ -202,7 +218,7 @@ void TRPLoader::RemoveEntry(std::string_view filename)
std::vector<TRPEntry>::iterator i = m_entries.begin();
while (i != m_entries.end())
{
if (i->name == filename)
if (get_entry_name(*i) == filename)
{
i = m_entries.erase(i);
}
@@ -222,7 +238,7 @@ void TRPLoader::RenameEntry(std::string_view oldname, std::string_view newname)
for (TRPEntry& entry : m_entries)
{
if (entry.name == oldname)
if (get_entry_name(entry) == oldname)
{
strcpy_trunc(entry.name, newname);
}