mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-03 21:41:26 +12:00
288 lines
4.4 KiB
C++
288 lines
4.4 KiB
C++
#include "stdafx.h"
|
||
#include "IdManager.h"
|
||
#include "VFS.h"
|
||
|
||
#include <regex>
|
||
|
||
struct vfs_manager
|
||
{
|
||
shared_mutex mutex;
|
||
|
||
// Device name -> Real path
|
||
std::unordered_map<std::string, std::string> mounted;
|
||
};
|
||
|
||
const std::regex s_regex_ps3("^/+(.*?)(?:$|/)(.*)", std::regex::optimize);
|
||
const std::regex s_regex_psv("^(.*?):(.*)", std::regex::optimize);
|
||
|
||
bool vfs::mount(const std::string& dev_name, const std::string& path)
|
||
{
|
||
const auto table = fxm::get_always<vfs_manager>();
|
||
|
||
writer_lock lock(table->mutex);
|
||
|
||
return table->mounted.emplace(dev_name, path).second;
|
||
}
|
||
|
||
std::string vfs::get(const std::string& vpath, vfs::type _type)
|
||
{
|
||
const auto table = fxm::get_always<vfs_manager>();
|
||
|
||
reader_lock lock(table->mutex);
|
||
|
||
std::smatch match;
|
||
|
||
if (!std::regex_match(vpath, match, _type == type::ps3 ? s_regex_ps3 : s_regex_psv))
|
||
{
|
||
const auto found = table->mounted.find("");
|
||
|
||
if (found == table->mounted.end())
|
||
{
|
||
LOG_WARNING(GENERAL, "vfs::get(): no default directory: %s", vpath);
|
||
return {};
|
||
}
|
||
|
||
return found->second + vfs::escape(vpath);
|
||
}
|
||
|
||
if (_type == type::ps3 && match.length(1) == 0)
|
||
{
|
||
return "/";
|
||
}
|
||
|
||
const auto found = table->mounted.find(match.str(1));
|
||
|
||
if (found == table->mounted.end())
|
||
{
|
||
LOG_WARNING(GENERAL, "vfs::get(): device not found: %s", vpath);
|
||
return {};
|
||
}
|
||
|
||
if (found->second.empty())
|
||
{
|
||
// Don't escape /host_root (TODO)
|
||
return match.str(2);
|
||
}
|
||
|
||
// Escape and concatenate
|
||
return found->second + vfs::escape(match.str(2));
|
||
}
|
||
|
||
|
||
std::string vfs::escape(const std::string& path)
|
||
{
|
||
std::string result;
|
||
result.reserve(path.size());
|
||
|
||
for (std::size_t i = 0, s = path.size(); i < s; i++)
|
||
{
|
||
switch (char c = path[i])
|
||
{
|
||
case '<':
|
||
{
|
||
result += u8"<";
|
||
break;
|
||
}
|
||
case '>':
|
||
{
|
||
result += u8">";
|
||
break;
|
||
}
|
||
case ':':
|
||
{
|
||
result += u8":";
|
||
break;
|
||
}
|
||
case '"':
|
||
{
|
||
result += u8""";
|
||
break;
|
||
}
|
||
case '\\':
|
||
{
|
||
result += u8"\";
|
||
break;
|
||
}
|
||
case '|':
|
||
{
|
||
result += u8"|";
|
||
break;
|
||
}
|
||
case '?':
|
||
{
|
||
result += u8"?";
|
||
break;
|
||
}
|
||
case '*':
|
||
{
|
||
result += u8"*";
|
||
break;
|
||
}
|
||
case char{u8"!"[0]}:
|
||
{
|
||
// Escape full-width characters 0xFF01..0xFF5e with ! (0xFF01)
|
||
switch (path[i + 1])
|
||
{
|
||
case char{u8"!"[1]}:
|
||
{
|
||
const uchar c3 = reinterpret_cast<const uchar&>(path[i + 2]);
|
||
|
||
if (c3 >= 0x81 && c3 <= 0xbf)
|
||
{
|
||
result += u8"!";
|
||
}
|
||
|
||
break;
|
||
}
|
||
case char{u8"`"[1]}:
|
||
{
|
||
const uchar c3 = reinterpret_cast<const uchar&>(path[i + 2]);
|
||
|
||
if (c3 >= 0x80 && c3 <= 0x9e)
|
||
{
|
||
result += u8"!";
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
result += c;
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
result += c;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
std::string vfs::unescape(const std::string& path)
|
||
{
|
||
std::string result;
|
||
result.reserve(path.size());
|
||
|
||
for (std::size_t i = 0, s = path.size(); i < s; i++)
|
||
{
|
||
switch (char c = path[i])
|
||
{
|
||
case char{u8"!"[0]}:
|
||
{
|
||
switch (path[i + 1])
|
||
{
|
||
case char{u8"!"[1]}:
|
||
{
|
||
const uchar c3 = reinterpret_cast<const uchar&>(path[i + 2]);
|
||
|
||
if (c3 >= 0x81 && c3 <= 0xbf)
|
||
{
|
||
switch (path[i + 2])
|
||
{
|
||
case char{u8"!"[2]}:
|
||
{
|
||
i += 3;
|
||
result += c;
|
||
continue;
|
||
}
|
||
case char{u8"<"[2]}:
|
||
{
|
||
result += '<';
|
||
break;
|
||
}
|
||
case char{u8">"[2]}:
|
||
{
|
||
result += '>';
|
||
break;
|
||
}
|
||
case char{u8":"[2]}:
|
||
{
|
||
result += ':';
|
||
break;
|
||
}
|
||
case char{u8"""[2]}:
|
||
{
|
||
result += '"';
|
||
break;
|
||
}
|
||
case char{u8"\"[2]}:
|
||
{
|
||
result += '\\';
|
||
break;
|
||
}
|
||
case char{u8"?"[2]}:
|
||
{
|
||
result += '?';
|
||
break;
|
||
}
|
||
case char{u8"*"[2]}:
|
||
{
|
||
result += '*';
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
// Unrecognized character (ignored)
|
||
break;
|
||
}
|
||
}
|
||
|
||
i += 2;
|
||
}
|
||
else
|
||
{
|
||
result += c;
|
||
}
|
||
|
||
break;
|
||
}
|
||
case char{u8"`"[1]}:
|
||
{
|
||
const uchar c3 = reinterpret_cast<const uchar&>(path[i + 2]);
|
||
|
||
if (c3 >= 0x80 && c3 <= 0x9e)
|
||
{
|
||
switch (path[i + 2])
|
||
{
|
||
case char{u8"|"[2]}:
|
||
{
|
||
result += '|';
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
// Unrecognized character (ignored)
|
||
break;
|
||
}
|
||
}
|
||
|
||
i += 2;
|
||
}
|
||
else
|
||
{
|
||
result += c;
|
||
}
|
||
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
result += c;
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
result += c;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|