Common/FileSystem: Add BuildRelativePath() function

This commit is contained in:
Connor McLaughlin 2021-04-17 03:47:40 +10:00
parent 1b16662f17
commit e1578be20f
5 changed files with 52 additions and 145 deletions

View file

@ -72,9 +72,6 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error)
return false; return false;
} }
// get the directory of the filename
std::string basepath(FileSystem::GetPathDirectory(filename));
basepath += "/";
m_filename = filename; m_filename = filename;
u32 disc_lba = 0; u32 disc_lba = 0;
@ -106,7 +103,7 @@ bool CDImageCueSheet::OpenAndParse(const char* filename, Common::Error* error)
} }
if (track_file_index == m_files.size()) 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"); std::FILE* track_fp = FileSystem::OpenCFile(track_full_filename.c_str(), "rb");
if (!track_fp && track_file_index == 0) if (!track_fp && track_file_index == 0)
{ {

View file

@ -1,11 +1,12 @@
#include "assert.h" #include "assert.h"
#include "cd_image.h" #include "cd_image.h"
#include "cd_subchannel_replacement.h" #include "cd_subchannel_replacement.h"
#include "error.h"
#include "file_system.h" #include "file_system.h"
#include "log.h" #include "log.h"
#include <algorithm> #include <algorithm>
#include <cerrno> #include <cerrno>
#include <fstream> #include <sstream>
#include <map> #include <map>
Log_SetChannel(CDImageMemory); Log_SetChannel(CDImageMemory);
@ -48,13 +49,19 @@ CDImageM3u::~CDImageM3u() = default;
bool CDImageM3u::Open(const char* path, Common::Error* error) bool CDImageM3u::Open(const char* path, Common::Error* error)
{ {
std::ifstream ifs(path); std::FILE* fp = FileSystem::OpenCFile(path, "rb");
if (!ifs.is_open()) if (!fp)
return false;
std::optional<std::string> 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; return false;
} }
std::istringstream ifs(cue_file.value());
m_filename = path; m_filename = path;
std::vector<std::string> entries; std::vector<std::string> entries;
@ -79,14 +86,12 @@ bool CDImageM3u::Open(const char* path, Common::Error* error)
continue; continue;
Entry entry; Entry entry;
entry.filename.assign(line.begin() + start_offset, line.begin() + end_offset + 1); std::string entry_filename(line.begin() + start_offset, line.begin() + end_offset + 1);
entry.title = FileSystem::GetFileTitleFromPath(entry.filename); entry.title = FileSystem::GetFileTitleFromPath(entry_filename);
if (!FileSystem::IsAbsolutePath(entry.filename)) if (!FileSystem::IsAbsolutePath(entry_filename))
{ entry.filename = FileSystem::BuildRelativePath(path, entry_filename);
SmallString absolute_path; else
FileSystem::BuildPathRelativeToFile(absolute_path, path, entry.filename.c_str()); entry.filename = std::move(entry_filename);
entry.filename = absolute_path;
}
Log_DevPrintf("Read path from m3u: '%s'", entry.filename.c_str()); Log_DevPrintf("Read path from m3u: '%s'", entry.filename.c_str());
m_entries.push_back(std::move(entry)); m_entries.push_back(std::move(entry));

View file

@ -283,13 +283,29 @@ std::string ReplaceExtension(const std::string_view& path, const std::string_vie
return ret; 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) std::string_view GetPathDirectory(const std::string_view& path)
{ {
#ifdef _WIN32 std::string::size_type pos = GetLastSeperatorPosition(path, false);
std::string::size_type pos = path.find_last_of("/\\");
#else
std::string::size_type pos = path.find_last_of("/");
#endif
if (pos == std::string_view::npos) if (pos == std::string_view::npos)
return {}; return {};
@ -298,15 +314,11 @@ std::string_view GetPathDirectory(const std::string_view& path)
std::string_view GetFileNameFromPath(const std::string_view& path) std::string_view GetFileNameFromPath(const std::string_view& path)
{ {
#ifdef _WIN32 std::string::size_type pos = GetLastSeperatorPosition(path, true);
std::string::size_type pos = path.find_last_of("/\\");
#else
std::string::size_type pos = path.find_last_of("/");
#endif
if (pos == std::string_view::npos) if (pos == std::string_view::npos)
return path; return path;
return path.substr(pos + 1); return path.substr(pos);
} }
std::string_view GetFileTitleFromPath(const std::string_view& path) std::string_view GetFileTitleFromPath(const std::string_view& path)
@ -346,108 +358,14 @@ std::vector<std::string> GetRootDirectoryList()
return results; return results;
} }
void BuildPathRelativeToFile(char* Destination, u32 cbDestination, const char* CurrentFileName, const char* NewFileName, std::string BuildRelativePath(const std::string_view& filename, const std::string_view& new_filename)
bool OSPath /* = true */, bool Canonicalize /* = true */)
{ {
s32 i; std::string new_string;
u32 currentPos = 0; std::string_view::size_type pos = GetLastSeperatorPosition(filename, true);
DebugAssert(Destination != nullptr && cbDestination > 0 && CurrentFileName != nullptr && NewFileName != nullptr); if (pos != std::string_view::npos)
new_string.assign(filename, 0, pos);
// clone to a local buffer if the same pointer new_string.append(new_filename);
std::string pathClone; return new_string;
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<u32>(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::unique_ptr<ByteStream> OpenFile(const char* FileName, u32 Flags) std::unique_ptr<ByteStream> OpenFile(const char* FileName, u32 Flags)

View file

@ -126,13 +126,8 @@ void BuildOSPath(char* Destination, u32 cbDestination, const char* Path);
void BuildOSPath(String& Destination, const char* Path); void BuildOSPath(String& Destination, const char* Path);
void BuildOSPath(String& Destination); void BuildOSPath(String& Destination);
// builds a path relative to the specified file, optionally canonicalizing it // builds a path relative to the specified file
void BuildPathRelativeToFile(char* Destination, u32 cbDestination, const char* CurrentFileName, const char* NewFileName, std::string BuildRelativePath(const std::string_view& filename, const std::string_view& new_filename);
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);
// sanitizes a filename for use in a filesystem. // sanitizes a filename for use in a filesystem.
void SanitizeFileName(char* Destination, u32 cbDestination, const char* FileName, bool StripSlashes = true); void SanitizeFileName(char* Destination, u32 cbDestination, const char* FileName, bool StripSlashes = true);

View file

@ -162,14 +162,6 @@ bool File::Load(const char* path)
return true; 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) 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 // 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<std::string> lib_name(file.GetTagString("_lib")); std::optional<std::string> lib_name(file.GetTagString("_lib"));
if (lib_name.has_value()) 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()); Log_InfoPrintf("Loading main parent PSF '%s'", lib_path.c_str());
// We should use the initial SP/PC from the **first** parent lib. // 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()) if (!lib_name.has_value())
break; 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()); Log_InfoPrintf("Loading parent PSF '%s'", lib_path.c_str());
if (!LoadLibraryPSF(lib_path.c_str(), false, depth + 1)) if (!LoadLibraryPSF(lib_path.c_str(), false, depth + 1))
{ {