ISO: mark archive as invalid on error and add sanity checks

This commit is contained in:
Megamouse
2026-08-10 14:45:00 +02:00
parent 0230580a88
commit d6d5c60823
7 changed files with 74 additions and 16 deletions
+6
View File
@@ -4473,6 +4473,12 @@ game_boot_result Emulator::AddGameToYml(std::string path)
if (is_iso_file(path))
{
archive = std::make_unique<iso_archive>(path);
if (!archive->is_valid())
{
sys_log.error("Failed to load ISO.");
return game_boot_result::invalid_file_or_folder;
}
}
// Load PARAM.SFO
+41 -8
View File
@@ -919,15 +919,20 @@ static std::optional<iso_fs_metadata> iso_read_directory_entry(fs::file& entry,
};
}
static void iso_form_hierarchy(fs::file& file, iso_fs_node& node, bool use_ucs2_decoding = false, const std::string& parent_path = "")
static bool iso_form_hierarchy(fs::file& file, iso_fs_node& node, bool use_ucs2_decoding = false, const std::string& parent_path = "")
{
if (!node.metadata.is_directory)
{
return;
return !parent_path.empty();
}
const std::string node_path = parent_path + "/" + node.metadata.name;
ensure(parent_path.empty() || Emu.IsPathInsideDir(node_path, parent_path, false));
if (!parent_path.empty() && !Emu.IsPathInsideDir(node_path, parent_path, false))
{
iso_log.error("iso_archive::iso_form_hierarchy: node path outside of parent (parent_path='%s', node_path='%s')", parent_path, node_path);
return false;
}
std::vector<usz> multi_extent_node_indices;
@@ -986,9 +991,14 @@ static void iso_form_hierarchy(fs::file& file, iso_fs_node& node, bool use_ucs2_
{
if (child_node->metadata.name != "." && child_node->metadata.name != "..")
{
iso_form_hierarchy(file, *child_node, use_ucs2_decoding, node_path);
if (!iso_form_hierarchy(file, *child_node, use_ucs2_decoding, node_path))
{
return false;
}
}
}
return true;
}
u64 iso_fs_metadata::size() const
@@ -1012,8 +1022,8 @@ iso_archive::iso_archive(const std::string& path)
if (!is_iso_file(m_path))
{
// Not ISO... TODO: throw something?
iso_log.error("iso_archive: Failed to recognize ISO file: '%s'", path);
invalidate();
return;
}
@@ -1054,24 +1064,31 @@ iso_archive::iso_archive(const std::string& path)
if (descriptor_type != 255)
{
iso_log.error("iso_archive: Corrupt ISO file '%s': Volume Descriptor Set Terminator not found", path);
invalidate();
return;
}
iso_form_hierarchy(iso_file, m_root, use_ucs2_decoding);
if (!iso_form_hierarchy(iso_file, m_root, use_ucs2_decoding))
{
iso_log.error("iso_archive: Corrupt ISO file '%s': Failed to form hierarchy", path);
invalidate();
return;
}
// Only when the archive object is fully set, we can finally initialize the decryption object needing the archive object
m_dec = std::make_shared<iso_file_decryption>();
if (!m_dec->init(m_path, this))
{
// TODO: throw something?
iso_log.error("iso_archive: Corrupt ISO file '%s': Decryption failed", path);
invalidate();
return;
}
}
iso_fs_node* iso_archive::retrieve(const std::string& passed_path)
{
if (passed_path.empty())
if (passed_path.empty() || !is_valid())
{
return nullptr;
}
@@ -1146,6 +1163,17 @@ iso_fs_node* iso_archive::retrieve(const std::string& passed_path)
return search_stack.top();
}
void iso_archive::invalidate()
{
m_root = {};
m_dec.reset();
}
bool iso_archive::is_valid() const
{
return !m_root.metadata.name.empty();
}
bool iso_archive::exists(const std::string& path)
{
return retrieve(path) != nullptr;
@@ -1165,6 +1193,11 @@ bool iso_archive::is_file(const std::string& path)
std::unique_ptr<fs::file_base> iso_archive::get_iso_file(const std::string& path, bs_t<fs::open_mode> mode, const iso_fs_node& node)
{
if (!is_valid())
{
return nullptr;
}
if (m_dec->get_enc_type() == iso_encryption_type::NONE)
{
return std::make_unique<iso_file>(path, mode, node);
+3
View File
@@ -166,6 +166,8 @@ public:
class iso_archive
{
private:
void invalidate();
std::string m_path;
iso_fs_node m_root {};
std::shared_ptr<iso_file_decryption> m_dec;
@@ -177,6 +179,7 @@ public:
const iso_fs_node& root() const { return m_root; }
iso_fs_node* retrieve(const std::string& path);
bool is_valid() const;
bool exists(const std::string& path);
bool is_file(const std::string& path);
+9 -4
View File
@@ -571,6 +571,7 @@ void game_list_frame::OnParsingFinished()
if (is_raw_device || !iso_cache::load(dir_or_elf, iso_cache_key, cache_entry))
{
archive = std::make_unique<iso_archive>(dir_or_elf);
if (!archive->is_valid()) return;
}
// Track this ISO path for cache cleanup after scan completes.
@@ -609,6 +610,8 @@ void game_list_frame::OnParsingFinished()
{
game_list_log.warning("Cached psf for iso not valid: '%s'", game.info.path);
archive = std::make_unique<iso_archive>(dir_or_elf);
if (!archive->is_valid()) return;
cache_entry = {}; // Reset so the cache gets rewritten after scan.
psf = {};
}
@@ -768,11 +771,11 @@ void game_list_frame::OnParsingFinished()
if (game.icon_in_archive)
{
auto icon_file = archive->open(game.info.icon_path);
const auto icon_size = icon_file->size();
if (icon_size > 0)
if (icon_file && icon_file->size() > 0)
{
cache_entry.icon_data.resize(icon_size);
icon_file->read(cache_entry.icon_data.data(), icon_size);
cache_entry.icon_data.resize(icon_file->size());
icon_file->read(cache_entry.icon_data.data(), icon_file->size());
}
}
@@ -883,6 +886,8 @@ void game_list_frame::OnParsingFinished()
}
iso_archive archive(entry.path);
if (!archive.is_valid()) return;
const iso_fs_node& root = archive.root();
const std::regex ps3_gm_regex("^PS3_GM[[:digit:]]{2}$");
+2
View File
@@ -729,6 +729,8 @@ namespace gui
if (!archive.exists(icon_path)) return false;
auto icon_file = archive.open(icon_path);
if (!icon_file) return false;
const auto icon_size = icon_file->size();
if (icon_size == 0) return false;
+7 -4
View File
@@ -113,6 +113,8 @@ void qt_video_source::init_movie()
{
iso_archive archive(m_iso_path);
auto movie_file = archive.open(m_video_path.toStdString());
if (!movie_file) return;
const auto movie_size = movie_file->size();
if (movie_size == 0) return;
@@ -156,11 +158,10 @@ void qt_video_source::init_movie()
{
iso_archive archive(m_iso_path);
auto movie_file = archive.open(m_video_path.toStdString());
if (!movie_file) return;
const auto movie_size = movie_file->size();
if (movie_size == 0)
{
return;
}
if (movie_size == 0) return;
m_video_data = QByteArray(movie_size, 0);
movie_file->read(m_video_data.data(), movie_size);
@@ -280,6 +281,8 @@ void qt_video_source::start_audio()
{
iso_archive archive(m_iso_path);
auto audio_file = archive.open(m_audio_path.toStdString());
if (!audio_file) return;
const auto audio_size = audio_file->size();
if (audio_size == 0) return;
+6
View File
@@ -500,6 +500,12 @@ namespace gui::utils
{
gameid_token_value = game->info.serial;
archive = std::make_shared<iso_archive>(game->info.path);
if (!archive->is_valid())
{
sys_log.error("Failed to create shortcut path '%s' (Failed to load ISO)", game->info.path);
return false;
}
}
else if (is_disc_without_patch || is_hdd_game_with_different_foldername || is_ps1_game_with_different_foldername)
{