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;
}
// 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)
{

View file

@ -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 <algorithm>
#include <cerrno>
#include <fstream>
#include <sstream>
#include <map>
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<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;
}
std::istringstream ifs(cue_file.value());
m_filename = path;
std::vector<std::string> 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));

View file

@ -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<std::string> 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<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::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<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);
// 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);

View file

@ -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<std::string> 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))
{