(view as text)
diff --git a/Source/Core/Common/Src/FileSearch.cpp b/Source/Core/Common/Src/FileSearch.cpp
index 7597b91..7e542af 100644
--- a/Source/Core/Common/Src/FileSearch.cpp
+++ b/Source/Core/Common/Src/FileSearch.cpp
@@ -5,100 +5,39 @@
 
 #include "Common.h"
 #include "CommonPaths.h"
-#ifndef _WIN32
-#include <sys/types.h>
-#include <dirent.h>
-#else
-#include <windows.h>
-#endif
-
 #include <algorithm>
+#include <regex>
 
 #include "FileSearch.h"
-#include "StringUtil.h"
-
+#include "FileUtil.h"
 
-CFileSearch::CFileSearch(const CFileSearch::XStringVector& _rSearchStrings, const CFileSearch::XStringVector& _rDirectories)
+std::vector<std::string> DoFileSearch(const std::vector<std::string>& _rSearchStrings, const std::vector<std::string>& _rDirectories, bool recursive)
 {
-	// Reverse the loop order for speed?
-	for (auto& _rSearchString : _rSearchStrings)
+	std::string regex_str = "^(";
+	for (const auto& str : _rSearchStrings)
 	{
-		for (auto& _rDirectory : _rDirectories)
-		{
-			FindFiles(_rSearchString, _rDirectory);
-		}
+		if (regex_str.size() != 2)
+			regex_str += "|";
+		// so verbose, much c++
+		regex_str += std::regex_replace(std::regex_replace(str, std::regex("\\."), "\\."), std::regex("\\*"), ".*");
 	}
-}
-
-
-void CFileSearch::FindFiles(const std::string& _searchString, const std::string& _strPath)
-{
-	std::string GCMSearchPath;
-	BuildCompleteFilename(GCMSearchPath, _strPath, _searchString);
-#ifdef _WIN32
-	WIN32_FIND_DATA findData;
-	HANDLE FindFirst = FindFirstFile(UTF8ToTStr(GCMSearchPath).c_str(), &findData);
-
-	if (FindFirst != INVALID_HANDLE_VALUE)
+	regex_str += ")$";
+	WARN_LOG(NETPLAY, "derp %s", regex_str.c_str());
+	std::regex regex(regex_str);
+	std::vector<std::string> result;
+	for (const std::string& directory : _rDirectories)
 	{
-		bool bkeepLooping = true;
-
-		while (bkeepLooping)
-		{
-			if (findData.cFileName[0] != '.')
-			{
-				std::string strFilename;
-				BuildCompleteFilename(strFilename, _strPath, TStrToUTF8(findData.cFileName));
-				m_FileNames.push_back(strFilename);
-			}
-
-			bkeepLooping = FindNextFile(FindFirst, &findData) ? true : false;
-		}
+		File::FSTEntry entry;
+		File::ScanDirectoryTree(directory, entry, recursive);
+
+		std::function<void(File::FSTEntry&)> DoEntry;
+		DoEntry = [&](File::FSTEntry& thisEntry) {
+			if (std::regex_match(thisEntry.virtualName, regex))
+				result.push_back(thisEntry.physicalName);
+			for (auto& child : thisEntry.children)
+				DoEntry(child);
+		};
+		DoEntry(entry);
 	}
-	FindClose(FindFirst);
-
-
-#else
-	// TODO: super lame/broken
-
-	auto end_match(_searchString);
-
-	// assuming we have a "*.blah"-like pattern
-	if (!end_match.empty() && end_match[0] == '*')
-		end_match.erase(0, 1);
-
-	// ugly
-	if (end_match == ".*")
-		end_match.clear();
-
-	DIR* dir = opendir(_strPath.c_str());
-
-	if (!dir)
-		return;
-
-	while (auto const dp = readdir(dir))
-	{
-		std::string found(dp->d_name);
-
-		if ((found != ".") && (found != "..")
-			&& (found.size() >= end_match.size())
-			&& std::equal(end_match.rbegin(), end_match.rend(), found.rbegin()))
-		{
-			std::string full_name;
-			if (_strPath.c_str()[_strPath.size()-1] == DIR_SEP_CHR)
-				full_name = _strPath + found;
-			else
-				full_name = _strPath + DIR_SEP + found;
-
-			m_FileNames.push_back(full_name);
-		}
-	}
-
-	closedir(dir);
-#endif
-}
-
-const CFileSearch::XStringVector& CFileSearch::GetFileNames() const
-{
-	return m_FileNames;
+	return result;
 }
diff --git a/Source/Core/Common/Src/FileSearch.h b/Source/Core/Common/Src/FileSearch.h
index 55aaf4e..c667176 100644
--- a/Source/Core/Common/Src/FileSearch.h
+++ b/Source/Core/Common/Src/FileSearch.h
@@ -9,20 +9,7 @@
 #include <string>
 #include <vector>
 
-class CFileSearch
-{
-public:
-	typedef std::vector<std::string>XStringVector;
-
-	CFileSearch(const XStringVector& _rSearchStrings, const XStringVector& _rDirectories);
-	const XStringVector& GetFileNames() const;
-
-private:
-
-	void FindFiles(const std::string& _searchString, const std::string& _strPath);
-
-	XStringVector m_FileNames;
-};
+std::vector<std::string> DoFileSearch(const std::vector<std::string>& _rSearchStrings, const std::vector<std::string>& _rDirectories, bool recursive = false);
 
 #endif // _FILESEARCH_H_
 
diff --git a/Source/Core/Common/Src/FileUtil.cpp b/Source/Core/Common/Src/FileUtil.cpp
index 7eed3f0..2415e708 100644
--- a/Source/Core/Common/Src/FileUtil.cpp
+++ b/Source/Core/Common/Src/FileUtil.cpp
@@ -454,7 +454,7 @@ bool CreateEmptyFile(const std::string &filename)
 
 // Scans the directory tree gets, starting from _Directory and adds the
 // results into parentEntry. Returns the number of files+directories found
-u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
+u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry, bool recursive)
 {
 	INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str());
 	// How many files + directories we found
@@ -500,7 +500,8 @@ u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
 		{
 			entry.isDirectory = true;
 			// is a directory, lets go inside
-			entry.size = ScanDirectoryTree(entry.physicalName, entry);
+			if (recursive)
+				entry.size = ScanDirectoryTree(entry.physicalName, entry);
 			foundEntries += (u32)entry.size;
 		}
 		else
@@ -671,6 +672,31 @@ bool SetCurrentDir(const std::string &directory)
 	return __chdir(directory.c_str()) == 0;
 }
 
+std::string CreateTempDir()
+{
+#ifdef _WIN32
+	TCHAR temp[MAX_PATH];
+	if (!GetTempPath(MAX_PATH, temp))
+		return "";
+
+	GUID guid;
+	CoCreateGuid(&guid);
+	TCHAR tguid[40];
+	StringFromGUID2(&guid, tguid, 39);
+	tguid[39] = 0;
+	std::string dir = TStrToUTF8(temp) + "/" + TStrToUTF8(tguid);
+	if (!CreateDir(dir))
+		return "";
+	return dir;
+#else
+	const char* base = getenv("TMPDIR") ?: "/tmp";
+	std::string path = std::string(base) + "/DolphinWii.XXXXXX";
+	if (!mkdtemp(&path[0]))
+		return "";
+	return path;
+#endif
+}
+
 std::string GetTempFilenameForAtomicWrite(const std::string &path)
 {
 	std::string abs = path;
diff --git a/Source/Core/Common/Src/FileUtil.h b/Source/Core/Common/Src/FileUtil.h
index b39135f..8a4bbf5 100644
--- a/Source/Core/Common/Src/FileUtil.h
+++ b/Source/Core/Common/Src/FileUtil.h
@@ -108,7 +108,7 @@ bool CreateEmptyFile(const std::string &filename);
 
 // Scans the directory tree gets, starting from _Directory and adds the
 // results into parentEntry. Returns the number of files+directories found
-u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry);
+u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry, bool recursive = true);
 
 // deletes the given directory and anything under it. Returns true on success.
 bool DeleteDirRecursively(const std::string &directory);
@@ -122,6 +122,9 @@ void CopyDir(const std::string &source_path, const std::string &dest_path);
 // Set the current directory to given directory
 bool SetCurrentDir(const std::string &directory);
 
+// Creates and returns the path to a new temporary directory.
+std::string CreateTempDir();
+
 // Get a filename that can hopefully be atomically renamed to the given path.
 std::string GetTempFilenameForAtomicWrite(const std::string &path);
 
diff --git a/Source/Core/Common/Src/NetHost.cpp b/Source/Core/Common/Src/NetHost.cpp
index 1e5fb90..0f684dc 100644
--- a/Source/Core/Common/Src/NetHost.cpp
+++ b/Source/Core/Common/Src/NetHost.cpp
@@ -49,7 +49,6 @@ void ENetUtil::Wakeup(ENetHost* host)
 	enet_socket_send(host->socket, &address, &buf, 1);
 }
 
-#define DEBUG_LOG WARN_LOG
 static void CompressIntoPacket(PWBuffer& vec, Packet& container)
 {
 	z_stream strm = {0};
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp
index 3bd37ba..d9753a1 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp
@@ -91,6 +91,7 @@ void Init()
 
 	_dbg_assert_msg_(WII_IPC_HLE, g_DeviceMap.empty(), "DeviceMap isn't empty on init");
 	CWII_IPC_HLE_Device_es::m_ContentFile = "";
+	HLE_IPC_InitFS();
 	u32 i;
 	for (i=0; i<IPC_MAX_FDS; i++)
 	{
@@ -615,6 +616,7 @@ void UpdateDevices()
 	}
 }
 
+bool g_HeadlessDeterminism = true;
 
 } // end of namespace WII_IPC_HLE_Interface
 
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.h b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.h
index 5f49201..c29da03 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.h
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.h
@@ -64,6 +64,11 @@ enum ECommandType
 	COMMAND_IOCTLV			= 7,
 };
 
+// Use a minimal FS, network calls fail, etc.  Netplay does not need this since
+// it can use the host's FS and such, but a TAS might want it.  Could be
+// improved to allow temporary writes to be read back.
+extern bool g_HeadlessDeterminism;
+
 } // end of namespace WII_IPC_HLE_Interface
 
 #endif
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp
index 1a8febe..3552460 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp
@@ -7,18 +7,64 @@
 #include "StringUtil.h"
 #include "ChunkFile.h"
 
+#include "WII_IPC_HLE.h"
 #include "WII_IPC_HLE_Device_fs.h"
 #include "WII_IPC_HLE_Device_FileIO.h"
 #include "NandPaths.h"
+#include "CommonPaths.h"
 #include <algorithm>
 
-
 static Common::replace_v replacements;
 
+static std::string GetWiiRoot();
+
+static std::string g_WiiRoot;
+
+static void DeleteWiiRoot()
+{
+	if (!g_WiiRoot.empty())
+		File::DeleteDirRecursively(g_WiiRoot);
+}
+
+static std::string GetWiiRoot()
+{
+	if (g_WiiRoot.empty())
+	{
+		if (WII_IPC_HLE_Interface::g_HeadlessDeterminism)
+		{
+			g_WiiRoot = File::CreateTempDir();
+			if (g_WiiRoot.empty())
+			{
+				ERROR_LOG(WII_IPC_FILEIO, "Could not create temporary directory");
+				return g_WiiRoot;
+			}
+			File::CopyDir(File::GetSysDirectory() + WII_USER_DIR, g_WiiRoot);
+			WARN_LOG(WII_IPC_FILEIO, "Using temporary directory %s for minimal Wii FS", g_WiiRoot.c_str());
+			static bool Registered;
+			if (!Registered)
+			{
+				Registered = true;
+				atexit(DeleteWiiRoot);
+			}
+		}
+		else
+		{
+			g_WiiRoot = File::GetUserPath(D_WIIROOT_IDX);
+		}
+	}
+	return g_WiiRoot;
+}
+
+void HLE_IPC_InitFS()
+{
+	DeleteWiiRoot();
+	g_WiiRoot.clear();
+}
+
 // This is used by several of the FileIO and /dev/fs functions
 std::string HLE_IPC_BuildFilename(std::string path_wii, int _size)
 {
-	std::string path_full = File::GetUserPath(D_WIIROOT_IDX);
+	std::string path_full = GetWiiRoot();
 
 	// Replaces chars that FAT32 can't support with strings defined in /sys/replace
 	for (auto& replacement : replacements)
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.h b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.h
index 65dd0ce..1ed8be0 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.h
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.h
@@ -8,6 +8,7 @@
 #include "WII_IPC_HLE_Device.h"
 #include "FileUtil.h"
 
+void HLE_IPC_InitFS();
 std::string HLE_IPC_BuildFilename(std::string _pFilename, int _size);
 void HLE_IPC_CreateVirtualFATFilesystem();
 
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp
index 4de7af8..21deed6 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp
@@ -16,6 +16,7 @@
 #include "../HW/SystemTimers.h"
 
 #include "../VolumeHandler.h"
+#include <algorithm>
 
 #define MAX_NAME				(12)
 
@@ -35,7 +36,7 @@ bool CWII_IPC_HLE_Device_fs::Open(u32 _CommandAddress, u32 _Mode)
 {
 	// clear tmp folder
 	{
-		std::string Path = File::GetUserPath(D_WIIUSER_IDX) + "tmp";
+		std::string Path = HLE_IPC_BuildFilename("/tmp", 4);
 		File::DeleteDirRecursively(Path);
 		File::CreateDir(Path.c_str());
 	}
@@ -109,19 +110,13 @@ bool CWII_IPC_HLE_Device_fs::IOCtlV(u32 _CommandAddress)
 				break;
 			}
 
-			// make a file search
-			CFileSearch::XStringVector Directories;
-			Directories.push_back(DirName);
-
-			CFileSearch::XStringVector Extensions;
-			Extensions.push_back("*.*");
-
-			CFileSearch FileSearch(Extensions, Directories);
+			File::FSTEntry ParentDir;
+			File::ScanDirectoryTree(DirName, ParentDir);
 
 			// it is one
 			if ((CommandBuffer.InBuffer.size() == 1) && (CommandBuffer.PayloadBuffer.size() == 1))
 			{
-				size_t numFile = FileSearch.GetFileNames().size();
+				size_t numFile = ParentDir.children.size();
 				INFO_LOG(WII_IPC_FILEIO, "\t%lu files found", (unsigned long)numFile);
 
 				Memory::Write_U32((u32)numFile, CommandBuffer.PayloadBuffer[0].m_Address);
@@ -135,14 +130,14 @@ bool CWII_IPC_HLE_Device_fs::IOCtlV(u32 _CommandAddress)
 				size_t numFiles = 0;
 				char* pFilename = (char*)Memory::GetPointer((u32)(CommandBuffer.PayloadBuffer[0].m_Address));
 
-				for (size_t i=0; i<FileSearch.GetFileNames().size(); i++)
-				{
-					if (i >= MaxEntries)
-						break;
+				// Sort for much determinism
+				std::sort(ParentDir.children.begin(), ParentDir.children.end(), [](File::FSTEntry& First, File::FSTEntry& Second) {
+					return First.virtualName < Second.virtualName;
+				});
 
-					std::string name, ext;
-					SplitPath(FileSearch.GetFileNames()[i], NULL, &name, &ext);
-					std::string FileName = name + ext;
+				for (size_t i=0, max = std::min(ParentDir.children.size(), (size_t) MaxEntries); i < max; i++)
+				{
+					std::string FileName = ParentDir.children[i].virtualName;
 
 					// Decode entities of invalid file system characters so that
 					// games (such as HP:HBP) will be able to find what they expect.
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_net.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_net.cpp
index 1cfcf26..03783db 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_net.cpp
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_net.cpp
@@ -383,7 +383,7 @@ bool CWII_IPC_HLE_Device_net_ncd_manage::IOCtlV(u32 _CommandAddress)
 	case IOCTLV_NCD_GETWIRELESSMACADDRESS:
 		INFO_LOG(WII_IPC_NET, "NET_NCD_MANAGE: IOCTLV_NCD_GETWIRELESSMACADDRESS");
 
-		if (!SConfig::GetInstance().m_WirelessMac.empty())
+		if (!SConfig::GetInstance().m_WirelessMac.empty() && !WII_IPC_HLE_Interface::g_HeadlessDeterminism)
 		{
 			int x = 0;
 			int tmpaddress[6];
@@ -639,6 +639,13 @@ bool CWII_IPC_HLE_Device_net_ip_top::IOCtl(u32 _CommandAddress)
 	u32 BufferOut		= Memory::Read_U32(_CommandAddress + 0x18);
 	u32 BufferOutSize	= Memory::Read_U32(_CommandAddress + 0x1C);
 
+	if (WII_IPC_HLE_Interface::g_HeadlessDeterminism)
+	{
+		// no net 4 u
+		Memory::Write_U32(-1, _CommandAddress + 0x4);
+		return true;
+	}
+
 	u32 ReturnValue = 0;
 	switch (Command)
 	{
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp
index 36d4b2d..31e7be0 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp
@@ -128,6 +128,13 @@ bool CWII_IPC_HLE_Device_net_ssl::IOCtlV(u32 _CommandAddress)
 		BufferOutSize3 = CommandBuffer.PayloadBuffer.at(2).m_Size;
 	}
 
+	// Righto, then...
+	if (WII_IPC_HLE_Interface::g_HeadlessDeterminism)
+	{
+		Memory::Write_U32(-1, _CommandAddress + 0x4);
+		return true;
+	}
+
 	switch (CommandBuffer.Parameter)
 	{
 	case IOCTLV_NET_SSL_NEW:
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_sdio_slot0.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_sdio_slot0.cpp
index 0b8e8bb..75e5c15 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_sdio_slot0.cpp
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_sdio_slot0.cpp
@@ -37,8 +37,9 @@ void CWII_IPC_HLE_Device_sdio_slot0::DoState(PointerWrap& p)
 
 void CWII_IPC_HLE_Device_sdio_slot0::EventNotify()
 {
-	if ((SConfig::GetInstance().m_WiiSDCard && m_event.type == EVENT_INSERT) ||
-		(!SConfig::GetInstance().m_WiiSDCard && m_event.type == EVENT_REMOVE))
+	bool Enable = SConfig::GetInstance().m_WiiSDCard && !WII_IPC_HLE_Interface::g_HeadlessDeterminism;
+	if ((Enable && m_event.type == EVENT_INSERT) ||
+		(!Enable && m_event.type == EVENT_REMOVE))
 	{
 		Memory::Write_U32(m_event.type, m_event.addr + 4);
 		WII_IPC_HLE_Interface::EnqReply(m_event.addr);
@@ -183,7 +184,7 @@ bool CWII_IPC_HLE_Device_sdio_slot0::IOCtl(u32 _CommandAddress)
 		break;
 
 	case IOCTL_GETSTATUS:
-		if (SConfig::GetInstance().m_WiiSDCard)
+		if (SConfig::GetInstance().m_WiiSDCard && !WII_IPC_HLE_Interface::g_HeadlessDeterminism)
 			m_Status |= CARD_INSERTED;
 		else
 			m_Status = CARD_NOT_EXIST;
diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_usb_kbd.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_usb_kbd.cpp
index 4300ecc..4e72374 100644
--- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_usb_kbd.cpp
+++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_usb_kbd.cpp
@@ -61,7 +61,7 @@ bool CWII_IPC_HLE_Device_usb_kbd::IOCtl(u32 _CommandAddress)
 {
 	u32 BufferOut		= Memory::Read_U32(_CommandAddress + 0x18);
 
-	if (SConfig::GetInstance().m_WiiKeyboard && !m_MessageQueue.empty())
+	if (SConfig::GetInstance().m_WiiKeyboard && !WII_IPC_HLE_Interface::g_HeadlessDeterminism && !m_MessageQueue.empty())
 	{
 		*(SMessageData*)Memory::GetPointer(BufferOut) = m_MessageQueue.front();
 		m_MessageQueue.pop();
@@ -86,7 +86,7 @@ bool CWII_IPC_HLE_Device_usb_kbd::IsKeyPressed(int _Key)
 
 u32 CWII_IPC_HLE_Device_usb_kbd::Update()
 {
-	if (!SConfig::GetInstance().m_WiiKeyboard || !m_Active)
+	if (!SConfig::GetInstance().m_WiiKeyboard || WII_IPC_HLE_Interface::g_HeadlessDeterminism || !m_Active)
 		return false;
 
 	u8 Modifiers = 0x00;
diff --git a/Source/Core/DolphinWX/Src/ConfigMain.cpp b/Source/Core/DolphinWX/Src/ConfigMain.cpp
index e1c3d2b..4420075 100644
--- a/Source/Core/DolphinWX/Src/ConfigMain.cpp
+++ b/Source/Core/DolphinWX/Src/ConfigMain.cpp
@@ -609,13 +609,12 @@ void CConfigMain::CreateGUIControls()
 	// theme selection
 	auto const theme_selection = new wxChoice(DisplayPage, wxID_ANY);
 
-	CFileSearch::XStringVector theme_dirs;
+	std::vector<std::string> theme_dirs;
 	theme_dirs.push_back(File::GetUserPath(D_THEMES_IDX));
 	theme_dirs.push_back(File::GetSysDirectory() + THEMES_DIR);
 
-	CFileSearch cfs(CFileSearch::XStringVector(1, "*"), theme_dirs);
-	auto const& sv = cfs.GetFileNames();
-	std::for_each(sv.begin(), sv.end(), [theme_selection](const std::string& filename)
+	auto sv = DoFileSearch({"*"}, theme_dirs);
+	for (const std::string& filename : sv)
 	{
 		std::string name, ext;
 		SplitPath(filename, NULL, &name, &ext);
@@ -624,7 +623,7 @@ void CConfigMain::CreateGUIControls()
 		auto const wxname = StrToWxStr(name);
 		if (-1 == theme_selection->FindString(wxname))
 			theme_selection->Append(wxname);
-	});
+	}
 
 	theme_selection->SetStringSelection(StrToWxStr(SConfig::GetInstance().m_LocalCoreStartupParameter.theme_name));
 
diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp
index dcba7cd..6e1a485 100644
--- a/Source/Core/DolphinWX/Src/FrameTools.cpp
+++ b/Source/Core/DolphinWX/Src/FrameTools.cpp
@@ -1776,13 +1776,7 @@ void CFrame::GameListChanged(wxCommandEvent& event)
 		SConfig::GetInstance().m_ListDrives = event.IsChecked();
 		break;
 	case IDM_PURGECACHE:
-		CFileSearch::XStringVector Directories;
-		Directories.push_back(File::GetUserPath(D_CACHE_IDX).c_str());
-		CFileSearch::XStringVector Extensions;
-		Extensions.push_back("*.cache");
-
-		CFileSearch FileSearch(Extensions, Directories);
-		const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames();
+		auto rFilenames = DoFileSearch({"*.cache"}, {File::GetUserPath(D_CACHE_IDX)});
 
 		for (auto& rFilename : rFilenames)
 		{
diff --git a/Source/Core/DolphinWX/Src/GameListCtrl.cpp b/Source/Core/DolphinWX/Src/GameListCtrl.cpp
index 923857d..cf6c22e 100644
--- a/Source/Core/DolphinWX/Src/GameListCtrl.cpp
+++ b/Source/Core/DolphinWX/Src/GameListCtrl.cpp
@@ -447,37 +447,7 @@ void CGameListCtrl::ScanForISOs()
 {
 	ClearIsoFiles();
 
-	CFileSearch::XStringVector Directories(SConfig::GetInstance().m_ISOFolder);
-
-	if (SConfig::GetInstance().m_RecursiveISOFolder)
-	{
-		for (u32 i = 0; i < Directories.size(); i++)
-		{
-			File::FSTEntry FST_Temp;
-			File::ScanDirectoryTree(Directories[i], FST_Temp);
-			for (auto& Entry : FST_Temp.children)
-			{
-				if (Entry.isDirectory)
-				{
-					bool duplicate = false;
-					for (auto& Directory : Directories)
-					{
-						if (strcmp(Directory.c_str(),
-									Entry.physicalName.c_str()) == 0)
-						{
-							duplicate = true;
-							break;
-						}
-					}
-					if (!duplicate)
-						Directories.push_back(
-								Entry.physicalName.c_str());
-				}
-			}
-		}
-	}
-
-	CFileSearch::XStringVector Extensions;
+	std::vector<std::string> Extensions;
 
 	if (SConfig::GetInstance().m_ListGC)
 		Extensions.push_back("*.gcm");
@@ -491,8 +461,7 @@ void CGameListCtrl::ScanForISOs()
 	if (SConfig::GetInstance().m_ListWad)
 		Extensions.push_back("*.wad");
 
-	CFileSearch FileSearch(Extensions, Directories);
-	const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames();
+	auto rFilenames = DoFileSearch(Extensions, {SConfig::GetInstance().m_ISOFolder}, SConfig::GetInstance().m_RecursiveISOFolder);
 
 	if (rFilenames.size() > 0)
 	{
diff --git a/Source/Core/DolphinWX/Src/InputConfigDiag.cpp b/Source/Core/DolphinWX/Src/InputConfigDiag.cpp
index a8b7b8a..386874f 100644
--- a/Source/Core/DolphinWX/Src/InputConfigDiag.cpp
+++ b/Source/Core/DolphinWX/Src/InputConfigDiag.cpp
@@ -144,19 +144,12 @@ void InputConfigDialog::UpdateProfileComboBox()
 	pname += PROFILES_PATH;
 	pname += m_plugin.profile_name;
 
-	CFileSearch::XStringVector exts;
-	exts.push_back("*.ini");
-	CFileSearch::XStringVector dirs;
-	dirs.push_back(pname);
-	CFileSearch cfs(exts, dirs);
-	const CFileSearch::XStringVector& sv = cfs.GetFileNames();
+	auto sv = DoFileSearch({"*.ini"}, {pname});
 
 	wxArrayString strs;
-	CFileSearch::XStringVector::const_iterator si = sv.begin(),
-		se = sv.end();
-	for (; si!=se; ++si)
+	for (auto& filename : sv)
 	{
-		std::string str(si->begin() + si->find_last_of('/') + 1 , si->end() - 4) ;
+		std::string str(filename.begin() + filename.find_last_of('/') + 1 , filename.end() - 4) ;
 		strs.push_back(StrToWxStr(str));
 	}
 
diff --git a/Source/Core/VideoCommon/Src/HiresTextures.cpp b/Source/Core/VideoCommon/Src/HiresTextures.cpp
index c88e4ae..039036f 100644
--- a/Source/Core/VideoCommon/Src/HiresTextures.cpp
+++ b/Source/Core/VideoCommon/Src/HiresTextures.cpp
@@ -22,45 +22,22 @@ void Init(const char *gameCode)
 {
 	textureMap.clear();
 
-	CFileSearch::XStringVector Directories;
+	std::vector<std::string> Directories;
 	//Directories.push_back(File::GetUserPath(D_HIRESTEXTURES_IDX));
 	char szDir[MAX_PATH];
 	sprintf(szDir, "%s%s", File::GetUserPath(D_HIRESTEXTURES_IDX).c_str(), gameCode);
 	Directories.push_back(std::string(szDir));
 
 
-	for (u32 i = 0; i < Directories.size(); i++)
-	{
-		File::FSTEntry FST_Temp;
-		File::ScanDirectoryTree(Directories[i], FST_Temp);
-		for (auto& entry : FST_Temp.children)
-		{
-			if (entry.isDirectory)
-			{
-				bool duplicate = false;
-				for (auto& Directory : Directories)
-				{
-					if (strcmp(Directory.c_str(), entry.physicalName.c_str()) == 0)
-					{
-						duplicate = true;
-						break;
-					}
-				}
-				if (!duplicate)
-					Directories.push_back(entry.physicalName.c_str());
-			}
-		}
-	}
-
-	CFileSearch::XStringVector Extensions;
-	Extensions.push_back("*.png");
-	Extensions.push_back("*.bmp");
-	Extensions.push_back("*.tga");
-	Extensions.push_back("*.dds");
-	Extensions.push_back("*.jpg"); // Why not? Could be useful for large photo-like textures
+	std::vector<std::string> Extensions {
+		"*.png",
+		"*.bmp",
+		"*.tga",
+		"*.dds",
+		"*.jpg" // Why not? Could be useful for large photo-like textures
+	};
 
-	CFileSearch FileSearch(Extensions, Directories);
-	const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames();
+	auto rFilenames = DoFileSearch(Extensions, {szDir}, /*recursive=*/true);
 	char code[MAX_PATH];
 	sprintf(code, "%s_", gameCode);