Big picture: recursive folder scanning (#5223)

* add recursive scanning - force depth 2 for now, make configurable later

* make setting configurable

* match item widths

* correctly load language as specific, fix profile button naming with multiple same titleid entries

* catch fs errors, while ensuring all other items are scanned
This commit is contained in:
rainmakerv2 authored and GitHub committed 2026-10-03 08:51:03 +03:00
1 parent ead912cf95
commit 8cd2ea8c41
4 files changed
+139 -57

No files matched your search

+5 -2
View File
@@ -199,6 +199,7 @@ struct GeneralSettings {
Setting<bool> show_fps_counter{false};
Setting<int> console_language{1};
Setting<int> big_picture_scale{1000};
Setting<int> big_picture_folder_depth{2};
Setting<std::string> shadnet_server{"srv.shadps4.net:31313"};
Setting<std::string> shadnet_webapi_server{"http://srv.shadps4.net:31315"};
Setting<std::string> signaling_info{};
@@ -241,8 +242,9 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GeneralSettings, install_dirs, addon_install_
trophy_popup_disabled, trophy_notification_duration, show_splash,
trophy_notification_side, connected_to_network,
discord_rpc_enabled, show_fps_counter, console_language,
big_picture_scale, shadnet_server, shadnet_webapi_server,
signaling_info, enable_upnp, redzone_patches)
big_picture_scale, big_picture_folder_depth, shadnet_server,
shadnet_webapi_server, signaling_info, enable_upnp,
redzone_patches)
// -------------------------------
// Log settings
@@ -659,6 +661,7 @@ public:
SETTING_FORWARD_BOOL(m_general, ShowFpsCounter, show_fps_counter)
SETTING_FORWARD(m_general, ConsoleLanguage, console_language)
SETTING_FORWARD(m_general, BigPictureScale, big_picture_scale)
SETTING_FORWARD(m_general, BigPictureFolderDepth, big_picture_folder_depth)
SETTING_FORWARD(m_general, ShadNetServer, shadnet_server)
SETTING_FORWARD(m_general, ShadNetWebApiServer, shadnet_webapi_server)
SETTING_FORWARD(m_general, SignalingInfo, signaling_info)
+97 -51
View File
@@ -162,62 +162,108 @@ SDL_Texture* LoadSdlTextureDataFromFile(std::filesystem::path filePath) {
return LoadSdlTextureData(data);
}
void scanFolder(std::vector<IconInfo>& icons, std::filesystem::path folderPath, int depth) {
if (depth > EmulatorSettings.GetBigPictureFolderDepth()) {
return;
}
std::error_code ec;
std::filesystem::directory_iterator it(folderPath, std::filesystem::directory_options::none,
ec);
std::filesystem::directory_iterator end;
if (ec) {
LOG_ERROR(ImGui, "Cannot open folder: {} due to filesystem error: {}", folderPath.string(),
ec.message());
return;
}
while (it != end) {
// For access errors getting it.path may not be safe, so log parent folder
if (ec) {
LOG_ERROR(ImGui, "Cannot access item inside folder {} due to error: {}",
folderPath.string(), ec.message());
ec.clear();
it.increment(ec);
continue;
}
const auto& entry = *it;
bool isEntryDir = std::filesystem::is_directory(entry, ec);
if (ec) {
LOG_ERROR(ImGui, "Skipping file/subfolder {} due to filesystem error: {}",
entry.path().string(), ec.message());
ec.clear();
it.increment(ec);
continue;
}
if (isEntryDir && !std::filesystem::exists(entry.path() / "eboot.bin", ec)) {
scanFolder(icons, entry.path(), depth + 1);
}
std::string pathstring = entry.path().filename().string();
if (pathstring.ends_with("-UPDATE") || pathstring.ends_with("-patch") ||
(!isEntryDir && !Core::FileSys::IsZArchiveFile(entry))) {
it.increment(ec);
continue;
}
if (Core::FileSys::IsZArchiveFile(entry)) {
size_t start = pathstring.length() - 3;
for (size_t i = start; i < pathstring.length(); ++i) {
pathstring[i] =
static_cast<char>(std::tolower(static_cast<unsigned char>(pathstring[i])));
}
if (pathstring.ends_with("-UPDATE.zar") || pathstring.ends_with("-patch.zar")) {
it.increment(ec);
continue;
}
}
IconInfo icon;
PSF psf;
const std::string sfoFileName = "param.sfo";
std::filesystem::path sfoPath = UpdateChecker(sfoFileName, entry.path());
if (std::filesystem::exists(sfoPath, ec) && psf.Open(sfoPath)) {
if (const auto title = psf.GetString("TITLE"); title.has_value()) {
icon.title = *title;
}
if (const auto title_id = psf.GetString("TITLE_ID"); title_id.has_value()) {
icon.serial = *title_id;
}
} else {
it.increment(ec);
continue;
}
const std::string iconFileName = "icon0.png";
std::filesystem::path iconPath = UpdateChecker(iconFileName, entry.path());
SDL_Texture* texture = LoadSdlTextureDataFromFile(iconPath);
icon.textureId = ImTextureID(texture);
icon.ebootPath = entry.path() / "eboot.bin";
if (Core::FileSys::IsZArchiveFile(entry.path())) {
icon.ebootPath = entry.path();
}
icon.focusState = false;
icons.push_back(icon);
it.increment(ec);
}
}
void GetGameIconInfo(std::vector<IconInfo>& icons) {
icons.clear();
for (const auto& installLoc : EmulatorSettings.GetAllGameInstallDirs()) {
if (installLoc.enabled && std::filesystem::exists(installLoc.path)) {
for (const auto& entry : std::filesystem::directory_iterator(installLoc.path)) {
std::string pathstring = entry.path().filename().string();
if (pathstring.ends_with("-UPDATE") || pathstring.ends_with("-patch") ||
(!entry.is_directory() && !Core::FileSys::IsZArchiveFile(entry))) {
continue;
}
if (Core::FileSys::IsZArchiveFile(entry)) {
size_t start = pathstring.length() - 3;
for (size_t i = start; i < pathstring.length(); ++i) {
pathstring[i] = static_cast<char>(
std::tolower(static_cast<unsigned char>(pathstring[i])));
}
if (pathstring.ends_with("-UPDATE.zar") || pathstring.ends_with("-patch.zar")) {
continue;
}
}
IconInfo icon;
PSF psf;
const std::string sfoFileName = "param.sfo";
std::filesystem::path sfoPath = UpdateChecker(sfoFileName, entry.path());
if (std::filesystem::exists(sfoPath) && psf.Open(sfoPath)) {
if (const auto title = psf.GetString("TITLE"); title.has_value()) {
icon.title = *title;
}
if (const auto title_id = psf.GetString("TITLE_ID"); title_id.has_value()) {
icon.serial = *title_id;
}
} else {
continue;
}
const std::string iconFileName = "icon0.png";
std::filesystem::path iconPath = UpdateChecker(iconFileName, entry.path());
SDL_Texture* texture = LoadSdlTextureDataFromFile(iconPath);
icon.textureId = ImTextureID(texture);
icon.ebootPath = entry.path() / "eboot.bin";
if (Core::FileSys::IsZArchiveFile(entry.path())) {
icon.ebootPath = entry.path();
}
icon.focusState = false;
icons.push_back(icon);
}
scanFolder(icons, installLoc.path, 0);
}
}
@@ -32,8 +32,14 @@ int SettingsWindow::GetComboIndex(std::string selection, std::vector<std::string
}
void SettingsWindow::LoadSettings(std::string profile) {
// Global-only settings
EmulatorSettings.Load();
scanDepthSetting = EmulatorSettings.GetBigPictureFolderDepth();
const bool isSpecific = currentProfile != "Global";
isSpecific ? EmulatorSettings.Load(profile) : EmulatorSettings.Load();
if (isSpecific) {
EmulatorSettings.Load(profile);
}
/////////// General Tab
int languageIndex = EmulatorSettings.GetConsoleLanguage();
@@ -43,7 +49,6 @@ void SettingsWindow::LoadSettings(std::string profile) {
language = key;
}
}
consoleLanguageSetting = GetComboIndex(language, languageOptions);
volumeSetting = EmulatorSettings.GetVolumeSlider();
showSplashSetting = EmulatorSettings.IsShowSplash();
@@ -513,8 +518,10 @@ void SettingsWindow::DrawProfileSelector() {
}
ImGui::TableNextColumn();
std::string profileLabel =
i == 0 ? "Global" : profileIcons[i].serial + " - " + profileIcons[i].title;
std::string profileLabel = i == 0
? "Global"
: profileIcons[i].serial + " - " +
profileIcons[i].title + "##" + std::to_string(i);
if (ImGui::Button(profileLabel.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0.0f))) {
@@ -595,6 +602,29 @@ void SettingsWindow::DrawGameFolderManager() {
ImGui::SetNextWindowSize(viewport->Size);
}
float buttonWidth = ImGui::GetItemRectSize().x;
ImGui::PushItemWidth(buttonWidth);
if (ImGui::SliderInt("Folder Scan Depth", &scanDepthSetting, 0, 5)) {
std::string profile;
const bool isGlobal = currentProfile == "Global";
if (!isGlobal) {
profile = currentProfile.substr(0, 9);
EmulatorSettings.Load();
}
EmulatorSettings.SetBigPictureFolderDepth(scanDepthSetting);
EmulatorSettings.Save();
if (!isGlobal) {
EmulatorSettings.Load(profile);
}
if (!isGameRunning) {
GetProfileInfo();
}
}
ImGui::PopItemWidth();
if (ImGuiFileDialog::Instance()->Display("OpenFolder", child_flags | ImGuiWindowFlags_NoMove)) {
if (ImGuiFileDialog::Instance()->IsOk()) {
GameInstallDir dir;
@@ -166,6 +166,9 @@ private:
bool pipelineCacheArchiveSetting;
int extraDmemSetting;
int vblankFrequencySetting;
// Folders tab
int scanDepthSetting;
};
} // namespace ImGuiEmuSettings