mirror of
https://git.citron-emu.org/Citron/Citron.git
synced 2025-01-22 08:36:32 +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,
|
||||
GameListDir* parent_dir) {
|
||||
const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool {
|
||||
if (stop_requested) {
|
||||
// Breaks the callback loop.
|
||||
return false;
|
||||
}
|
||||
GameListDir* parent_dir) {
|
||||
// Use std::filesystem for native directory iteration
|
||||
const std::filesystem::path root_path(dir_path);
|
||||
|
||||
const auto physical_name = Common::FS::PathToUTF8String(path);
|
||||
const auto is_dir = Common::FS::IsDir(path);
|
||||
|
||||
if (!is_dir &&
|
||||
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
|
||||
const auto file = vfs->OpenFile(physical_name, FileSys::OpenMode::Read);
|
||||
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());
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (deep_scan) {
|
||||
// Use recursive directory iterator for deep scanning
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(root_path)) {
|
||||
if (stop_requested) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
std::vector<u64> program_ids;
|
||||
loader->ReadProgramIds(program_ids);
|
||||
|
||||
if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 &&
|
||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||
for (const auto id : program_ids) {
|
||||
loader = Loader::GetLoader(system, file, id);
|
||||
if (!loader) {
|
||||
continue;
|
||||
}
|
||||
const auto physical_name = entry.path().string();
|
||||
const bool is_dir = entry.is_directory();
|
||||
|
||||
std::vector<u8> icon;
|
||||
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
|
||||
|
||||
std::string name = " ";
|
||||
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
|
||||
|
||||
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
auto entry = MakeGameListEntry(
|
||||
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
||||
id, compatibility_list, play_time_manager, patch);
|
||||
|
||||
RecordEvent(
|
||||
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||
}
|
||||
} else {
|
||||
std::vector<u8> icon;
|
||||
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
|
||||
|
||||
std::string name = " ";
|
||||
[[maybe_unused]] const auto res3 = loader->ReadTitle(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); });
|
||||
if (!is_dir && (HasSupportedFileExtension(physical_name) ||
|
||||
IsExtractedNCAMain(physical_name))) {
|
||||
ProcessGameFile(physical_name, target, parent_dir);
|
||||
} else if (is_dir) {
|
||||
watch_list.append(QString::fromStdString(physical_name));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Use regular directory iterator for shallow scanning
|
||||
for (const auto& entry : std::filesystem::directory_iterator(root_path)) {
|
||||
if (stop_requested) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto physical_name = entry.path().string();
|
||||
const bool is_dir = entry.is_directory();
|
||||
|
||||
if (!is_dir && (HasSupportedFileExtension(physical_name) ||
|
||||
IsExtractedNCAMain(physical_name))) {
|
||||
ProcessGameFile(physical_name, target, parent_dir);
|
||||
} else if (is_dir) {
|
||||
watch_list.append(QString::fromStdString(physical_name));
|
||||
}
|
||||
}
|
||||
} 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) {
|
||||
Common::FS::IterateDirEntriesRecursively(dir_path, callback,
|
||||
Common::FS::DirEntryFilter::All);
|
||||
auto loader = Loader::GetLoader(system, file);
|
||||
if (!loader) {
|
||||
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 {
|
||||
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,
|
||||
GameListDir* parent_dir);
|
||||
|
||||
void ProcessGameFile(const std::string& physical_name, ScanTarget target,
|
||||
GameListDir* parent_dir);
|
||||
|
||||
std::shared_ptr<FileSys::VfsFilesystem> vfs;
|
||||
FileSys::ManualContentProvider* provider;
|
||||
QVector<UISettings::GameDir>& game_dirs;
|
||||
|
|
Loading…
Reference in a new issue