From e1578be20f2ec49f9ec1b9589dfe95280024557c Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 17 Apr 2021 03:47:40 +1000 Subject: [PATCH] Common/FileSystem: Add BuildRelativePath() function --- src/common/cd_image_cue.cpp | 5 +- src/common/cd_image_m3u.cpp | 29 +++++--- src/common/file_system.cpp | 142 ++++++++---------------------------- src/common/file_system.h | 9 +-- src/core/psf_loader.cpp | 12 +-- 5 files changed, 52 insertions(+), 145 deletions(-) diff --git a/src/common/cd_image_cue.cpp b/src/common/cd_image_cue.cpp index d5cca562a..2e71b0c06 100644 --- a/src/common/cd_image_cue.cpp +++ b/src/common/cd_image_cue.cpp @@ -72,9 +72,6 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error) return false; } - // get the directory of the filename - std::string basepath(FileSystem::GetPathDirectory(filename)); - basepath += "/"; m_filename = filename; u32 disc_lba = 0; @@ -106,7 +103,7 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error) } if (track_file_index == m_files.size()) { - const std::string track_full_filename(basepath + track_filename); + const std::string track_full_filename(FileSystem::BuildRelativePath(m_filename, track_filename)); std::FILE* track_fp = FileSystem::OpenCFile(track_full_filename.c_str(), "rb"); if (!track_fp && track_file_index == 0) { diff --git a/src/common/cd_image_m3u.cpp b/src/common/cd_image_m3u.cpp index e733ce023..c64ad04db 100644 --- a/src/common/cd_image_m3u.cpp +++ b/src/common/cd_image_m3u.cpp @@ -1,11 +1,12 @@ #include "assert.h" #include "cd_image.h" #include "cd_subchannel_replacement.h" +#include "error.h" #include "file_system.h" #include "log.h" #include #include -#include +#include #include Log_SetChannel(CDImageMemory); @@ -48,13 +49,19 @@ CDImageM3u::~CDImageM3u() = default; bool CDImageM3u::Open(const char* path, Common::Error* error) { - std::ifstream ifs(path); - if (!ifs.is_open()) + std::FILE* fp = FileSystem::OpenCFile(path, "rb"); + if (!fp) + return false; + + std::optional cue_file(FileSystem::ReadFileToString(fp)); + std::fclose(fp); + if (!cue_file.has_value() || cue_file->empty()) { - Log_ErrorPrintf("Failed to open %s", path); + error->SetMessage("Failed to read cue sheet"); return false; } + std::istringstream ifs(cue_file.value()); m_filename = path; std::vector entries; @@ -79,14 +86,12 @@ bool CDImageM3u::Open(const char* path, Common::Error* error) continue; Entry entry; - entry.filename.assign(line.begin() + start_offset, line.begin() + end_offset + 1); - entry.title = FileSystem::GetFileTitleFromPath(entry.filename); - if (!FileSystem::IsAbsolutePath(entry.filename)) - { - SmallString absolute_path; - FileSystem::BuildPathRelativeToFile(absolute_path, path, entry.filename.c_str()); - entry.filename = absolute_path; - } + std::string entry_filename(line.begin() + start_offset, line.begin() + end_offset + 1); + entry.title = FileSystem::GetFileTitleFromPath(entry_filename); + if (!FileSystem::IsAbsolutePath(entry_filename)) + entry.filename = FileSystem::BuildRelativePath(path, entry_filename); + else + entry.filename = std::move(entry_filename); Log_DevPrintf("Read path from m3u: '%s'", entry.filename.c_str()); m_entries.push_back(std::move(entry)); diff --git a/src/common/file_system.cpp b/src/common/file_system.cpp index d39c9e5a9..5f8a1a3c3 100644 --- a/src/common/file_system.cpp +++ b/src/common/file_system.cpp @@ -283,13 +283,29 @@ std::string ReplaceExtension(const std::string_view& path, const std::string_vie return ret; } +static std::string_view::size_type GetLastSeperatorPosition(const std::string_view& filename, bool include_separator) +{ + std::string_view::size_type last_separator = filename.rfind('/'); + if (include_separator && last_separator != std::string_view::npos) + last_separator++; + +#if defined(_WIN32) + std::string_view::size_type other_last_separator = filename.rfind('\\'); + if (other_last_separator != std::string_view::npos) + { + if (include_separator) + other_last_separator++; + if (last_separator == std::string_view::npos || other_last_separator > last_separator) + last_separator = other_last_separator; + } +#endif + + return last_separator; +} + std::string_view GetPathDirectory(const std::string_view& path) { -#ifdef _WIN32 - std::string::size_type pos = path.find_last_of("/\\"); -#else - std::string::size_type pos = path.find_last_of("/"); -#endif + std::string::size_type pos = GetLastSeperatorPosition(path, false); if (pos == std::string_view::npos) return {}; @@ -298,15 +314,11 @@ std::string_view GetPathDirectory(const std::string_view& path) std::string_view GetFileNameFromPath(const std::string_view& path) { -#ifdef _WIN32 - std::string::size_type pos = path.find_last_of("/\\"); -#else - std::string::size_type pos = path.find_last_of("/"); -#endif + std::string::size_type pos = GetLastSeperatorPosition(path, true); if (pos == std::string_view::npos) return path; - return path.substr(pos + 1); + return path.substr(pos); } std::string_view GetFileTitleFromPath(const std::string_view& path) @@ -346,108 +358,14 @@ std::vector GetRootDirectoryList() return results; } -void BuildPathRelativeToFile(char* Destination, u32 cbDestination, const char* CurrentFileName, const char* NewFileName, - bool OSPath /* = true */, bool Canonicalize /* = true */) +std::string BuildRelativePath(const std::string_view& filename, const std::string_view& new_filename) { - s32 i; - u32 currentPos = 0; - DebugAssert(Destination != nullptr && cbDestination > 0 && CurrentFileName != nullptr && NewFileName != nullptr); - - // clone to a local buffer if the same pointer - std::string pathClone; - if (Destination == CurrentFileName) - { - pathClone = CurrentFileName; - CurrentFileName = pathClone.c_str(); - } - - // search for a / or \, copy everything up to and including it to the destination - i = (s32)std::strlen(CurrentFileName); - for (; i >= 0; i--) - { - if (CurrentFileName[i] == '/' || CurrentFileName[i] == '\\') - { - // cap to destination length - u32 copyLen; - if (NewFileName[0] != '\0') - copyLen = std::min((u32)(i + 1), cbDestination); - else - copyLen = std::min((u32)i, cbDestination); - - if (copyLen > 0) - { - std::memcpy(Destination, CurrentFileName, copyLen); - if (copyLen == cbDestination) - Destination[cbDestination - 1] = '\0'; - - currentPos = copyLen; - } - - break; - } - } - - // copy the new parts in - if (currentPos < cbDestination && NewFileName[0] != '\0') - StringUtil::Strlcpy(Destination + currentPos, NewFileName, cbDestination - currentPos); - - // canonicalize it - if (Canonicalize) - CanonicalizePath(Destination, cbDestination, Destination, OSPath); - else if (OSPath) - BuildOSPath(Destination, cbDestination, Destination); -} - -void BuildPathRelativeToFile(String& Destination, const char* CurrentFileName, const char* NewFileName, - bool OSPath /* = true */, bool Canonicalize /* = true */) -{ - s32 i; - DebugAssert(CurrentFileName != nullptr && NewFileName != nullptr); - - // get curfile length - u32 curFileLength = static_cast(std::strlen(CurrentFileName)); - - // clone to a local buffer if the same pointer - if (Destination.GetWriteableCharArray() == CurrentFileName) - { - char* pathClone = (char*)alloca(curFileLength + 1); - StringUtil::Strlcpy(pathClone, CurrentFileName, curFileLength + 1); - CurrentFileName = pathClone; - } - - // search for a / or \\, copy everything up to and including it to the destination - Destination.Clear(); - i = (s32)curFileLength; - for (; i >= 0; i--) - { - if (CurrentFileName[i] == '/' || CurrentFileName[i] == '\\') - { - if (NewFileName[0] != '\0') - Destination.AppendSubString(CurrentFileName, 0, i + 1); - else - Destination.AppendSubString(CurrentFileName, 0, i); - - break; - } - } - - // copy the new parts in - if (NewFileName[0] != '\0') - Destination.AppendString(NewFileName); - - // canonicalize it - if (Canonicalize) - CanonicalizePath(Destination, Destination.GetCharArray(), OSPath); - else if (OSPath) - BuildOSPath(Destination, Destination.GetCharArray()); -} - -String BuildPathRelativeToFile(const char* CurrentFileName, const char* NewFileName, bool OSPath /*= true*/, - bool Canonicalize /*= true*/) -{ - String ret; - BuildPathRelativeToFile(ret, CurrentFileName, NewFileName, OSPath, Canonicalize); - return ret; + std::string new_string; + std::string_view::size_type pos = GetLastSeperatorPosition(filename, true); + if (pos != std::string_view::npos) + new_string.assign(filename, 0, pos); + new_string.append(new_filename); + return new_string; } std::unique_ptr OpenFile(const char* FileName, u32 Flags) diff --git a/src/common/file_system.h b/src/common/file_system.h index 028f08740..60a4e2d92 100644 --- a/src/common/file_system.h +++ b/src/common/file_system.h @@ -126,13 +126,8 @@ void BuildOSPath(char* Destination, u32 cbDestination, const char* Path); void BuildOSPath(String& Destination, const char* Path); void BuildOSPath(String& Destination); -// builds a path relative to the specified file, optionally canonicalizing it -void BuildPathRelativeToFile(char* Destination, u32 cbDestination, const char* CurrentFileName, const char* NewFileName, - bool OSPath = true, bool Canonicalize = true); -void BuildPathRelativeToFile(String& Destination, const char* CurrentFileName, const char* NewFileName, - bool OSPath = true, bool Canonicalize = true); -String BuildPathRelativeToFile(const char* CurrentFileName, const char* NewFileName, bool OSPath = true, - bool Canonicalize = true); +// builds a path relative to the specified file +std::string BuildRelativePath(const std::string_view& filename, const std::string_view& new_filename); // sanitizes a filename for use in a filesystem. void SanitizeFileName(char* Destination, u32 cbDestination, const char* FileName, bool StripSlashes = true); diff --git a/src/core/psf_loader.cpp b/src/core/psf_loader.cpp index 0e44ce6c4..9e5ae80a3 100644 --- a/src/core/psf_loader.cpp +++ b/src/core/psf_loader.cpp @@ -162,14 +162,6 @@ bool File::Load(const char* path) return true; } -static std::string GetLibraryPSFPath(const char* main_path, const char* lib_path) -{ - std::string path(FileSystem::GetPathDirectory(main_path)); - path += FS_OSPATH_SEPARATOR_CHARACTER; - path += lib_path; - return path; -} - static bool LoadLibraryPSF(const char* path, bool use_pc_sp, u32 depth = 0) { // don't recurse past 10 levels just in case of broken files @@ -190,7 +182,7 @@ static bool LoadLibraryPSF(const char* path, bool use_pc_sp, u32 depth = 0) std::optional lib_name(file.GetTagString("_lib")); if (lib_name.has_value()) { - const std::string lib_path(GetLibraryPSFPath(path, lib_name->c_str())); + const std::string lib_path(FileSystem::BuildRelativePath(path, lib_name.value())); Log_InfoPrintf("Loading main parent PSF '%s'", lib_path.c_str()); // We should use the initial SP/PC from the **first** parent lib. @@ -222,7 +214,7 @@ static bool LoadLibraryPSF(const char* path, bool use_pc_sp, u32 depth = 0) if (!lib_name.has_value()) break; - const std::string lib_path(GetLibraryPSFPath(path, lib_name->c_str())); + const std::string lib_path(FileSystem::BuildRelativePath(path, lib_name.value())); Log_InfoPrintf("Loading parent PSF '%s'", lib_path.c_str()); if (!LoadLibraryPSF(lib_path.c_str(), false, depth + 1)) {