diff --git a/Common/FileUtil.cpp b/Common/FileUtil.cpp index 1a9629f35b..2588d73bc6 100644 --- a/Common/FileUtil.cpp +++ b/Common/FileUtil.cpp @@ -100,6 +100,28 @@ bool OpenCPPFile(std::fstream & stream, const std::string &filename, std::ios::o return stream.is_open(); } +std::string ResolvePath(const std::string &path) { +#ifdef _WIN32 + HANDLE hFile = CreateFile(ConvertUTF8ToWString(path).c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (hFile == INVALID_HANDLE_VALUE) + return path; + + wchar_t buf[1024] = {0}; + int result = GetFinalPathNameByHandle(hFile, buf, (int)ARRAY_SIZE(buf) - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + if (result >= ARRAY_SIZE(buf)) + return path; + + // Undo the \\?\C:\ syntax that's normally returned. + if (buf[0] == '\\' && buf[1] == '\\' && buf[2] == '?' && buf[3] == '\\' && isalpha(buf[4]) && buf[5] == ':') + return ConvertWStringToUTF8(buf).substr(4); + return ConvertWStringToUTF8(buf); +#else + char buf[PATH_MAX + 1]; + if (realpath(path.c_str(), buf) == nullptr) + return path; + return buf; +#endif +} // Remove any ending forward slashes from directory paths // Modifies argument. diff --git a/Common/FileUtil.h b/Common/FileUtil.h index 07ac398fb7..31e0e7a373 100644 --- a/Common/FileUtil.h +++ b/Common/FileUtil.h @@ -58,6 +58,9 @@ struct FileDetails { FILE *OpenCFile(const std::string &filename, const char *mode); bool OpenCPPFile(std::fstream & stream, const std::string &filename, std::ios::openmode mode); +// Resolves symlinks and similar. +std::string ResolvePath(const std::string &path); + // Returns true if file filename exists bool Exists(const std::string &filename); diff --git a/Core/Config.cpp b/Core/Config.cpp index e7c22fe0ad..e6bf5440ea 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1209,30 +1209,34 @@ void Config::AddRecent(const std::string &file) { if (iMaxRecent <= 0) return; -#ifdef _WIN32 - std::string filename = ReplaceAll(file, "\\", "/"); -#else - std::string filename = file; -#endif - + const std::string filename = File::ResolvePath(file); for (auto str = recentIsos.begin(); str != recentIsos.end(); ++str) { -#ifdef _WIN32 - if (!strcmpIgnore((*str).c_str(), filename.c_str(), "\\", "/")) { -#else - if (!strcmp((*str).c_str(), filename.c_str())) { -#endif + const std::string recent = File::ResolvePath(*str); + if (filename == recent) { recentIsos.erase(str); - recentIsos.insert(recentIsos.begin(), filename); - if ((int)recentIsos.size() > iMaxRecent) - recentIsos.resize(iMaxRecent); - return; + // We'll add it back below. } } + recentIsos.insert(recentIsos.begin(), filename); if ((int)recentIsos.size() > iMaxRecent) recentIsos.resize(iMaxRecent); } +void Config::RemoveRecent(const std::string &file) { + // Don't bother with this if the user disabled recents (it's -1). + if (iMaxRecent <= 0) + return; + + const std::string filename = File::ResolvePath(file); + for (auto str = recentIsos.begin(); str != recentIsos.end(); ++str) { + const std::string recent = File::ResolvePath(*str); + if (filename == recent) { + recentIsos.erase(str); + } + } +} + void Config::CleanRecent() { std::vector cleanedRecent; for (size_t i = 0; i < recentIsos.size(); i++) { diff --git a/Core/Config.h b/Core/Config.h index d39957e7fb..10ae5c23d8 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -428,6 +428,7 @@ public: // Utility functions for "recent" management void AddRecent(const std::string &file); + void RemoveRecent(const std::string &file); void CleanRecent(); static void DownloadCompletedCallback(http::Download &download); diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index 5d555e4adc..b07946ebf9 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -44,7 +44,7 @@ VirtualDiscFileSystem::VirtualDiscFileSystem(IHandleAllocator *_hAlloc, std::str : basePath(_basePath),currentBlockIndex(0) { #ifdef _WIN32 - if (!endsWith(basePath, "\\")) + if (!endsWith(basePath, "\\") && !endsWith(basePath, "/")) basePath = basePath + "\\"; #else if (!endsWith(basePath, "/")) diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index caa8f396e3..f2a5e9f431 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -66,10 +66,7 @@ bool GameInfo::Delete() { // Just delete the one file (TODO: handle two-disk games as well somehow). const char *fileToRemove = filePath_.c_str(); File::Delete(fileToRemove); - auto i = std::find(g_Config.recentIsos.begin(), g_Config.recentIsos.end(), fileToRemove); - if (i != g_Config.recentIsos.end()) { - g_Config.recentIsos.erase(i); - } + g_Config.RemoveRecent(filePath_); return true; } case IdentifiedFileType::PSP_PBP_DIRECTORY: diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index cf0f8c0ba5..d10584463e 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -354,31 +354,18 @@ bool GameScreen::isRecentGame(const std::string &gamePath) { if (g_Config.iMaxRecent <= 0) return false; + const std::string resolved = File::ResolvePath(gamePath); for (auto it = g_Config.recentIsos.begin(); it != g_Config.recentIsos.end(); ++it) { -#ifdef _WIN32 - if (!strcmpIgnore((*it).c_str(), gamePath.c_str(), "\\","/")) -#else - if (!strcmp((*it).c_str(), gamePath.c_str())) -#endif + const std::string recent = File::ResolvePath(*it); + if (resolved == recent) return true; } return false; } UI::EventReturn GameScreen::OnRemoveFromRecent(UI::EventParams &e) { - if (g_Config.iMaxRecent <= 0) - return UI::EVENT_DONE; - for (auto it = g_Config.recentIsos.begin(); it != g_Config.recentIsos.end(); ++it) { -#ifdef _WIN32 - if (!strcmpIgnore((*it).c_str(), gamePath_.c_str(), "\\","/")) { -#else - if (!strcmp((*it).c_str(), gamePath_.c_str())) { -#endif - g_Config.recentIsos.erase(it); - screenManager()->switchScreen(new MainScreen()); - return UI::EVENT_DONE; - } - } + g_Config.RemoveRecent(gamePath_); + screenManager()->switchScreen(new MainScreen()); return UI::EVENT_DONE; }