mirror of
https://git.citron-emu.org/Citron/Citron.git
synced 2025-01-22 16:46:59 +01:00
refactor: Improve game list scanning with std::filesystem
Refactors the game list scanning code to use std::filesystem instead of custom directory iteration functions. Main changes include: - Replace Common::FS directory iteration with std::filesystem iterators - Split game file processing logic into separate ProcessGameFile method - Improve error handling with try-catch for filesystem operations - Simplify control NCA metadata extraction - Use more modern C++ features and cleaner code organization This change should improve maintainability and reliability of the game scanning system while potentially offering better performance through native filesystem APIs.
This commit is contained in:
parent
3100d13fc0
commit
60cb826e93
2 changed files with 99 additions and 96 deletions
|
@ -335,112 +335,112 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
|
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
|
||||||
GameListDir* parent_dir) {
|
GameListDir* parent_dir) {
|
||||||
const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool {
|
// Use std::filesystem for native directory iteration
|
||||||
if (stop_requested) {
|
const std::filesystem::path root_path(dir_path);
|
||||||
// Breaks the callback loop.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto physical_name = Common::FS::PathToUTF8String(path);
|
try {
|
||||||
const auto is_dir = Common::FS::IsDir(path);
|
if (deep_scan) {
|
||||||
|
// Use recursive directory iterator for deep scanning
|
||||||
if (!is_dir &&
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(root_path)) {
|
||||||
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
|
if (stop_requested) {
|
||||||
const auto file = vfs->OpenFile(physical_name, FileSys::OpenMode::Read);
|
return;
|
||||||
if (!file) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto loader = Loader::GetLoader(system, file);
|
|
||||||
if (!loader) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto file_type = loader->GetFileType();
|
|
||||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 program_id = 0;
|
|
||||||
const auto res2 = loader->ReadProgramId(program_id);
|
|
||||||
|
|
||||||
if (target == ScanTarget::FillManualContentProvider) {
|
|
||||||
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
|
|
||||||
provider->AddEntry(FileSys::TitleType::Application,
|
|
||||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
|
||||||
program_id, file);
|
|
||||||
} else if (res2 == Loader::ResultStatus::Success &&
|
|
||||||
(file_type == Loader::FileType::XCI ||
|
|
||||||
file_type == Loader::FileType::NSP)) {
|
|
||||||
const auto nsp = file_type == Loader::FileType::NSP
|
|
||||||
? std::make_shared<FileSys::NSP>(file)
|
|
||||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
|
||||||
for (const auto& title : nsp->GetNCAs()) {
|
|
||||||
for (const auto& entry : title.second) {
|
|
||||||
provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
|
||||||
entry.second->GetBaseFile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
std::vector<u64> program_ids;
|
|
||||||
loader->ReadProgramIds(program_ids);
|
|
||||||
|
|
||||||
if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 &&
|
const auto physical_name = entry.path().string();
|
||||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
const bool is_dir = entry.is_directory();
|
||||||
for (const auto id : program_ids) {
|
|
||||||
loader = Loader::GetLoader(system, file, id);
|
|
||||||
if (!loader) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> icon;
|
if (!is_dir && (HasSupportedFileExtension(physical_name) ||
|
||||||
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
|
IsExtractedNCAMain(physical_name))) {
|
||||||
|
ProcessGameFile(physical_name, target, parent_dir);
|
||||||
std::string name = " ";
|
} else if (is_dir) {
|
||||||
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
|
watch_list.append(QString::fromStdString(physical_name));
|
||||||
|
}
|
||||||
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
|
}
|
||||||
system.GetContentProvider()};
|
} else {
|
||||||
|
// Use regular directory iterator for shallow scanning
|
||||||
auto entry = MakeGameListEntry(
|
for (const auto& entry : std::filesystem::directory_iterator(root_path)) {
|
||||||
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
if (stop_requested) {
|
||||||
id, compatibility_list, play_time_manager, patch);
|
return;
|
||||||
|
}
|
||||||
RecordEvent(
|
|
||||||
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
const auto physical_name = entry.path().string();
|
||||||
}
|
const bool is_dir = entry.is_directory();
|
||||||
} else {
|
|
||||||
std::vector<u8> icon;
|
if (!is_dir && (HasSupportedFileExtension(physical_name) ||
|
||||||
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
|
IsExtractedNCAMain(physical_name))) {
|
||||||
|
ProcessGameFile(physical_name, target, parent_dir);
|
||||||
std::string name = " ";
|
} else if (is_dir) {
|
||||||
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
|
watch_list.append(QString::fromStdString(physical_name));
|
||||||
|
|
||||||
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
|
|
||||||
system.GetContentProvider()};
|
|
||||||
|
|
||||||
auto entry = MakeGameListEntry(
|
|
||||||
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
|
||||||
program_id, compatibility_list, play_time_manager, patch);
|
|
||||||
|
|
||||||
RecordEvent(
|
|
||||||
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (is_dir) {
|
|
||||||
watch_list.append(QString::fromStdString(physical_name));
|
|
||||||
}
|
}
|
||||||
|
} catch (const std::filesystem::filesystem_error& e) {
|
||||||
|
LOG_ERROR(Frontend, "Error scanning directory {}: {}", dir_path, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
void GameListWorker::ProcessGameFile(const std::string& physical_name, ScanTarget target,
|
||||||
};
|
GameListDir* parent_dir) {
|
||||||
|
const auto file = vfs->OpenFile(physical_name, FileSys::OpenMode::Read);
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (deep_scan) {
|
auto loader = Loader::GetLoader(system, file);
|
||||||
Common::FS::IterateDirEntriesRecursively(dir_path, callback,
|
if (!loader) {
|
||||||
Common::FS::DirEntryFilter::All);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto file_type = loader->GetFileType();
|
||||||
|
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 program_id = 0;
|
||||||
|
if (loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target == ScanTarget::FillManualContentProvider) {
|
||||||
|
if (file_type == Loader::FileType::NCA) {
|
||||||
|
provider->AddEntry(FileSys::TitleType::Application,
|
||||||
|
FileSys::GetCRTypeFromNCAType(FileSys::NCAContentType::Program),
|
||||||
|
program_id,
|
||||||
|
std::static_pointer_cast<FileSys::VfsFile>(file));
|
||||||
|
} else if (file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI) {
|
||||||
|
const auto nsp = file_type == Loader::FileType::NSP
|
||||||
|
? std::make_shared<FileSys::NSP>(file)
|
||||||
|
: std::make_shared<FileSys::XCI>(file)
|
||||||
|
->GetSecurePartitionNSP();
|
||||||
|
for (const auto& title : nsp->GetNCAs()) {
|
||||||
|
for (const auto& entry : title.second) {
|
||||||
|
provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||||
|
entry.second->GetBaseFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Common::FS::IterateDirEntries(dir_path, callback, Common::FS::DirEntryFilter::File);
|
std::vector<u8> icon;
|
||||||
|
std::string name;
|
||||||
|
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
|
||||||
|
system.GetContentProvider()};
|
||||||
|
|
||||||
|
const auto control = system.GetContentProvider().GetEntry(program_id, FileSys::ContentRecordType::Control);
|
||||||
|
if (control != nullptr) {
|
||||||
|
GetMetadataFromControlNCA(patch, *control, icon, name);
|
||||||
|
} else {
|
||||||
|
std::tie(icon, name) = std::make_pair(std::vector<u8>{}, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loader->ReadProgramId(program_id) == Loader::ResultStatus::Success) {
|
||||||
|
auto entry = MakeGameListEntry(physical_name, name, file->GetSize(), icon, *loader,
|
||||||
|
program_id, compatibility_list, play_time_manager, patch);
|
||||||
|
|
||||||
|
RecordEvent([entry, parent_dir](GameList* game_list) {
|
||||||
|
game_list->AddEntry(entry, parent_dir);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,9 @@ private:
|
||||||
void ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
|
void ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
|
||||||
GameListDir* parent_dir);
|
GameListDir* parent_dir);
|
||||||
|
|
||||||
|
void ProcessGameFile(const std::string& physical_name, ScanTarget target,
|
||||||
|
GameListDir* parent_dir);
|
||||||
|
|
||||||
std::shared_ptr<FileSys::VfsFilesystem> vfs;
|
std::shared_ptr<FileSys::VfsFilesystem> vfs;
|
||||||
FileSys::ManualContentProvider* provider;
|
FileSys::ManualContentProvider* provider;
|
||||||
QVector<UISettings::GameDir>& game_dirs;
|
QVector<UISettings::GameDir>& game_dirs;
|
||||||
|
|
Loading…
Reference in a new issue