| 
									
										
										
										
											2015-05-24 06:55:12 +02:00
										 |  |  | // Copyright 2008 Dolphin Emulator Project
 | 
					
						
							| 
									
										
										
										
											2015-05-18 01:08:10 +02:00
										 |  |  | // Licensed under GPLv2+
 | 
					
						
							| 
									
										
										
										
											2013-04-17 23:09:55 -04:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2014-11-15 15:46:40 -05:00
										 |  |  | #include <functional>
 | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "Common/CommonPaths.h"
 | 
					
						
							|  |  |  | #include "Common/FileSearch.h"
 | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef _MSC_VER
 | 
					
						
							| 
									
										
										
										
											2017-06-25 16:46:59 -07:00
										 |  |  | #include <Windows.h>
 | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  | #include <experimental/filesystem>
 | 
					
						
							|  |  |  | namespace fs = std::experimental::filesystem; | 
					
						
							|  |  |  | #define HAS_STD_FILESYSTEM
 | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2017-06-27 12:45:02 +02:00
										 |  |  | #include <cstring>
 | 
					
						
							|  |  |  | #include "Common/CommonFuncs.h"
 | 
					
						
							| 
									
										
										
										
											2014-11-15 15:46:40 -05:00
										 |  |  | #include "Common/FileUtil.h"
 | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-07 00:55:10 -04:00
										 |  |  | namespace Common | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  | #ifndef HAS_STD_FILESYSTEM
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | static std::vector<std::string> | 
					
						
							|  |  |  | FileSearchWithTest(const std::vector<std::string>& directories, bool recursive, | 
					
						
							|  |  |  |                    std::function<bool(const File::FSTEntry&)> callback) | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::vector<std::string> result; | 
					
						
							|  |  |  |   for (const std::string& directory : directories) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     File::FSTEntry top = File::ScanDirectoryTree(directory, recursive); | 
					
						
							| 
									
										
										
										
											2014-11-15 15:46:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     std::function<void(File::FSTEntry&)> DoEntry; | 
					
						
							|  |  |  |     DoEntry = [&](File::FSTEntry& entry) { | 
					
						
							|  |  |  |       if (callback(entry)) | 
					
						
							|  |  |  |         result.push_back(entry.physicalName); | 
					
						
							|  |  |  |       for (auto& child : entry.children) | 
					
						
							|  |  |  |         DoEntry(child); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     for (auto& child : top.children) | 
					
						
							|  |  |  |       DoEntry(child); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // remove duplicates
 | 
					
						
							|  |  |  |   std::sort(result.begin(), result.end()); | 
					
						
							|  |  |  |   result.erase(std::unique(result.begin(), result.end()), result.end()); | 
					
						
							|  |  |  |   return result; | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-25 11:59:28 +02:00
										 |  |  | std::vector<std::string> DoFileSearch(const std::vector<std::string>& directories, | 
					
						
							|  |  |  |                                       const std::vector<std::string>& exts, bool recursive) | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  |   bool accept_all = exts.empty(); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return FileSearchWithTest(directories, recursive, [&](const File::FSTEntry& entry) { | 
					
						
							|  |  |  |     if (accept_all) | 
					
						
							|  |  |  |       return true; | 
					
						
							| 
									
										
										
										
											2017-06-26 11:44:23 +02:00
										 |  |  |     if (entry.isDirectory) | 
					
						
							|  |  |  |       return false; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     return std::any_of(exts.begin(), exts.end(), [&](const std::string& ext) { | 
					
						
							| 
									
										
										
										
											2017-06-27 12:45:02 +02:00
										 |  |  |       const std::string& name = entry.virtualName; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       return name.length() >= ext.length() && | 
					
						
							| 
									
										
										
										
											2017-06-27 12:45:02 +02:00
										 |  |  |              strcasecmp(name.c_str() + name.length() - ext.length(), ext.c_str()) == 0; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2008-12-08 05:30:24 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  | #else
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<std::string> DoFileSearch(const std::vector<std::string>& directories, | 
					
						
							|  |  |  |                                       const std::vector<std::string>& exts, bool recursive) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   bool accept_all = exts.empty(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::vector<fs::path> native_exts; | 
					
						
							|  |  |  |   for (const auto& ext : exts) | 
					
						
							| 
									
										
										
										
											2017-06-28 09:50:02 +02:00
										 |  |  |     native_exts.push_back(fs::u8path(ext)); | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // N.B. This avoids doing any copies
 | 
					
						
							|  |  |  |   auto ext_matches = [&native_exts](const fs::path& path) { | 
					
						
							|  |  |  |     const auto& native_path = path.native(); | 
					
						
							|  |  |  |     return std::any_of(native_exts.cbegin(), native_exts.cend(), [&native_path](const auto& ext) { | 
					
						
							|  |  |  |       // TODO provide cross-platform compat for the comparison function, once more platforms
 | 
					
						
							|  |  |  |       // support std::filesystem
 | 
					
						
							| 
									
										
										
										
											2017-06-25 16:46:59 -07:00
										 |  |  |       int compare_len = static_cast<int>(ext.native().length()); | 
					
						
							|  |  |  |       return native_path.length() >= compare_len && | 
					
						
							|  |  |  |              CompareStringOrdinal(&native_path.c_str()[native_path.length() - compare_len], | 
					
						
							|  |  |  |                                   compare_len, ext.c_str(), compare_len, TRUE) == CSTR_EQUAL; | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::vector<std::string> result; | 
					
						
							|  |  |  |   auto add_filtered = [&](const fs::directory_entry& entry) { | 
					
						
							|  |  |  |     auto& path = entry.path(); | 
					
						
							|  |  |  |     if (accept_all || (ext_matches(path) && !fs::is_directory(path))) | 
					
						
							|  |  |  |       result.emplace_back(path.u8string()); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   for (const auto& directory : directories) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (recursive) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // TODO use fs::directory_options::follow_directory_symlink ?
 | 
					
						
							| 
									
										
										
										
											2017-06-28 09:48:28 +02:00
										 |  |  |       for (auto& entry : fs::recursive_directory_iterator(fs::u8path(directory))) | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  |         add_filtered(entry); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2017-06-28 09:48:28 +02:00
										 |  |  |       for (auto& entry : fs::directory_iterator(fs::u8path(directory))) | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  |         add_filtered(entry); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Remove duplicates (occurring because caller gave e.g. duplicate or overlapping directories -
 | 
					
						
							|  |  |  |   // not because std::filesystem returns duplicates). Also note that this pathname-based uniqueness
 | 
					
						
							|  |  |  |   // isn't as thorough as std::filesystem::equivalent.
 | 
					
						
							|  |  |  |   std::sort(result.begin(), result.end()); | 
					
						
							|  |  |  |   result.erase(std::unique(result.begin(), result.end()), result.end()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-26 11:50:10 +02:00
										 |  |  |   // Dolphin expects to be able to use "/" (DIR_SEP) everywhere.
 | 
					
						
							|  |  |  |   // std::filesystem uses the OS separator.
 | 
					
						
							|  |  |  |   constexpr fs::path::value_type os_separator = fs::path::preferred_separator; | 
					
						
							|  |  |  |   static_assert(os_separator == DIR_SEP_CHR || os_separator == '\\', "Unsupported path separator"); | 
					
						
							|  |  |  |   if (os_separator != DIR_SEP_CHR) | 
					
						
							| 
									
										
										
										
											2017-06-20 21:24:26 -07:00
										 |  |  |     for (auto& path : result) | 
					
						
							|  |  |  |       std::replace(path.begin(), path.end(), '\\', DIR_SEP_CHR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-07 00:55:10 -04:00
										 |  |  | }  // namespace Common
 |