mirror of https://github.com/PCSX2/pcsx2.git
Path: Add CreateFileURL()
This commit is contained in:
parent
b4992856f7
commit
515cbc7b29
|
@ -759,6 +759,115 @@ std::string Path::Combine(const std::string_view& base, const std::string_view&
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
std::string Path::URLEncode(std::string_view str)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length() + ((str.length() + 3) / 4) * 3);
|
||||
|
||||
for (size_t i = 0, l = str.size(); i < l; i++)
|
||||
{
|
||||
const char c = str[i];
|
||||
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '_' ||
|
||||
c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || c == ')')
|
||||
{
|
||||
ret.push_back(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back('%');
|
||||
|
||||
const unsigned char n1 = static_cast<unsigned char>(c) >> 4;
|
||||
const unsigned char n2 = static_cast<unsigned char>(c) & 0x0F;
|
||||
ret.push_back((n1 >= 10) ? ('a' + (n1 - 10)) : ('0' + n1));
|
||||
ret.push_back((n2 >= 10) ? ('a' + (n2 - 10)) : ('0' + n2));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Path::URLDecode(std::string_view str)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length());
|
||||
|
||||
for (size_t i = 0, l = str.size(); i < l; i++)
|
||||
{
|
||||
const char c = str[i];
|
||||
if (c == '+')
|
||||
{
|
||||
ret.push_back(c);
|
||||
}
|
||||
else if (c == '%')
|
||||
{
|
||||
if ((i + 2) >= str.length())
|
||||
break;
|
||||
|
||||
const char clower = str[i + 1];
|
||||
const char cupper = str[i + 2];
|
||||
const unsigned char lower =
|
||||
(clower >= '0' && clower <= '9') ?
|
||||
static_cast<unsigned char>(clower - '0') :
|
||||
((clower >= 'a' && clower <= 'f') ?
|
||||
static_cast<unsigned char>(clower - 'a') :
|
||||
((clower >= 'A' && clower <= 'F') ? static_cast<unsigned char>(clower - 'A') : 0));
|
||||
const unsigned char upper =
|
||||
(cupper >= '0' && cupper <= '9') ?
|
||||
static_cast<unsigned char>(cupper - '0') :
|
||||
((cupper >= 'a' && cupper <= 'f') ?
|
||||
static_cast<unsigned char>(cupper - 'a') :
|
||||
((cupper >= 'A' && cupper <= 'F') ? static_cast<unsigned char>(cupper - 'A') : 0));
|
||||
const char dch = static_cast<char>(lower | (upper << 4));
|
||||
ret.push_back(dch);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
std::string Path::CreateFileURL(std::string_view path)
|
||||
{
|
||||
pxAssert(IsAbsolute(path));
|
||||
|
||||
std::string ret;
|
||||
ret.reserve(path.length() + 10);
|
||||
ret.append("file://");
|
||||
|
||||
const std::vector<std::string_view> components = SplitNativePath(path);
|
||||
pxAssertRel(!components.empty(), "Trying to create a URL from an empty path.");
|
||||
|
||||
const std::string_view& first = components.front();
|
||||
#ifdef _WIN32
|
||||
// Windows doesn't urlencode the drive letter.
|
||||
// UNC paths should be omit the leading slash.
|
||||
if (first.starts_with("\\\\"))
|
||||
{
|
||||
// file://hostname/...
|
||||
ret.append(first.substr(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// file:///c:/...
|
||||
fmt::format_to(std::back_inserter(ret), "/{}", first);
|
||||
}
|
||||
#else
|
||||
// Don't append a leading slash for the first component.
|
||||
ret.append(first);
|
||||
#endif
|
||||
|
||||
for (size_t comp = 1; comp < components.size(); comp++)
|
||||
{
|
||||
fmt::format_to(std::back_inserter(ret), "/{}", URLEncode(components[comp]));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::FILE* FileSystem::OpenCFile(const char* filename, const char* mode, Error* error)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -227,69 +227,6 @@ bool HTTPDownloader::HasAnyRequests()
|
|||
return !m_pending_http_requests.empty();
|
||||
}
|
||||
|
||||
std::string HTTPDownloader::URLEncode(const std::string_view& str)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length() + ((str.length() + 3) / 4) * 3);
|
||||
|
||||
for (size_t i = 0, l = str.size(); i < l; i++)
|
||||
{
|
||||
const char c = str[i];
|
||||
if ((c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
c == '-' || c == '_' || c == '.' || c == '!' || c == '~' ||
|
||||
c == '*' || c == '\'' || c == '(' || c == ')')
|
||||
{
|
||||
ret.push_back(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back('%');
|
||||
|
||||
const unsigned char n1 = static_cast<unsigned char>(c) >> 4;
|
||||
const unsigned char n2 = static_cast<unsigned char>(c) & 0x0F;
|
||||
ret.push_back((n1 >= 10) ? ('a' + (n1 - 10)) : ('0' + n1));
|
||||
ret.push_back((n2 >= 10) ? ('a' + (n2 - 10)) : ('0' + n2));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string HTTPDownloader::URLDecode(const std::string_view& str)
|
||||
{
|
||||
std::string ret;
|
||||
ret.reserve(str.length());
|
||||
|
||||
for (size_t i = 0, l = str.size(); i < l; i++)
|
||||
{
|
||||
const char c = str[i];
|
||||
if (c == '+')
|
||||
{
|
||||
ret.push_back(c);
|
||||
}
|
||||
else if (c == '%')
|
||||
{
|
||||
if ((i + 2) >= str.length())
|
||||
break;
|
||||
|
||||
const char clower = str[i + 1];
|
||||
const char cupper = str[i + 2];
|
||||
const unsigned char lower = (clower >= '0' && clower <= '9') ? static_cast<unsigned char>(clower - '0') : ((clower >= 'a' && clower <= 'f') ? static_cast<unsigned char>(clower - 'a') : ((clower >= 'A' && clower <= 'F') ? static_cast<unsigned char>(clower - 'A') : 0));
|
||||
const unsigned char upper = (cupper >= '0' && cupper <= '9') ? static_cast<unsigned char>(cupper - '0') : ((cupper >= 'a' && cupper <= 'f') ? static_cast<unsigned char>(cupper - 'a') : ((cupper >= 'A' && cupper <= 'F') ? static_cast<unsigned char>(cupper - 'A') : 0));
|
||||
const char dch = static_cast<char>(lower | (upper << 4));
|
||||
ret.push_back(dch);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
std::string HTTPDownloader::GetExtensionForContentType(const std::string& content_type)
|
||||
{
|
||||
// Based on https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
||||
|
|
|
@ -66,8 +66,6 @@ public:
|
|||
virtual ~HTTPDownloader();
|
||||
|
||||
static std::unique_ptr<HTTPDownloader> Create(std::string user_agent = DEFAULT_USER_AGENT);
|
||||
static std::string URLEncode(const std::string_view& str);
|
||||
static std::string URLDecode(const std::string_view& str);
|
||||
static std::string GetExtensionForContentType(const std::string& content_type);
|
||||
|
||||
void SetTimeout(float timeout);
|
||||
|
|
|
@ -75,4 +75,13 @@ namespace Path
|
|||
/// Splits a path into its components, only handling native separators.
|
||||
std::vector<std::string_view> SplitNativePath(const std::string_view& path);
|
||||
std::string JoinNativePath(const std::vector<std::string_view>& components);
|
||||
|
||||
/// URL encodes the specified string.
|
||||
std::string URLEncode(std::string_view str);
|
||||
|
||||
/// Decodes the specified escaped string.
|
||||
std::string URLDecode(std::string_view str);
|
||||
|
||||
/// Returns a URL for a given path. The path should be absolute.
|
||||
std::string CreateFileURL(std::string_view path);
|
||||
} // namespace Path
|
||||
|
|
|
@ -1367,7 +1367,7 @@ QString QtHost::GetResourcesBasePath()
|
|||
|
||||
std::string QtHost::GetRuntimeDownloadedResourceURL(std::string_view name)
|
||||
{
|
||||
return fmt::format("{}/{}", RUNTIME_RESOURCES_URL, HTTPDownloader::URLEncode(name));
|
||||
return fmt::format("{}/{}", RUNTIME_RESOURCES_URL, Path::URLEncode(name));
|
||||
}
|
||||
|
||||
std::optional<bool> QtHost::DownloadFile(QWidget* parent, const QString& title, std::string url, std::vector<u8>* data)
|
||||
|
|
|
@ -1260,11 +1260,11 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
|||
{
|
||||
std::string url(url_template);
|
||||
if (has_title)
|
||||
StringUtil::ReplaceAll(&url, "${title}", HTTPDownloader::URLEncode(entry.title));
|
||||
StringUtil::ReplaceAll(&url, "${title}", Path::URLEncode(entry.title));
|
||||
if (has_file_title)
|
||||
StringUtil::ReplaceAll(&url, "${filetitle}", HTTPDownloader::URLEncode(Path::GetFileTitle(entry.path)));
|
||||
StringUtil::ReplaceAll(&url, "${filetitle}", Path::URLEncode(Path::GetFileTitle(entry.path)));
|
||||
if (has_serial)
|
||||
StringUtil::ReplaceAll(&url, "${serial}", HTTPDownloader::URLEncode(entry.serial));
|
||||
StringUtil::ReplaceAll(&url, "${serial}", Path::URLEncode(entry.serial));
|
||||
|
||||
download_urls.emplace_back(entry.path, std::move(url));
|
||||
}
|
||||
|
@ -1305,7 +1305,7 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
|||
}
|
||||
|
||||
// we could actually do a few in parallel here...
|
||||
std::string filename(HTTPDownloader::URLDecode(url));
|
||||
std::string filename = Path::URLDecode(url);
|
||||
downloader->CreateRequest(
|
||||
std::move(url), [use_serial, &save_callback, entry_path = std::move(entry_path), filename = std::move(filename)](
|
||||
s32 status_code, const std::string& content_type, HTTPDownloader::Request::Data data) {
|
||||
|
|
|
@ -228,6 +228,16 @@ TEST(Path, ChangeFileName)
|
|||
#endif
|
||||
}
|
||||
|
||||
TEST(Path, CreateFileURL)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ASSERT_EQ(Path::CreateFileURL("C:\\foo\\bar"), "file:///C:/foo/bar");
|
||||
ASSERT_EQ(Path::CreateFileURL("\\\\server\\share\\file.txt"), "file://server/share/file.txt");
|
||||
#else
|
||||
ASSERT_EQ(Path::CreateFileURL("/foo/bar"), "file:///foo/bar");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
// Relies on presence of files.
|
||||
|
|
Loading…
Reference in New Issue