mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-09 00:11:24 +12:00
cellGame: Truncate excess of characters in TITLE_ID
In cellGameDataCheckCreate
This commit is contained in:
parent
5a63271f0e
commit
27cad422b9
5 changed files with 118 additions and 28 deletions
|
@ -466,7 +466,7 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
|
||||||
// psf::assign(sfo, "CATEGORY", psf::string(3, "HG"));
|
// psf::assign(sfo, "CATEGORY", psf::string(3, "HG"));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// psf::assign(sfo, "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, setParam->titleId));
|
// psf::assign(sfo, "TITLE_ID", psf::string(TITLEID_SFO_ENTRY_SIZE, setParam->titleId));
|
||||||
// psf::assign(sfo, "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->title));
|
// psf::assign(sfo, "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->title));
|
||||||
// psf::assign(sfo, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
|
// psf::assign(sfo, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
|
||||||
// psf::assign(sfo, "PARENTAL_LEVEL", +setParam->parentalLevel);
|
// psf::assign(sfo, "PARENTAL_LEVEL", +setParam->parentalLevel);
|
||||||
|
@ -990,7 +990,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||||
psf::assign(sfo, "CATEGORY", psf::string(3, "GD"));
|
psf::assign(sfo, "CATEGORY", psf::string(3, "GD"));
|
||||||
}
|
}
|
||||||
|
|
||||||
psf::assign(sfo, "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, setParam->titleId));
|
psf::assign(sfo, "TITLE_ID", psf::string(TITLEID_SFO_ENTRY_SIZE, setParam->titleId, true));
|
||||||
psf::assign(sfo, "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->title));
|
psf::assign(sfo, "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->title));
|
||||||
psf::assign(sfo, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
|
psf::assign(sfo, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
|
||||||
psf::assign(sfo, "PARENTAL_LEVEL", +setParam->parentalLevel);
|
psf::assign(sfo, "PARENTAL_LEVEL", +setParam->parentalLevel);
|
||||||
|
@ -1005,6 +1005,14 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||||
psf::assign(sfo, fmt::format("TITLE_%02d", i), psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->titleLang[i]));
|
psf::assign(sfo, fmt::format("TITLE_%02d", i), psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->titleLang[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!psf::check_registry(sfo))
|
||||||
|
{
|
||||||
|
// This results in CELL_OK, broken SFO and CELL_GAMEDATA_ERROR_BROKEN on the next load
|
||||||
|
// Avoid creation for now
|
||||||
|
cellGame.error("Broken SFO paramters: %s", sfo);
|
||||||
|
return CELL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
fs::pending_file temp(vfs::get(dir + "/PARAM.SFO"));
|
fs::pending_file temp(vfs::get(dir + "/PARAM.SFO"));
|
||||||
temp.file.write(psf::save_object(sfo));
|
temp.file.write(psf::save_object(sfo));
|
||||||
ensure(temp.commit());
|
ensure(temp.commit());
|
||||||
|
@ -1125,7 +1133,7 @@ error_code cellGameCreateGameData(vm::ptr<CellGameSetInitParams> init, vm::ptr<c
|
||||||
perm.sfo =
|
perm.sfo =
|
||||||
{
|
{
|
||||||
{ "CATEGORY", psf::string(3, "GD") },
|
{ "CATEGORY", psf::string(3, "GD") },
|
||||||
{ "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, init->titleId) },
|
{ "TITLE_ID", psf::string(TITLEID_SFO_ENTRY_SIZE, init->titleId) },
|
||||||
{ "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, init->title) },
|
{ "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, init->title) },
|
||||||
{ "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, init->version) },
|
{ "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, init->version) },
|
||||||
};
|
};
|
||||||
|
|
|
@ -216,6 +216,11 @@ enum // old consts
|
||||||
CELL_DISCGAME_SYSP_TITLEID_SIZE=10,
|
CELL_DISCGAME_SYSP_TITLEID_SIZE=10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TITLEID_SFO_ENTRY_SIZE = 16, // This is the true length on PS3 (TODO: Fix in more places)
|
||||||
|
};
|
||||||
|
|
||||||
struct CellGameDataSystemFileParam
|
struct CellGameDataSystemFileParam
|
||||||
{
|
{
|
||||||
char title[CELL_GAMEDATA_SYSP_TITLE_SIZE];
|
char title[CELL_GAMEDATA_SYSP_TITLE_SIZE];
|
||||||
|
|
|
@ -1036,6 +1036,14 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
|
||||||
// Try to boot a game through game ID only
|
// Try to boot a game through game ID only
|
||||||
m_title_id = m_path.substr(("%RPCS3_GAMEID%:"sv).size());
|
m_title_id = m_path.substr(("%RPCS3_GAMEID%:"sv).size());
|
||||||
m_title_id = m_title_id.substr(0, m_title_id.find_first_of(fs::delim));
|
m_title_id = m_title_id.substr(0, m_title_id.find_first_of(fs::delim));
|
||||||
|
|
||||||
|
if (m_title_id.size() < 3 && m_title_id.find_first_not_of('.') == umax)
|
||||||
|
{
|
||||||
|
// Do not allow if TITLE_ID result in path redirection
|
||||||
|
sys_log.fatal("Game directory not found using GAMEID token. ('%s')", m_title_id);
|
||||||
|
return game_boot_result::invalid_file_or_folder;
|
||||||
|
}
|
||||||
|
|
||||||
std::string tail = m_path.substr(("%RPCS3_GAMEID%:"sv).size() + m_title_id.size());
|
std::string tail = m_path.substr(("%RPCS3_GAMEID%:"sv).size() + m_title_id.size());
|
||||||
|
|
||||||
if (tail.find_first_not_of(fs::delim) == umax)
|
if (tail.find_first_not_of(fs::delim) == umax)
|
||||||
|
|
|
@ -100,16 +100,21 @@ namespace psf
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
entry::entry(format type, u32 max_size, std::string_view value)
|
entry::entry(format type, u32 max_size, std::string_view value, bool allow_truncate) noexcept
|
||||||
: m_type(type)
|
: m_type(type)
|
||||||
, m_max_size(max_size)
|
, m_max_size(max_size)
|
||||||
, m_value_string(value)
|
, m_value_string(value)
|
||||||
{
|
{
|
||||||
ensure(type == format::string || type == format::array);
|
ensure(type == format::string || type == format::array);
|
||||||
ensure(max_size);
|
ensure(max_size > (type == format::string ? 1 : 0));
|
||||||
|
|
||||||
|
if (allow_truncate && value.size() > max(false))
|
||||||
|
{
|
||||||
|
m_value_string.resize(max(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entry::entry(u32 value)
|
entry::entry(u32 value) noexcept
|
||||||
: m_type(format::integer)
|
: m_type(format::integer)
|
||||||
, m_max_size(sizeof(u32))
|
, m_max_size(sizeof(u32))
|
||||||
, m_value_integer(value)
|
, m_value_integer(value)
|
||||||
|
@ -148,7 +153,7 @@ namespace psf
|
||||||
{
|
{
|
||||||
case format::string:
|
case format::string:
|
||||||
case format::array:
|
case format::array:
|
||||||
return std::min(m_max_size, ::narrow<u32>(m_value_string.size() + (m_type == format::string)));
|
return std::min(m_max_size, ::narrow<u32>(m_value_string.size() + (m_type == format::string ? 1 : 0)));
|
||||||
|
|
||||||
case format::integer:
|
case format::integer:
|
||||||
return sizeof(u32);
|
return sizeof(u32);
|
||||||
|
@ -157,6 +162,22 @@ namespace psf
|
||||||
fmt::throw_exception("Invalid format (0x%x)", m_type);
|
fmt::throw_exception("Invalid format (0x%x)", m_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool entry::is_valid() const
|
||||||
|
{
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case format::string:
|
||||||
|
case format::array:
|
||||||
|
return m_value_string.size() <= this->max(false);
|
||||||
|
|
||||||
|
case format::integer:
|
||||||
|
return true;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::throw_exception("Invalid format (0x%x)", m_type);
|
||||||
|
}
|
||||||
|
|
||||||
load_result_t load(const fs::file& stream, std::string_view filename)
|
load_result_t load(const fs::file& stream, std::string_view filename)
|
||||||
{
|
{
|
||||||
#define PSF_CHECK(cond, err) if (!static_cast<bool>(cond)) { if (error::err != error::stream) psf_log.error("Error loading PSF '%s': %s%s", filename, error::err, \
|
#define PSF_CHECK(cond, err) if (!static_cast<bool>(cond)) { if (error::err != error::stream) psf_log.error("Error loading PSF '%s': %s%s", filename, error::err, \
|
||||||
|
@ -251,14 +272,6 @@ namespace psf
|
||||||
PSF_CHECK(false, corrupt);
|
PSF_CHECK(false, corrupt);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto tid = get_string(pair.sfo, "TITLE_ID", "");
|
|
||||||
|
|
||||||
if (std::find_if(tid.begin(), tid.end(), [](char ch){ return !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); }) != tid.end())
|
|
||||||
{
|
|
||||||
psf_log.error("Invalid title ID ('%s')", tid);
|
|
||||||
PSF_CHECK(false, corrupt);
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef PSF_CHECK
|
#undef PSF_CHECK
|
||||||
return pair;
|
return pair;
|
||||||
}
|
}
|
||||||
|
@ -283,7 +296,7 @@ namespace psf
|
||||||
index.key_off = ::narrow<u32>(key_offset);
|
index.key_off = ::narrow<u32>(key_offset);
|
||||||
index.param_fmt = entry.second.type();
|
index.param_fmt = entry.second.type();
|
||||||
index.param_len = entry.second.size();
|
index.param_len = entry.second.size();
|
||||||
index.param_max = entry.second.max();
|
index.param_max = entry.second.max(true);
|
||||||
index.data_off = ::narrow<u32>(data_offset);
|
index.data_off = ::narrow<u32>(data_offset);
|
||||||
|
|
||||||
// Update offsets:
|
// Update offsets:
|
||||||
|
@ -322,7 +335,7 @@ namespace psf
|
||||||
for (const auto& entry : psf)
|
for (const auto& entry : psf)
|
||||||
{
|
{
|
||||||
const auto fmt = entry.second.type();
|
const auto fmt = entry.second.type();
|
||||||
const u32 max = entry.second.max();
|
const u32 max = entry.second.max(true);
|
||||||
|
|
||||||
if (fmt == format::integer && max == sizeof(u32))
|
if (fmt == format::integer && max == sizeof(u32))
|
||||||
{
|
{
|
||||||
|
@ -331,17 +344,17 @@ namespace psf
|
||||||
}
|
}
|
||||||
else if (fmt == format::string || fmt == format::array)
|
else if (fmt == format::string || fmt == format::array)
|
||||||
{
|
{
|
||||||
const std::string& value = entry.second.as_string();
|
std::string_view value = entry.second.as_string();
|
||||||
const usz size = std::min<usz>(max, value.size());
|
|
||||||
|
|
||||||
if (value.size() + (fmt == format::string) > max)
|
if (!entry.second.is_valid())
|
||||||
{
|
{
|
||||||
// TODO: check real limitations of PSF format
|
// TODO: check real limitations of PSF format
|
||||||
psf_log.error("Entry value shrinkage (key='%s', value='%s', size=0x%zx, max=0x%x)", entry.first, value, size, max);
|
psf_log.error("Entry value shrinkage (key='%s', value='%s', size=0x%zx, max=0x%x)", entry.first, value, value.size(), max);
|
||||||
|
value = value.substr(0, entry.second.max(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.write(value);
|
stream.write(value.data(), value.size());
|
||||||
stream.trunc(stream.seek(max - size, fs::seek_cur)); // Skip up to max_size
|
stream.trunc(stream.seek(max - value.size(), fs::seek_cur)); // Skip up to max_size
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -375,4 +388,44 @@ namespace psf
|
||||||
|
|
||||||
return found->second.as_integer();
|
return found->second.as_integer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool check_registry(const registry& psf, std::function<bool(bool ok, const std::string& key, const entry& value)> validate, u32 line, u32 col, const char* file, const char* func)
|
||||||
|
{
|
||||||
|
bool psf_ok = true;
|
||||||
|
|
||||||
|
for (const auto& [key, value] : psf)
|
||||||
|
{
|
||||||
|
bool entry_ok = value.is_valid();
|
||||||
|
|
||||||
|
if (validate)
|
||||||
|
{
|
||||||
|
// Validate against a custom condition as well (forward error)
|
||||||
|
if (!validate(entry_ok, key, value))
|
||||||
|
{
|
||||||
|
entry_ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry_ok)
|
||||||
|
{
|
||||||
|
if (value.type() == format::string)
|
||||||
|
{
|
||||||
|
psf_log.error("Entry '%s' is invalid: string='%s'.%s", key, value.as_string(), src_loc{line , col, file, func});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Better logging of other types
|
||||||
|
psf_log.error("Entry %s is invalid.%s", key, value.as_string(), src_loc{line , col, file, func});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry_ok)
|
||||||
|
{
|
||||||
|
// Do not break, run over all entries in order to report all errors
|
||||||
|
psf_ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return psf_ok;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,10 +55,10 @@ namespace psf
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Construct string entry, assign the value
|
// Construct string entry, assign the value
|
||||||
entry(format type, u32 max_size, std::string_view value);
|
entry(format type, u32 max_size, std::string_view value, bool allow_truncate = false) noexcept;
|
||||||
|
|
||||||
// Construct integer entry, assign the value
|
// Construct integer entry, assign the value
|
||||||
entry(u32 value);
|
entry(u32 value) noexcept;
|
||||||
|
|
||||||
~entry() = default;
|
~entry() = default;
|
||||||
|
|
||||||
|
@ -69,8 +69,9 @@ namespace psf
|
||||||
entry& operator =(u32 value);
|
entry& operator =(u32 value);
|
||||||
|
|
||||||
format type() const { return m_type; }
|
format type() const { return m_type; }
|
||||||
u32 max() const { return m_max_size; }
|
u32 max(bool with_nts) const { return m_max_size - (!with_nts && m_type == format::string ? 1 : 0); }
|
||||||
u32 size() const;
|
u32 size() const;
|
||||||
|
bool is_valid() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define PSF registry as a sorted map of entries:
|
// Define PSF registry as a sorted map of entries:
|
||||||
|
@ -102,6 +103,12 @@ namespace psf
|
||||||
// Get integer value or default value
|
// Get integer value or default value
|
||||||
u32 get_integer(const registry& psf, std::string_view key, u32 def = 0);
|
u32 get_integer(const registry& psf, std::string_view key, u32 def = 0);
|
||||||
|
|
||||||
|
bool check_registry(const registry& psf, std::function<bool(bool ok, const std::string& key, const entry& value)> validate = {},
|
||||||
|
u32 line = __builtin_LINE(),
|
||||||
|
u32 col = __builtin_COLUMN(),
|
||||||
|
const char* file = __builtin_FILE(),
|
||||||
|
const char* func = __builtin_FUNCTION());
|
||||||
|
|
||||||
// Assign new entry
|
// Assign new entry
|
||||||
inline void assign(registry& psf, std::string_view key, entry&& _entry)
|
inline void assign(registry& psf, std::string_view key, entry&& _entry)
|
||||||
{
|
{
|
||||||
|
@ -118,9 +125,18 @@ namespace psf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make string entry
|
// Make string entry
|
||||||
inline entry string(u32 max_size, std::string_view value)
|
inline entry string(u32 max_size, std::string_view value, bool allow_truncate = false)
|
||||||
{
|
{
|
||||||
return {format::string, max_size, value};
|
return {format::string, max_size, value, allow_truncate};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make string entry (from char[N])
|
||||||
|
template <usz CharN>
|
||||||
|
inline entry string(u32 max_size, char (&value_array)[CharN], bool allow_truncate = false)
|
||||||
|
{
|
||||||
|
std::string_view value{value_array, CharN};
|
||||||
|
value = value.substr(0, std::min<usz>(value.find_first_of('\0'), value.size()));
|
||||||
|
return string(CharN, value, allow_truncate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make array entry
|
// Make array entry
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue