mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-11 15:40:51 +00:00
cellGame: Truncate excess of characters in TITLE_ID
In cellGameDataCheckCreate
This commit is contained in:
parent
5a63271f0e
commit
27cad422b9
@ -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, "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, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
|
||||
// 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, "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, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
|
||||
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]));
|
||||
}
|
||||
|
||||
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"));
|
||||
temp.file.write(psf::save_object(sfo));
|
||||
ensure(temp.commit());
|
||||
@ -1125,7 +1133,7 @@ error_code cellGameCreateGameData(vm::ptr<CellGameSetInitParams> init, vm::ptr<c
|
||||
perm.sfo =
|
||||
{
|
||||
{ "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) },
|
||||
{ "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, init->version) },
|
||||
};
|
||||
|
@ -216,6 +216,11 @@ enum // old consts
|
||||
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
|
||||
{
|
||||
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
|
||||
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));
|
||||
|
||||
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());
|
||||
|
||||
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_max_size(max_size)
|
||||
, m_value_string(value)
|
||||
{
|
||||
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_max_size(sizeof(u32))
|
||||
, m_value_integer(value)
|
||||
@ -148,7 +153,7 @@ namespace psf
|
||||
{
|
||||
case format::string:
|
||||
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:
|
||||
return sizeof(u32);
|
||||
@ -157,6 +162,22 @@ namespace psf
|
||||
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)
|
||||
{
|
||||
#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);
|
||||
}
|
||||
|
||||
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
|
||||
return pair;
|
||||
}
|
||||
@ -283,7 +296,7 @@ namespace psf
|
||||
index.key_off = ::narrow<u32>(key_offset);
|
||||
index.param_fmt = entry.second.type();
|
||||
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);
|
||||
|
||||
// Update offsets:
|
||||
@ -322,7 +335,7 @@ namespace psf
|
||||
for (const auto& entry : psf)
|
||||
{
|
||||
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))
|
||||
{
|
||||
@ -331,17 +344,17 @@ namespace psf
|
||||
}
|
||||
else if (fmt == format::string || fmt == format::array)
|
||||
{
|
||||
const std::string& value = entry.second.as_string();
|
||||
const usz size = std::min<usz>(max, value.size());
|
||||
std::string_view value = entry.second.as_string();
|
||||
|
||||
if (value.size() + (fmt == format::string) > max)
|
||||
if (!entry.second.is_valid())
|
||||
{
|
||||
// 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.trunc(stream.seek(max - size, fs::seek_cur)); // Skip up to max_size
|
||||
stream.write(value.data(), value.size());
|
||||
stream.trunc(stream.seek(max - value.size(), fs::seek_cur)); // Skip up to max_size
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -375,4 +388,44 @@ namespace psf
|
||||
|
||||
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:
|
||||
// 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
|
||||
entry(u32 value);
|
||||
entry(u32 value) noexcept;
|
||||
|
||||
~entry() = default;
|
||||
|
||||
@ -69,8 +69,9 @@ namespace psf
|
||||
entry& operator =(u32 value);
|
||||
|
||||
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;
|
||||
bool is_valid() const;
|
||||
};
|
||||
|
||||
// Define PSF registry as a sorted map of entries:
|
||||
@ -102,6 +103,12 @@ namespace psf
|
||||
// Get integer value or default value
|
||||
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
|
||||
inline void assign(registry& psf, std::string_view key, entry&& _entry)
|
||||
{
|
||||
@ -118,9 +125,18 @@ namespace psf
|
||||
}
|
||||
|
||||
// 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user