4949#include " util/sysinfo.hpp"
5050
5151#include < memory>
52- #include < regex>
5352#include < shared_mutex>
5453
5554#include " Utilities/JIT.h"
@@ -4412,16 +4411,30 @@ u32 Emulator::AddGamesFromDir(std::string path)
44124411
44134412 fmt::trim_back (path, fs::delim);
44144413
4414+ // Don't write "games.yml" on each added game: it is saved once, at the end of the scan.
4415+ // NOTE: this function is recursive, so the previous value is restored instead of being forced back to enabled,
4416+ // otherwise a nested scan would re-enable the write for the remaining part of the outer one
4417+ const bool save_on_dirty = m_games_config.is_save_on_dirty ();
44154418 m_games_config.set_save_on_dirty (false );
44164419
4420+ // A game was found on a path if it has just been added or if it was already registered
4421+ const auto game_found = [](game_boot_result error)
4422+ {
4423+ return error == game_boot_result::no_errors || error == game_boot_result::already_added;
4424+ };
4425+
44174426 // search for a game on the provided path first (game on ISO file or on folder type)
4418- if (const game_boot_result error = AddGame (path); error == game_boot_result::no_errors)
4427+ const game_boot_result path_error = AddGame (path);
4428+
4429+ if (path_error == game_boot_result::no_errors)
44194430 {
44204431 games_added++;
44214432 }
44224433
4423- // search for games on subfolders only if not nested inside a discovered game folder
4424- if (games_added == 0 )
4434+ // search for games on subfolders only if not nested inside a discovered game folder, otherwise the same title
4435+ // would be registered again through a different path (e.g. the root of a BD drive "E:/" is registered as a raw
4436+ // device, its subfolder "E:/PS3_GAME" would register it again as a disc folder)
4437+ if (!game_found (path_error))
44254438 {
44264439 std::vector<fs::dir_entry> entries;
44274440
@@ -4447,16 +4460,20 @@ u32 Emulator::AddGamesFromDir(std::string path)
44474460
44484461 const std::string dir_path = path + " /" + dir_entry.name ;
44494462
4450- if (!dir_entry.is_directory && !is_iso_file (dir_path))
4463+ // The outcome is handed over to "AddGame()" so that the ISO is not recognized twice: each check
4464+ // reads the volume descriptor, which is a physical read when the path points to an optical drive
4465+ const bool is_iso = !dir_entry.is_directory && is_iso_file (dir_path);
4466+
4467+ if (!dir_entry.is_directory && !is_iso)
44514468 {
44524469 continue ;
44534470 }
44544471
4455- if (const game_boot_result error = AddGame (dir_path); error == game_boot_result::no_errors)
4472+ if (const game_boot_result error = AddGame (dir_path, is_iso ); error == game_boot_result::no_errors)
44564473 {
44574474 games_added++;
44584475 }
4459- else if (g_cfg.misc .use_recursive_scan )
4476+ else if (! game_found (error) && g_cfg.misc .use_recursive_scan )
44604477 {
44614478 games_added += AddGamesFromDir (dir_path);
44624479 }
@@ -4471,24 +4488,25 @@ u32 Emulator::AddGamesFromDir(std::string path)
44714488 });
44724489 }
44734490
4474- m_games_config.set_save_on_dirty (true );
4491+ m_games_config.set_save_on_dirty (save_on_dirty );
44754492
4476- if (m_games_config.is_dirty () && !m_games_config.save ())
4493+ // Flush the changes only when the outermost scan is done
4494+ if (save_on_dirty && m_games_config.is_dirty () && !m_games_config.save ())
44774495 {
44784496 sys_log.error (" Failed to save games.yml after adding games" );
44794497 }
44804498
44814499 return games_added;
44824500}
44834501
4484- game_boot_result Emulator::AddGame (std::string path)
4502+ game_boot_result Emulator::AddGame (std::string path, bool is_iso )
44854503{
44864504 fmt::trim_back (path, fs::delim);
44874505
44884506 // Handle files directly
44894507 if (!fs::is_dir (path) || fs::get_optical_raw_device (path))
44904508 {
4491- return AddGameToYml (path);
4509+ return AddGameToYml (path, is_iso );
44924510 }
44934511
44944512 game_boot_result result = game_boot_result::nothing_to_boot;
@@ -4509,7 +4527,7 @@ game_boot_result Emulator::AddGame(std::string path)
45094527 continue ;
45104528 }
45114529
4512- if (entry.is_directory && std::regex_match (entry.name , std::regex ( " ^PS3_GM[[:digit:]]{2}$ " ) ))
4530+ if (entry.is_directory && rpcs3::utils::is_ps3_gm_dir_name (entry.name ))
45134531 {
45144532 const std::string elf = path + " /" + entry.name + " /USRDIR/EBOOT.BIN" ;
45154533
@@ -4530,7 +4548,7 @@ game_boot_result Emulator::AddGame(std::string path)
45304548 return result;
45314549}
45324550
4533- game_boot_result Emulator::AddGameToYml (std::string path)
4551+ game_boot_result Emulator::AddGameToYml (std::string path, bool is_iso )
45344552{
45354553 fmt::trim_back (path, fs::delim);
45364554
@@ -4559,7 +4577,9 @@ game_boot_result Emulator::AddGameToYml(std::string path)
45594577 }
45604578
45614579 std::unique_ptr<iso_archive> archive;
4562- if (is_iso_file (path))
4580+
4581+ // Skip the check if the caller already recognized the path as an ISO: it would read the volume descriptor again
4582+ if (is_iso || is_iso_file (path))
45634583 {
45644584 archive = std::make_unique<iso_archive>(path);
45654585
@@ -4826,7 +4846,7 @@ void Emulator::GetBdvdDir(std::string& bdvd_dir, std::string& sfb_dir, std::stri
48264846 continue ;
48274847 }
48284848
4829- if (dir_name == " PS3_GAME" sv || std::regex_match (dir_name. begin (), dir_name. end (), std::regex ( " ^PS3_GM[[:digit:]]{2}$ " ) ))
4849+ if (dir_name == " PS3_GAME" sv || rpcs3::utils::is_ps3_gm_dir_name (dir_name ))
48304850 {
48314851 if (IsValidSfb (parent_dir + " /PS3_DISC.SFB" ))
48324852 {
0 commit comments