mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-29 12:34:17 +01:00
Merge pull request #231 from Exzap/main
FSC: Replace wstring paths with utf8 encoded strings + misc clean up
This commit is contained in:
commit
3349d7b424
@ -209,7 +209,7 @@ void InfoLog_TitleLoaded()
|
|||||||
fs::path effectiveSavePath = getTitleSavePath();
|
fs::path effectiveSavePath = getTitleSavePath();
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
const bool saveDirExists = fs::exists(effectiveSavePath, ec);
|
const bool saveDirExists = fs::exists(effectiveSavePath, ec);
|
||||||
cemuLog_force("Save path: {}{}", _utf8Wrapper(effectiveSavePath), saveDirExists ? "" : " (not present)");
|
cemuLog_force("Save path: {}{}", _pathToUtf8(effectiveSavePath), saveDirExists ? "" : " (not present)");
|
||||||
|
|
||||||
// log shader cache name
|
// log shader cache name
|
||||||
cemuLog_log(LogType::Force, "Shader cache file: shaderCache/transferable/{:016x}.bin", titleId);
|
cemuLog_log(LogType::Force, "Shader cache file: shaderCache/transferable/{:016x}.bin", titleId);
|
||||||
@ -617,7 +617,7 @@ namespace CafeSystem
|
|||||||
sLaunchModeIsStandalone = true;
|
sLaunchModeIsStandalone = true;
|
||||||
cemuLog_log(LogType::Force, "Launching executable in standalone mode due to incorrect layout or missing meta files");
|
cemuLog_log(LogType::Force, "Launching executable in standalone mode due to incorrect layout or missing meta files");
|
||||||
fs::path executablePath = path;
|
fs::path executablePath = path;
|
||||||
std::string dirName = _utf8Wrapper(executablePath.parent_path().filename());
|
std::string dirName = _pathToUtf8(executablePath.parent_path().filename());
|
||||||
if (boost::iequals(dirName, "code"))
|
if (boost::iequals(dirName, "code"))
|
||||||
{
|
{
|
||||||
// check for content folder
|
// check for content folder
|
||||||
@ -626,18 +626,18 @@ namespace CafeSystem
|
|||||||
if (fs::is_directory(contentPath, ec))
|
if (fs::is_directory(contentPath, ec))
|
||||||
{
|
{
|
||||||
// mounting content folder
|
// mounting content folder
|
||||||
bool r = FSCDeviceHostFS_Mount(std::string("/vol/content").c_str(), boost::nowide::widen(_utf8Wrapper(contentPath)).c_str(), FSC_PRIORITY_BASE);
|
bool r = FSCDeviceHostFS_Mount(std::string("/vol/content").c_str(), _pathToUtf8(contentPath), FSC_PRIORITY_BASE);
|
||||||
if (!r)
|
if (!r)
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Failed to mount {}", _utf8Wrapper(contentPath).c_str());
|
cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath));
|
||||||
return STATUS_CODE::UNABLE_TO_MOUNT;
|
return STATUS_CODE::UNABLE_TO_MOUNT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// mount code folder to a virtual temporary path
|
// mount code folder to a virtual temporary path
|
||||||
FSCDeviceHostFS_Mount(std::string("/internal/code/").c_str(), boost::nowide::widen(_utf8Wrapper(executablePath.parent_path())).c_str(), FSC_PRIORITY_BASE);
|
FSCDeviceHostFS_Mount(std::string("/internal/code/").c_str(), _pathToUtf8(executablePath.parent_path()), FSC_PRIORITY_BASE);
|
||||||
std::string internalExecutablePath = "/internal/code/";
|
std::string internalExecutablePath = "/internal/code/";
|
||||||
internalExecutablePath.append(_utf8Wrapper(executablePath.filename()));
|
internalExecutablePath.append(_pathToUtf8(executablePath.filename()));
|
||||||
_pathToExecutable = internalExecutablePath;
|
_pathToExecutable = internalExecutablePath;
|
||||||
// since a lot of systems (including save folder location) rely on a TitleId, we derive a placeholder id from the executable hash
|
// since a lot of systems (including save folder location) rely on a TitleId, we derive a placeholder id from the executable hash
|
||||||
auto execData = fsc_extractFile(_pathToExecutable.c_str());
|
auto execData = fsc_extractFile(_pathToExecutable.c_str());
|
||||||
|
@ -1,347 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
class parsedPathW
|
|
||||||
{
|
|
||||||
static const int MAX_NODES = 32;
|
|
||||||
|
|
||||||
public:
|
|
||||||
parsedPathW(std::wstring_view path)
|
|
||||||
{
|
|
||||||
// init vars
|
|
||||||
this->numNodes = 0;
|
|
||||||
// init parsed path data
|
|
||||||
if (path.front() == '/')
|
|
||||||
path.remove_prefix(1);
|
|
||||||
pathData.assign(path.begin(), path.end());
|
|
||||||
pathData.push_back('\0');
|
|
||||||
// start parsing
|
|
||||||
sint32 offset = 0;
|
|
||||||
sint32 startOffset = 0;
|
|
||||||
if (offset < pathData.size()-1)
|
|
||||||
{
|
|
||||||
this->nodeOffset[this->numNodes] = offset;
|
|
||||||
this->numNodes++;
|
|
||||||
}
|
|
||||||
while (offset < pathData.size() - 1)
|
|
||||||
{
|
|
||||||
if (this->pathData[offset] == '/' || this->pathData[offset] == '\\')
|
|
||||||
{
|
|
||||||
this->pathData[offset] = '\0';
|
|
||||||
offset++;
|
|
||||||
// double slashes are ignored and instead are handled like a single slash
|
|
||||||
if (this->pathData[offset] == '/' || this->pathData[offset] == '\\')
|
|
||||||
{
|
|
||||||
// if we're in the beginning and having a \\ it's a network path
|
|
||||||
if (offset != 1)
|
|
||||||
{
|
|
||||||
this->pathData[offset] = '\0';
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// start new node
|
|
||||||
if (this->numNodes < MAX_NODES)
|
|
||||||
{
|
|
||||||
if (offset < pathData.size() - 1)
|
|
||||||
{
|
|
||||||
this->nodeOffset[this->numNodes] = offset;
|
|
||||||
this->numNodes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
// handle special nodes like '.' or '..'
|
|
||||||
sint32 nodeIndex = 0;
|
|
||||||
while (nodeIndex < this->numNodes)
|
|
||||||
{
|
|
||||||
if (compareNodeName(nodeIndex, L".."))
|
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
else if (compareNodeName(nodeIndex, L"."))
|
|
||||||
{
|
|
||||||
removeNode(nodeIndex);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nodeIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true if the names match (case sensitive)
|
|
||||||
bool compareNodeName(sint32 index, std::wstring_view name)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= this->numNodes)
|
|
||||||
return false;
|
|
||||||
const wchar_t* nodeName = this->pathData.data() + this->nodeOffset[index];
|
|
||||||
if (boost::iequals(nodeName, name))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool compareNodeName(std::wstring_view name1, std::wstring_view name2)
|
|
||||||
{
|
|
||||||
if (boost::iequals(name1, name2))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool compareNodeNameCaseInsensitive(std::wstring_view name1, std::wstring_view name2)
|
|
||||||
{
|
|
||||||
if (boost::iequals(name1, name2))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wchar_t* getNodeName(sint32 index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= this->numNodes)
|
|
||||||
return nullptr;
|
|
||||||
return this->pathData.data() + this->nodeOffset[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeNode(sint32 index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= numNodes)
|
|
||||||
return;
|
|
||||||
numNodes--;
|
|
||||||
for (sint32 i = 0; i < numNodes; i++)
|
|
||||||
{
|
|
||||||
nodeOffset[i] = nodeOffset[i + 1];
|
|
||||||
}
|
|
||||||
// remove empty space
|
|
||||||
if (numNodes > 0)
|
|
||||||
updateOffsets(nodeOffset[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void prependNode(wchar_t* newNode)
|
|
||||||
{
|
|
||||||
if (numNodes >= MAX_NODES)
|
|
||||||
return;
|
|
||||||
sint32 len = (sint32)wcslen(newNode);
|
|
||||||
updateOffsets(-(len + 1));
|
|
||||||
numNodes++;
|
|
||||||
for (sint32 i = numNodes - 1; i >= 1; i--)
|
|
||||||
{
|
|
||||||
nodeOffset[i] = nodeOffset[i - 1];
|
|
||||||
}
|
|
||||||
nodeOffset[0] = 0;
|
|
||||||
memcpy(pathData.data() + 0, newNode, (len + 1) * sizeof(wchar_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
void buildPathString(std::wstring& pathStr, bool appendSlash = false)
|
|
||||||
{
|
|
||||||
pathStr.resize(0);
|
|
||||||
for (sint32 i = 0; i < numNodes; i++)
|
|
||||||
{
|
|
||||||
if (numNodes > 1)
|
|
||||||
pathStr.append(L"/");
|
|
||||||
pathStr.append(pathData.data() + nodeOffset[i]);
|
|
||||||
}
|
|
||||||
if(appendSlash)
|
|
||||||
pathStr.append(L"/");
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void updateOffsets(sint32 newBaseOffset)
|
|
||||||
{
|
|
||||||
if (numNodes == 0 || newBaseOffset == 0)
|
|
||||||
return;
|
|
||||||
cemu_assert_debug(newBaseOffset <= nodeOffset[0]);
|
|
||||||
if (newBaseOffset > 0)
|
|
||||||
{
|
|
||||||
// decrease size
|
|
||||||
memmove(pathData.data(), pathData.data() + newBaseOffset, (pathData.size() - newBaseOffset) * sizeof(wchar_t));
|
|
||||||
pathData.resize(pathData.size() - newBaseOffset);
|
|
||||||
// update node offsets
|
|
||||||
for (sint32 i = 0; i < numNodes; i++)
|
|
||||||
{
|
|
||||||
nodeOffset[i] -= newBaseOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// increase size
|
|
||||||
newBaseOffset = -newBaseOffset;
|
|
||||||
pathData.resize(pathData.size() + newBaseOffset);
|
|
||||||
memmove(pathData.data() + newBaseOffset, pathData.data(), (pathData.size() - newBaseOffset) * sizeof(wchar_t));
|
|
||||||
// update node offsets
|
|
||||||
for (sint32 i = 0; i < numNodes; i++)
|
|
||||||
{
|
|
||||||
nodeOffset[i] += newBaseOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
//std::wstring pathData;
|
|
||||||
std::vector<wchar_t> pathData;
|
|
||||||
sint32 nodeOffset[MAX_NODES + 1];
|
|
||||||
|
|
||||||
public:
|
|
||||||
sint32 numNodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<typename F, bool isCaseSensitive>
|
|
||||||
class FileTree
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
enum NODETYPE : uint8
|
|
||||||
{
|
|
||||||
NODETYPE_DIRECTORY,
|
|
||||||
NODETYPE_FILE,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _node_t
|
|
||||||
{
|
|
||||||
std::wstring name;
|
|
||||||
std::vector<_node_t*> subnodes;
|
|
||||||
F* custom;
|
|
||||||
NODETYPE type;
|
|
||||||
}node_t;
|
|
||||||
|
|
||||||
node_t* getByNodePath(parsedPathW& p, sint32 numNodes, bool createAsDirectories)
|
|
||||||
{
|
|
||||||
node_t* currentNode = &rootNode;
|
|
||||||
for (sint32 i = 0; i < numNodes; i++)
|
|
||||||
{
|
|
||||||
// find subnode by path
|
|
||||||
node_t* foundSubnode = getSubnode(currentNode, p.getNodeName(i));
|
|
||||||
if (foundSubnode == nullptr)
|
|
||||||
{
|
|
||||||
// no subnode found -> create new directory node (if requested)
|
|
||||||
if (createAsDirectories == false)
|
|
||||||
return nullptr; // path not found
|
|
||||||
currentNode = newNode(currentNode, NODETYPE_DIRECTORY, p.getNodeName(i));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentNode = foundSubnode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t* getSubnode(node_t* parentNode, std::wstring_view name)
|
|
||||||
{
|
|
||||||
for (auto& sn : parentNode->subnodes)
|
|
||||||
{
|
|
||||||
if constexpr (isCaseSensitive)
|
|
||||||
{
|
|
||||||
if (parsedPathW::compareNodeName(sn->name.c_str(), name))
|
|
||||||
return sn;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (parsedPathW::compareNodeNameCaseInsensitive(sn->name.c_str(), name))
|
|
||||||
return sn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_t* newNode(node_t* parentNode, NODETYPE type, std::wstring_view name)
|
|
||||||
{
|
|
||||||
node_t* newNode = new node_t;
|
|
||||||
newNode->name = std::wstring(name);
|
|
||||||
newNode->type = type;
|
|
||||||
newNode->custom = nullptr;
|
|
||||||
parentNode->subnodes.push_back(newNode);
|
|
||||||
return newNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
FileTree()
|
|
||||||
{
|
|
||||||
rootNode.type = NODETYPE_DIRECTORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool addFile(const wchar_t* path, F* custom)
|
|
||||||
{
|
|
||||||
parsedPathW p(path);
|
|
||||||
if (p.numNodes == 0)
|
|
||||||
return false;
|
|
||||||
node_t* directoryNode = getByNodePath(p, p.numNodes - 1, true);
|
|
||||||
// check if a node with same name already exists
|
|
||||||
if (getSubnode(directoryNode, p.getNodeName(p.numNodes - 1)) != nullptr)
|
|
||||||
return false; // node already exists
|
|
||||||
// add file node
|
|
||||||
node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.getNodeName(p.numNodes - 1));
|
|
||||||
fileNode->custom = custom;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getFile(std::wstring_view path, F* &custom)
|
|
||||||
{
|
|
||||||
parsedPathW p(path);
|
|
||||||
if (p.numNodes == 0)
|
|
||||||
return false;
|
|
||||||
node_t* node = getByNodePath(p, p.numNodes, false);
|
|
||||||
if (node == nullptr)
|
|
||||||
return false;
|
|
||||||
if (node->type != NODETYPE_FILE)
|
|
||||||
return false;
|
|
||||||
custom = node->custom;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool removeFile(std::wstring_view path)
|
|
||||||
{
|
|
||||||
parsedPathW p(path);
|
|
||||||
if (p.numNodes == 0)
|
|
||||||
return false;
|
|
||||||
node_t* directoryNode = getByNodePath(p, p.numNodes - 1, false);
|
|
||||||
if (directoryNode == nullptr)
|
|
||||||
return false;
|
|
||||||
// find node
|
|
||||||
node_t* fileNode = getSubnode(directoryNode, p.getNodeName(p.numNodes - 1));
|
|
||||||
if (fileNode == nullptr)
|
|
||||||
return false;
|
|
||||||
if (fileNode->type != NODETYPE_FILE)
|
|
||||||
return false;
|
|
||||||
if (fileNode->subnodes.empty() == false)
|
|
||||||
{
|
|
||||||
// files shouldn't have subnodes
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
// remove node from parent
|
|
||||||
directoryNode->subnodes.erase(std::remove(directoryNode->subnodes.begin(), directoryNode->subnodes.end(), fileNode), directoryNode->subnodes.end());
|
|
||||||
// delete node
|
|
||||||
delete fileNode;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename TFunc>
|
|
||||||
bool listDirectory(const wchar_t* path, TFunc fn)
|
|
||||||
{
|
|
||||||
parsedPathW p(path);
|
|
||||||
node_t* node = getByNodePath(p, p.numNodes, false);
|
|
||||||
if (node == nullptr)
|
|
||||||
return false;
|
|
||||||
if (node->type != NODETYPE_DIRECTORY)
|
|
||||||
return false;
|
|
||||||
for (auto& it : node->subnodes)
|
|
||||||
{
|
|
||||||
if (it->type == NODETYPE_DIRECTORY)
|
|
||||||
{
|
|
||||||
fn(it->name, true, it->custom);
|
|
||||||
}
|
|
||||||
else if (it->type == NODETYPE_FILE)
|
|
||||||
{
|
|
||||||
fn(it->name, false, it->custom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
node_t rootNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
// path parser and utility class for Wii U paths
|
// path parser and utility class for Wii U paths
|
||||||
@ -383,7 +42,7 @@ public:
|
|||||||
m_isAbsolute = true;
|
m_isAbsolute = true;
|
||||||
path.remove_prefix(1);
|
path.remove_prefix(1);
|
||||||
// skip any additional leading slashes
|
// skip any additional leading slashes
|
||||||
while(!path.empty() && isSlash(path.front()))
|
while (!path.empty() && isSlash(path.front()))
|
||||||
path.remove_prefix(1);
|
path.remove_prefix(1);
|
||||||
}
|
}
|
||||||
// parse nodes
|
// parse nodes
|
||||||
@ -427,17 +86,15 @@ public:
|
|||||||
return std::basic_string_view<char>(m_names.data() + m_nodes[index].offset, m_nodes[index].len);
|
return std::basic_string_view<char>(m_names.data() + m_nodes[index].offset, m_nodes[index].len);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MatchNode(sint32 index, std::string_view name) const
|
// returns true if the node names match according to FSA case-insensitivity rules
|
||||||
|
static bool MatchNodeName(std::string_view name1, std::string_view name2)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= (sint32)m_nodes.size())
|
if (name1.size() != name2.size())
|
||||||
return false;
|
return false;
|
||||||
auto nodeName = GetNodeName(index);
|
for (size_t i = 0; i < name1.size(); i++)
|
||||||
if (nodeName.size() != name.size())
|
|
||||||
return false;
|
|
||||||
for (size_t i = 0; i < nodeName.size(); i++)
|
|
||||||
{
|
{
|
||||||
char c1 = nodeName[i];
|
char c1 = name1[i];
|
||||||
char c2 = name[i];
|
char c2 = name2[i];
|
||||||
if (c1 >= 'A' && c1 <= 'Z')
|
if (c1 >= 'A' && c1 <= 'Z')
|
||||||
c1 += ('a' - 'A');
|
c1 += ('a' - 'A');
|
||||||
if (c2 >= 'A' && c2 <= 'Z')
|
if (c2 >= 'A' && c2 <= 'Z')
|
||||||
@ -447,6 +104,165 @@ public:
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MatchNodeName(sint32 index, std::string_view name) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= (sint32)m_nodes.size())
|
||||||
|
return false;
|
||||||
|
auto nodeName = GetNodeName(index);
|
||||||
|
return MatchNodeName(nodeName, name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
class FSAFileTree
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum NODETYPE : uint8
|
||||||
|
{
|
||||||
|
NODETYPE_DIRECTORY,
|
||||||
|
NODETYPE_FILE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node_t
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::vector<node_t*> subnodes;
|
||||||
|
F* custom;
|
||||||
|
NODETYPE type;
|
||||||
|
};
|
||||||
|
|
||||||
|
node_t* getByNodePath(FSCPath& p, sint32 numNodes, bool createAsDirectories)
|
||||||
|
{
|
||||||
|
node_t* currentNode = &rootNode;
|
||||||
|
for (sint32 i = 0; i < numNodes; i++)
|
||||||
|
{
|
||||||
|
// find subnode by path
|
||||||
|
node_t* foundSubnode = getSubnode(currentNode, p.GetNodeName(i));
|
||||||
|
if (foundSubnode == nullptr)
|
||||||
|
{
|
||||||
|
// no subnode found -> create new directory node (if requested)
|
||||||
|
if (createAsDirectories == false)
|
||||||
|
return nullptr; // path not found
|
||||||
|
currentNode = newNode(currentNode, NODETYPE_DIRECTORY, p.GetNodeName(i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentNode = foundSubnode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t* getSubnode(node_t* parentNode, std::string_view name)
|
||||||
|
{
|
||||||
|
for (auto& sn : parentNode->subnodes)
|
||||||
|
{
|
||||||
|
if (FSCPath::MatchNodeName(sn->name, name))
|
||||||
|
return sn;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t* newNode(node_t* parentNode, NODETYPE type, std::string_view name)
|
||||||
|
{
|
||||||
|
node_t* newNode = new node_t;
|
||||||
|
newNode->name.assign(name);
|
||||||
|
newNode->type = type;
|
||||||
|
newNode->custom = nullptr;
|
||||||
|
parentNode->subnodes.push_back(newNode);
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FSAFileTree()
|
||||||
|
{
|
||||||
|
rootNode.type = NODETYPE_DIRECTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addFile(std::string_view path, F* custom)
|
||||||
|
{
|
||||||
|
FSCPath p(path);
|
||||||
|
if (p.GetNodeCount() == 0)
|
||||||
|
return false;
|
||||||
|
node_t* directoryNode = getByNodePath(p, p.GetNodeCount() - 1, true);
|
||||||
|
// check if a node with same name already exists
|
||||||
|
if (getSubnode(directoryNode, p.GetNodeName(p.GetNodeCount() - 1)) != nullptr)
|
||||||
|
return false; // node already exists
|
||||||
|
// add file node
|
||||||
|
node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.GetNodeName(p.GetNodeCount() - 1));
|
||||||
|
fileNode->custom = custom;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getFile(std::string_view path, F* &custom)
|
||||||
|
{
|
||||||
|
FSCPath p(path);
|
||||||
|
if (p.GetNodeCount() == 0)
|
||||||
|
return false;
|
||||||
|
node_t* node = getByNodePath(p, p.GetNodeCount(), false);
|
||||||
|
if (node == nullptr)
|
||||||
|
return false;
|
||||||
|
if (node->type != NODETYPE_FILE)
|
||||||
|
return false;
|
||||||
|
custom = node->custom;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeFile(std::string_view path)
|
||||||
|
{
|
||||||
|
FSCPath p(path);
|
||||||
|
if (p.GetNodeCount() == 0)
|
||||||
|
return false;
|
||||||
|
node_t* directoryNode = getByNodePath(p, p.GetNodeCount() - 1, false);
|
||||||
|
if (directoryNode == nullptr)
|
||||||
|
return false;
|
||||||
|
// find node
|
||||||
|
node_t* fileNode = getSubnode(directoryNode, p.GetNodeName(p.GetNodeCount() - 1));
|
||||||
|
if (fileNode == nullptr)
|
||||||
|
return false;
|
||||||
|
if (fileNode->type != NODETYPE_FILE)
|
||||||
|
return false;
|
||||||
|
if (fileNode->subnodes.empty() == false)
|
||||||
|
{
|
||||||
|
// files shouldn't have subnodes
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
// remove node from parent
|
||||||
|
directoryNode->subnodes.erase(std::remove(directoryNode->subnodes.begin(), directoryNode->subnodes.end(), fileNode), directoryNode->subnodes.end());
|
||||||
|
// delete node
|
||||||
|
delete fileNode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TFunc>
|
||||||
|
bool listDirectory(std::string_view path, TFunc fn)
|
||||||
|
{
|
||||||
|
FSCPath p(path);
|
||||||
|
node_t* node = getByNodePath(p, p.GetNodeCount(), false);
|
||||||
|
if (node == nullptr)
|
||||||
|
return false;
|
||||||
|
if (node->type != NODETYPE_DIRECTORY)
|
||||||
|
return false;
|
||||||
|
for (auto& it : node->subnodes)
|
||||||
|
{
|
||||||
|
if (it->type == NODETYPE_DIRECTORY)
|
||||||
|
{
|
||||||
|
fn(it->name, true, it->custom);
|
||||||
|
}
|
||||||
|
else if (it->type == NODETYPE_FILE)
|
||||||
|
{
|
||||||
|
fn(it->name, false, it->custom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
node_t rootNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void FSTPathUnitTest()
|
static void FSTPathUnitTest()
|
||||||
@ -454,28 +270,28 @@ static void FSTPathUnitTest()
|
|||||||
// test 1
|
// test 1
|
||||||
FSCPath p1("/vol/content");
|
FSCPath p1("/vol/content");
|
||||||
cemu_assert_debug(p1.GetNodeCount() == 2);
|
cemu_assert_debug(p1.GetNodeCount() == 2);
|
||||||
cemu_assert_debug(p1.MatchNode(0, "tst") == false);
|
cemu_assert_debug(p1.MatchNodeName(0, "tst") == false);
|
||||||
cemu_assert_debug(p1.MatchNode(0, "vol"));
|
cemu_assert_debug(p1.MatchNodeName(0, "vol"));
|
||||||
cemu_assert_debug(p1.MatchNode(1, "CONTENT"));
|
cemu_assert_debug(p1.MatchNodeName(1, "CONTENT"));
|
||||||
// test 2
|
// test 2
|
||||||
FSCPath p2("/vol/content/");
|
FSCPath p2("/vol/content/");
|
||||||
cemu_assert_debug(p2.GetNodeCount() == 2);
|
cemu_assert_debug(p2.GetNodeCount() == 2);
|
||||||
cemu_assert_debug(p2.MatchNode(0, "vol"));
|
cemu_assert_debug(p2.MatchNodeName(0, "vol"));
|
||||||
cemu_assert_debug(p2.MatchNode(1, "content"));
|
cemu_assert_debug(p2.MatchNodeName(1, "content"));
|
||||||
// test 3
|
// test 3
|
||||||
FSCPath p3("/vol//content/\\/");
|
FSCPath p3("/vol//content/\\/");
|
||||||
cemu_assert_debug(p3.GetNodeCount() == 2);
|
cemu_assert_debug(p3.GetNodeCount() == 2);
|
||||||
cemu_assert_debug(p3.MatchNode(0, "vol"));
|
cemu_assert_debug(p3.MatchNodeName(0, "vol"));
|
||||||
cemu_assert_debug(p3.MatchNode(1, "content"));
|
cemu_assert_debug(p3.MatchNodeName(1, "content"));
|
||||||
// test 4
|
// test 4
|
||||||
FSCPath p4("vol/content/");
|
FSCPath p4("vol/content/");
|
||||||
cemu_assert_debug(p4.GetNodeCount() == 2);
|
cemu_assert_debug(p4.GetNodeCount() == 2);
|
||||||
// test 5
|
// test 5
|
||||||
FSCPath p5("/vol/content/test.bin");
|
FSCPath p5("/vol/content/test.bin");
|
||||||
cemu_assert_debug(p5.GetNodeCount() == 3);
|
cemu_assert_debug(p5.GetNodeCount() == 3);
|
||||||
cemu_assert_debug(p5.MatchNode(0, "vol"));
|
cemu_assert_debug(p5.MatchNodeName(0, "vol"));
|
||||||
cemu_assert_debug(p5.MatchNode(1, "content"));
|
cemu_assert_debug(p5.MatchNodeName(1, "content"));
|
||||||
cemu_assert_debug(p5.MatchNode(2, "TEST.BIN"));
|
cemu_assert_debug(p5.MatchNodeName(2, "TEST.BIN"));
|
||||||
// test 6 - empty paths
|
// test 6 - empty paths
|
||||||
FSCPath p6("");
|
FSCPath p6("");
|
||||||
cemu_assert_debug(p6.GetNodeCount() == 0);
|
cemu_assert_debug(p6.GetNodeCount() == 0);
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
#include "Cafe/Filesystem/fsc.h"
|
#include "Cafe/Filesystem/fsc.h"
|
||||||
|
#include "Cafe/Filesystem/FST/fstUtil.h"
|
||||||
|
|
||||||
struct FSCMountPathNode
|
struct FSCMountPathNode
|
||||||
{
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
std::vector<FSCMountPathNode*> subnodes;
|
std::vector<FSCMountPathNode*> subnodes;
|
||||||
FSCMountPathNode* parent;
|
FSCMountPathNode* parent;
|
||||||
// device target and path (if list_subnodes is nullptr)
|
// device target and path (if subnodes is empty)
|
||||||
fscDeviceC* device{ nullptr };
|
fscDeviceC* device{ nullptr };
|
||||||
void* ctx{ nullptr };
|
void* ctx{ nullptr };
|
||||||
std::wstring targetPath;
|
std::string deviceTargetPath; // the destination base path for the device, utf8
|
||||||
// priority
|
// priority
|
||||||
sint32 priority{};
|
sint32 priority{};
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ struct FSCMountPathNode
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// compare two file or directory names using FS rules
|
// compare two file or directory names using FSA rules
|
||||||
bool FSA_CompareNodeName(std::string_view a, std::string_view b)
|
bool FSA_CompareNodeName(std::string_view a, std::string_view b)
|
||||||
{
|
{
|
||||||
if (a.size() != b.size())
|
if (a.size() != b.size())
|
||||||
@ -74,25 +75,25 @@ void fsc_reset()
|
|||||||
* /vol/content/data -> Map to HostFS
|
* /vol/content/data -> Map to HostFS
|
||||||
* If overlapping paths with different priority are created, then the higher priority one will be checked first
|
* If overlapping paths with different priority are created, then the higher priority one will be checked first
|
||||||
*/
|
*/
|
||||||
FSCMountPathNode* fsc_createMountPath(CoreinitFSParsedPath* parsedMountPath, sint32 priority)
|
FSCMountPathNode* fsc_createMountPath(const FSCPath& mountPath, sint32 priority)
|
||||||
{
|
{
|
||||||
cemu_assert(priority >= 0 && priority < FSC_PRIORITY_COUNT);
|
cemu_assert(priority >= 0 && priority < FSC_PRIORITY_COUNT);
|
||||||
fscEnter();
|
fscEnter();
|
||||||
FSCMountPathNode* nodeParent = s_fscRootNodePerPrio[priority];
|
FSCMountPathNode* nodeParent = s_fscRootNodePerPrio[priority];
|
||||||
for(sint32 i=0; i<parsedMountPath->numNodes; i++)
|
for (size_t i=0; i< mountPath.GetNodeCount(); i++)
|
||||||
{
|
{
|
||||||
// search for subdirectory
|
// search for subdirectory
|
||||||
FSCMountPathNode* nodeSub = nullptr; // set if we found a subnode with a matching name, else this is used to store the new nodes
|
FSCMountPathNode* nodeSub = nullptr; // set if we found a subnode with a matching name, else this is used to store the new nodes
|
||||||
for(auto& nodeItr : nodeParent->subnodes)
|
for (auto& nodeItr : nodeParent->subnodes)
|
||||||
{
|
{
|
||||||
if( coreinitFS_checkNodeName(parsedMountPath, i, nodeItr->path.c_str()) )
|
if (mountPath.MatchNodeName(i, nodeItr->path))
|
||||||
{
|
{
|
||||||
// subnode found
|
// subnode found
|
||||||
nodeSub = nodeItr;
|
nodeSub = nodeItr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( nodeSub )
|
if (nodeSub)
|
||||||
{
|
{
|
||||||
// traverse subnode
|
// traverse subnode
|
||||||
nodeParent = nodeSub;
|
nodeParent = nodeSub;
|
||||||
@ -100,10 +101,10 @@ FSCMountPathNode* fsc_createMountPath(CoreinitFSParsedPath* parsedMountPath, sin
|
|||||||
}
|
}
|
||||||
// no matching subnode, add new entry
|
// no matching subnode, add new entry
|
||||||
nodeSub = new FSCMountPathNode(nodeParent);
|
nodeSub = new FSCMountPathNode(nodeParent);
|
||||||
nodeSub->path = coreinitFS_getNodeName(parsedMountPath, i);
|
nodeSub->path = mountPath.GetNodeName(i);
|
||||||
nodeSub->priority = priority;
|
nodeSub->priority = priority;
|
||||||
nodeParent->subnodes.emplace_back(nodeSub);
|
nodeParent->subnodes.emplace_back(nodeSub);
|
||||||
if( i == (parsedMountPath->numNodes-1) )
|
if (i == (mountPath.GetNodeCount() - 1))
|
||||||
{
|
{
|
||||||
// last node
|
// last node
|
||||||
fscLeave();
|
fscLeave();
|
||||||
@ -114,47 +115,44 @@ FSCMountPathNode* fsc_createMountPath(CoreinitFSParsedPath* parsedMountPath, sin
|
|||||||
}
|
}
|
||||||
// path is empty or already mounted
|
// path is empty or already mounted
|
||||||
fscLeave();
|
fscLeave();
|
||||||
if (parsedMountPath->numNodes == 0)
|
if (mountPath.GetNodeCount() == 0)
|
||||||
return nodeParent;
|
return nodeParent;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map a virtual FSC directory to a device and device directory
|
// Map a virtual FSC directory to a device. targetPath points to the destination base directory within the device
|
||||||
sint32 fsc_mount(const char* mountPath, const wchar_t* _targetPath, fscDeviceC* fscDevice, void* ctx, sint32 priority)
|
sint32 fsc_mount(std::string_view mountPath, std::string_view targetPath, fscDeviceC* fscDevice, void* ctx, sint32 priority)
|
||||||
{
|
{
|
||||||
cemu_assert(fscDevice); // device must not be nullptr
|
cemu_assert(fscDevice);
|
||||||
|
std::string mountPathTmp(mountPath);
|
||||||
// make sure the target path ends with a slash
|
// make sure the target path ends with a slash
|
||||||
std::wstring targetPath(_targetPath);
|
std::string targetPathWithSlash(targetPath);
|
||||||
if (!targetPath.empty() && (targetPath.back() != '/' && targetPath.back() != '\\'))
|
if (!targetPathWithSlash.empty() && (targetPathWithSlash.back() != '/' && targetPathWithSlash.back() != '\\'))
|
||||||
targetPath.push_back('/');
|
targetPathWithSlash.push_back('/');
|
||||||
|
|
||||||
// parse mount path
|
FSCPath parsedMountPath(mountPathTmp);
|
||||||
CoreinitFSParsedPath parsedMountPath;
|
|
||||||
coreinitFS_parsePath(&parsedMountPath, mountPath);
|
|
||||||
// register path
|
// register path
|
||||||
fscEnter();
|
fscEnter();
|
||||||
FSCMountPathNode* node = fsc_createMountPath(&parsedMountPath, priority);
|
FSCMountPathNode* node = fsc_createMountPath(parsedMountPath, priority);
|
||||||
if( !node )
|
if( !node )
|
||||||
{
|
{
|
||||||
// path empty, invalid or already used
|
// path empty, invalid or already used
|
||||||
cemuLog_log(LogType::Force, "fsc_mount failed (virtual path: %s)", mountPath);
|
cemuLog_log(LogType::Force, "fsc_mount failed (virtual path: {})", mountPath);
|
||||||
fscLeave();
|
fscLeave();
|
||||||
return FSC_STATUS_INVALID_PATH;
|
return FSC_STATUS_INVALID_PATH;
|
||||||
}
|
}
|
||||||
node->device = fscDevice;
|
node->device = fscDevice;
|
||||||
node->ctx = ctx;
|
node->ctx = ctx;
|
||||||
node->targetPath = targetPath;
|
node->deviceTargetPath = targetPathWithSlash;
|
||||||
fscLeave();
|
fscLeave();
|
||||||
return FSC_STATUS_OK;
|
return FSC_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fsc_unmount(const char* mountPath, sint32 priority)
|
bool fsc_unmount(std::string_view mountPath, sint32 priority)
|
||||||
{
|
{
|
||||||
CoreinitFSParsedPath parsedMountPath;
|
std::string _tmp(mountPath);
|
||||||
coreinitFS_parsePath(&parsedMountPath, mountPath);
|
|
||||||
|
|
||||||
fscEnter();
|
fscEnter();
|
||||||
FSCMountPathNode* mountPathNode = fsc_lookupPathVirtualNode(mountPath, priority);
|
FSCMountPathNode* mountPathNode = fsc_lookupPathVirtualNode(_tmp.c_str(), priority);
|
||||||
if (!mountPathNode)
|
if (!mountPathNode)
|
||||||
{
|
{
|
||||||
fscLeave();
|
fscLeave();
|
||||||
@ -185,21 +183,19 @@ void fsc_unmountAll()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// lookup virtual path and find mounted device and relative device directory
|
// lookup virtual path and find mounted device and relative device directory
|
||||||
bool fsc_lookupPath(const char* path, std::wstring& devicePathOut, fscDeviceC** fscDeviceOut, void** ctxOut, sint32 priority = FSC_PRIORITY_BASE)
|
bool fsc_lookupPath(const char* path, std::string& devicePathOut, fscDeviceC** fscDeviceOut, void** ctxOut, sint32 priority = FSC_PRIORITY_BASE)
|
||||||
{
|
{
|
||||||
// parse path
|
FSCPath parsedPath(path);
|
||||||
CoreinitFSParsedPath parsedPath;
|
|
||||||
coreinitFS_parsePath(&parsedPath, path);
|
|
||||||
FSCMountPathNode* nodeParent = s_fscRootNodePerPrio[priority];
|
FSCMountPathNode* nodeParent = s_fscRootNodePerPrio[priority];
|
||||||
sint32 i;
|
size_t i;
|
||||||
fscEnter();
|
fscEnter();
|
||||||
for (i = 0; i < parsedPath.numNodes; i++)
|
for (i = 0; i < parsedPath.GetNodeCount(); i++)
|
||||||
{
|
{
|
||||||
// search for subdirectory
|
// search for subdirectory
|
||||||
FSCMountPathNode* nodeSub = nullptr;
|
FSCMountPathNode* nodeSub = nullptr;
|
||||||
for(auto& nodeItr : nodeParent->subnodes)
|
for(auto& nodeItr : nodeParent->subnodes)
|
||||||
{
|
{
|
||||||
if (coreinitFS_checkNodeName(&parsedPath, i, nodeItr->path.c_str()))
|
if (parsedPath.MatchNodeName(i, nodeItr->path))
|
||||||
{
|
{
|
||||||
nodeSub = nodeItr;
|
nodeSub = nodeItr;
|
||||||
break;
|
break;
|
||||||
@ -213,17 +209,17 @@ bool fsc_lookupPath(const char* path, std::wstring& devicePathOut, fscDeviceC**
|
|||||||
// no matching subnode
|
// no matching subnode
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// find deepest device mount point
|
// if the found node is not a device mount point, then travel back towards the root until we find one
|
||||||
while (nodeParent)
|
while (nodeParent)
|
||||||
{
|
{
|
||||||
if (nodeParent->device)
|
if (nodeParent->device)
|
||||||
{
|
{
|
||||||
devicePathOut = nodeParent->targetPath;
|
devicePathOut = nodeParent->deviceTargetPath;
|
||||||
for (sint32 f = i; f < parsedPath.numNodes; f++)
|
for (size_t f = i; f < parsedPath.GetNodeCount(); f++)
|
||||||
{
|
{
|
||||||
const char* nodeName = coreinitFS_getNodeName(&parsedPath, f);
|
auto nodeName = parsedPath.GetNodeName(f);
|
||||||
devicePathOut.append(boost::nowide::widen(nodeName));
|
devicePathOut.append(nodeName);
|
||||||
if (f < (parsedPath.numNodes - 1))
|
if (f < (parsedPath.GetNodeCount() - 1))
|
||||||
devicePathOut.push_back('/');
|
devicePathOut.push_back('/');
|
||||||
}
|
}
|
||||||
*fscDeviceOut = nodeParent->device;
|
*fscDeviceOut = nodeParent->device;
|
||||||
@ -241,19 +237,16 @@ bool fsc_lookupPath(const char* path, std::wstring& devicePathOut, fscDeviceC**
|
|||||||
// lookup path and find virtual device node
|
// lookup path and find virtual device node
|
||||||
FSCMountPathNode* fsc_lookupPathVirtualNode(const char* path, sint32 priority)
|
FSCMountPathNode* fsc_lookupPathVirtualNode(const char* path, sint32 priority)
|
||||||
{
|
{
|
||||||
// parse path
|
FSCPath parsedPath(path);
|
||||||
CoreinitFSParsedPath parsedPath;
|
|
||||||
coreinitFS_parsePath(&parsedPath, path);
|
|
||||||
FSCMountPathNode* nodeCurrentDir = s_fscRootNodePerPrio[priority];
|
FSCMountPathNode* nodeCurrentDir = s_fscRootNodePerPrio[priority];
|
||||||
sint32 i;
|
|
||||||
fscEnter();
|
fscEnter();
|
||||||
for (i = 0; i < parsedPath.numNodes; i++)
|
for (size_t i = 0; i < parsedPath.GetNodeCount(); i++)
|
||||||
{
|
{
|
||||||
// search for subdirectory
|
// search for subdirectory
|
||||||
FSCMountPathNode* nodeSub = nullptr;
|
FSCMountPathNode* nodeSub = nullptr;
|
||||||
for (auto& nodeItr : nodeCurrentDir->subnodes)
|
for (auto& nodeItr : nodeCurrentDir->subnodes)
|
||||||
{
|
{
|
||||||
if (coreinitFS_checkNodeName(&parsedPath, i, nodeItr->path.c_str()))
|
if (parsedPath.MatchNodeName(i, nodeItr->path))
|
||||||
{
|
{
|
||||||
nodeSub = nodeItr;
|
nodeSub = nodeItr;
|
||||||
break;
|
break;
|
||||||
@ -360,11 +353,9 @@ private:
|
|||||||
FSCVirtualFile* fsc_open(const char* path, FSC_ACCESS_FLAG accessFlags, sint32* fscStatus, sint32 maxPriority)
|
FSCVirtualFile* fsc_open(const char* path, FSC_ACCESS_FLAG accessFlags, sint32* fscStatus, sint32 maxPriority)
|
||||||
{
|
{
|
||||||
cemu_assert_debug(HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE) || HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR)); // must open either file or directory
|
cemu_assert_debug(HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE) || HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR)); // must open either file or directory
|
||||||
|
|
||||||
FSCVirtualFile* dirList[FSC_PRIORITY_COUNT];
|
FSCVirtualFile* dirList[FSC_PRIORITY_COUNT];
|
||||||
uint8 dirListCount = 0;
|
uint8 dirListCount = 0;
|
||||||
|
std::string devicePath;
|
||||||
std::wstring devicePath;
|
|
||||||
fscDeviceC* fscDevice = NULL;
|
fscDeviceC* fscDevice = NULL;
|
||||||
*fscStatus = FSC_STATUS_UNDEFINED;
|
*fscStatus = FSC_STATUS_UNDEFINED;
|
||||||
void* ctx;
|
void* ctx;
|
||||||
@ -455,7 +446,7 @@ bool fsc_createDir(char* path, sint32* fscStatus)
|
|||||||
fscDeviceC* fscDevice = NULL;
|
fscDeviceC* fscDevice = NULL;
|
||||||
*fscStatus = FSC_STATUS_UNDEFINED;
|
*fscStatus = FSC_STATUS_UNDEFINED;
|
||||||
void* ctx;
|
void* ctx;
|
||||||
std::wstring devicePath;
|
std::string devicePath;
|
||||||
fscEnter();
|
fscEnter();
|
||||||
if( fsc_lookupPath(path, devicePath, &fscDevice, &ctx) )
|
if( fsc_lookupPath(path, devicePath, &fscDevice, &ctx) )
|
||||||
{
|
{
|
||||||
@ -472,8 +463,8 @@ bool fsc_createDir(char* path, sint32* fscStatus)
|
|||||||
*/
|
*/
|
||||||
bool fsc_rename(char* srcPath, char* dstPath, sint32* fscStatus)
|
bool fsc_rename(char* srcPath, char* dstPath, sint32* fscStatus)
|
||||||
{
|
{
|
||||||
std::wstring srcDevicePath;
|
std::string srcDevicePath;
|
||||||
std::wstring dstDevicePath;
|
std::string dstDevicePath;
|
||||||
void* srcCtx;
|
void* srcCtx;
|
||||||
void* dstCtx;
|
void* dstCtx;
|
||||||
fscDeviceC* fscSrcDevice = NULL;
|
fscDeviceC* fscSrcDevice = NULL;
|
||||||
@ -492,7 +483,7 @@ bool fsc_rename(char* srcPath, char* dstPath, sint32* fscStatus)
|
|||||||
*/
|
*/
|
||||||
bool fsc_remove(char* path, sint32* fscStatus)
|
bool fsc_remove(char* path, sint32* fscStatus)
|
||||||
{
|
{
|
||||||
std::wstring devicePath;
|
std::string devicePath;
|
||||||
fscDeviceC* fscDevice = NULL;
|
fscDeviceC* fscDevice = NULL;
|
||||||
*fscStatus = FSC_STATUS_UNDEFINED;
|
*fscStatus = FSC_STATUS_UNDEFINED;
|
||||||
void* ctx;
|
void* ctx;
|
||||||
@ -691,7 +682,7 @@ bool fsc_doesFileExist(const char* path, sint32 maxPriority)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper function to check if a folder exists
|
// helper function to check if a directory exists
|
||||||
bool fsc_doesDirectoryExist(const char* path, sint32 maxPriority)
|
bool fsc_doesDirectoryExist(const char* path, sint32 maxPriority)
|
||||||
{
|
{
|
||||||
fscDeviceC* fscDevice = nullptr;
|
fscDeviceC* fscDevice = nullptr;
|
||||||
@ -708,93 +699,7 @@ bool fsc_doesDirectoryExist(const char* path, sint32 maxPriority)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize Cemu's virtual filesystem
|
||||||
void coreinitFS_parsePath(CoreinitFSParsedPath* parsedPath, const char* path)
|
|
||||||
{
|
|
||||||
// if the path starts with a '/', skip it
|
|
||||||
if (*path == '/')
|
|
||||||
path++;
|
|
||||||
// init parsedPath struct
|
|
||||||
memset(parsedPath, 0x00, sizeof(CoreinitFSParsedPath));
|
|
||||||
// init parsed path data
|
|
||||||
size_t pathLength = std::min((size_t)640, strlen(path));
|
|
||||||
memcpy(parsedPath->pathData, path, pathLength);
|
|
||||||
// start parsing
|
|
||||||
sint32 offset = 0;
|
|
||||||
sint32 startOffset = 0;
|
|
||||||
if (offset < pathLength)
|
|
||||||
{
|
|
||||||
parsedPath->nodeOffset[parsedPath->numNodes] = offset;
|
|
||||||
parsedPath->numNodes++;
|
|
||||||
}
|
|
||||||
while (offset < pathLength)
|
|
||||||
{
|
|
||||||
if (parsedPath->pathData[offset] == '/' || parsedPath->pathData[offset] == '\\')
|
|
||||||
{
|
|
||||||
parsedPath->pathData[offset] = '\0';
|
|
||||||
offset++;
|
|
||||||
// double slashes are ignored and instead are handled like a single slash
|
|
||||||
if (parsedPath->pathData[offset] == '/' || parsedPath->pathData[offset] == '\\')
|
|
||||||
{
|
|
||||||
// if we're in the beginning and having a \\ it's a network path
|
|
||||||
if (offset != 1)
|
|
||||||
{
|
|
||||||
parsedPath->pathData[offset] = '\0';
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// start new node
|
|
||||||
if (parsedPath->numNodes < FSC_PARSED_PATH_NODES_MAX)
|
|
||||||
{
|
|
||||||
if (offset < pathLength)
|
|
||||||
{
|
|
||||||
parsedPath->nodeOffset[parsedPath->numNodes] = offset;
|
|
||||||
parsedPath->numNodes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
// handle special nodes like '.' or '..'
|
|
||||||
sint32 nodeIndex = 0;
|
|
||||||
while (nodeIndex < parsedPath->numNodes)
|
|
||||||
{
|
|
||||||
if (coreinitFS_checkNodeName(parsedPath, nodeIndex, ".."))
|
|
||||||
cemu_assert_suspicious(); // how does Cafe OS handle .. ?
|
|
||||||
else if (coreinitFS_checkNodeName(parsedPath, nodeIndex, "."))
|
|
||||||
{
|
|
||||||
// remove this node and shift back all following nodes by 1
|
|
||||||
parsedPath->numNodes--;
|
|
||||||
for (sint32 i = nodeIndex; i < parsedPath->numNodes; i++)
|
|
||||||
{
|
|
||||||
parsedPath->nodeOffset[i] = parsedPath->nodeOffset[i + 1];
|
|
||||||
}
|
|
||||||
// continue without increasing nodeIndex
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
nodeIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool coreinitFS_checkNodeName(CoreinitFSParsedPath* parsedPath, sint32 index, const char* name)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= parsedPath->numNodes)
|
|
||||||
return false;
|
|
||||||
char* nodeName = parsedPath->pathData + parsedPath->nodeOffset[index];
|
|
||||||
if (boost::iequals(nodeName, name))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* coreinitFS_getNodeName(CoreinitFSParsedPath* parsedPath, sint32 index)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= parsedPath->numNodes)
|
|
||||||
return nullptr;
|
|
||||||
return parsedPath->pathData + parsedPath->nodeOffset[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize Cemu's virtual filesystem
|
|
||||||
void fsc_init()
|
void fsc_init()
|
||||||
{
|
{
|
||||||
fsc_reset();
|
fsc_reset();
|
||||||
|
@ -57,25 +57,25 @@ struct FSCDirEntry
|
|||||||
class fscDeviceC
|
class fscDeviceC
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual FSCVirtualFile* fscDeviceOpenByPath(std::wstring_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus)
|
virtual FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus)
|
||||||
{
|
{
|
||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool fscDeviceCreateDir(std::wstring_view path, void* ctx, sint32* fscStatus)
|
virtual bool fscDeviceCreateDir(std::string_view path, void* ctx, sint32* fscStatus)
|
||||||
{
|
{
|
||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool fscDeviceRemoveFileOrDir(std::wstring_view path, void* ctx, sint32* fscStatus)
|
virtual bool fscDeviceRemoveFileOrDir(std::string_view path, void* ctx, sint32* fscStatus)
|
||||||
{
|
{
|
||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool fscDeviceRename(std::wstring_view srcPath, std::wstring_view dstPath, void* ctx, sint32* fscStatus)
|
virtual bool fscDeviceRename(std::string_view srcPath, std::string_view dstPath, void* ctx, sint32* fscStatus)
|
||||||
{
|
{
|
||||||
cemu_assert_unimplemented();
|
cemu_assert_unimplemented();
|
||||||
return false;
|
return false;
|
||||||
@ -161,8 +161,8 @@ struct FSCVirtualFile
|
|||||||
#define FSC_PRIORITY_COUNT (4)
|
#define FSC_PRIORITY_COUNT (4)
|
||||||
|
|
||||||
void fsc_init();
|
void fsc_init();
|
||||||
sint32 fsc_mount(const char* mountPath, const wchar_t* targetPath, fscDeviceC* fscDevice, void* ctx, sint32 priority=0);
|
sint32 fsc_mount(std::string_view mountPath, std::string_view targetPath, fscDeviceC* fscDevice, void* ctx, sint32 priority=0);
|
||||||
bool fsc_unmount(const char* mountPath, sint32 priority);
|
bool fsc_unmount(std::string_view mountPath, sint32 priority);
|
||||||
void fsc_unmountAll();
|
void fsc_unmountAll();
|
||||||
|
|
||||||
FSCVirtualFile* fsc_open(const char* path, FSC_ACCESS_FLAG accessFlags, sint32* fscStatus, sint32 maxPriority=FSC_PRIORITY_MAX);
|
FSCVirtualFile* fsc_open(const char* path, FSC_ACCESS_FLAG accessFlags, sint32* fscStatus, sint32 maxPriority=FSC_PRIORITY_MAX);
|
||||||
@ -188,32 +188,15 @@ bool fsc_doesFileExist(const char* path, sint32 maxPriority = FSC_PRIORITY_MAX);
|
|||||||
bool fsc_doesDirectoryExist(const char* path, sint32 maxPriority = FSC_PRIORITY_MAX);
|
bool fsc_doesDirectoryExist(const char* path, sint32 maxPriority = FSC_PRIORITY_MAX);
|
||||||
|
|
||||||
// wud device
|
// wud device
|
||||||
bool FSCDeviceWUD_Mount(const char* mountPath, std::string_view destinationBaseDir, class FSTVolume* mountedVolume, sint32 priority);
|
bool FSCDeviceWUD_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class FSTVolume* mountedVolume, sint32 priority);
|
||||||
|
|
||||||
// wua device
|
// wua device
|
||||||
bool FSCDeviceWUA_Mount(const char* mountPath, std::string_view destinationBaseDir, class ZArchiveReader* archive, sint32 priority);
|
bool FSCDeviceWUA_Mount(std::string_view mountPath, std::string_view destinationBaseDir, class ZArchiveReader* archive, sint32 priority);
|
||||||
|
|
||||||
// hostFS device
|
// hostFS device
|
||||||
void fscDeviceHostFS_mapBaseDirectories_deprecated();
|
void fscDeviceHostFS_mapBaseDirectories_deprecated();
|
||||||
bool FSCDeviceHostFS_Mount(const char* mountPath, const wchar_t* hostFSPath, sint32 priority);
|
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
|
||||||
|
|
||||||
// redirect device
|
// redirect device
|
||||||
void fscDeviceRedirect_map();
|
void fscDeviceRedirect_map();
|
||||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority);
|
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority);
|
||||||
|
|
||||||
|
|
||||||
// Old path parser helper functions
|
|
||||||
// Replace with FSCPath
|
|
||||||
|
|
||||||
#define FSC_PARSED_PATH_NODES_MAX (32)
|
|
||||||
|
|
||||||
struct CoreinitFSParsedPath
|
|
||||||
{
|
|
||||||
char pathData[640 + 1];
|
|
||||||
uint16 nodeOffset[FSC_PARSED_PATH_NODES_MAX];
|
|
||||||
sint32 numNodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
void coreinitFS_parsePath(CoreinitFSParsedPath* parsedPath, const char* path);
|
|
||||||
bool coreinitFS_checkNodeName(CoreinitFSParsedPath* parsedPath, sint32 index, const char* name);
|
|
||||||
char* coreinitFS_getNodeName(CoreinitFSParsedPath* parsedPath, sint32 index);
|
|
||||||
|
@ -127,7 +127,7 @@ bool FSCVirtualFile_Host::fscDirNext(FSCDirEntry* dirEntry)
|
|||||||
m_dirIterator.reset(new fs::directory_iterator(*m_path));
|
m_dirIterator.reset(new fs::directory_iterator(*m_path));
|
||||||
if (!m_dirIterator)
|
if (!m_dirIterator)
|
||||||
{
|
{
|
||||||
cemuLog_force("Failed to iterate directory: {}", _utf8Wrapper(m_path->generic_u8string()));
|
cemuLog_force("Failed to iterate directory: {}", _pathToUtf8(*m_path));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,14 +175,14 @@ FSCVirtualFile* FSCVirtualFile_Host::OpenFile(const fs::path& path, FSC_ACCESS_F
|
|||||||
cemu_assert_debug(writeAccessRequested);
|
cemu_assert_debug(writeAccessRequested);
|
||||||
fs = FileStream::createFile2(path);
|
fs = FileStream::createFile2(path);
|
||||||
if (!fs)
|
if (!fs)
|
||||||
cemuLog_force("FSC: File create failed for {}", _utf8Wrapper(path));
|
cemuLog_force("FSC: File create failed for {}", _pathToUtf8(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALWAYS_CREATE))
|
else if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALWAYS_CREATE))
|
||||||
{
|
{
|
||||||
fs = FileStream::createFile2(path);
|
fs = FileStream::createFile2(path);
|
||||||
if (!fs)
|
if (!fs)
|
||||||
cemuLog_force("FSC: File create failed for {}", _utf8Wrapper(path));
|
cemuLog_force("FSC: File create failed for {}", _pathToUtf8(path));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -221,36 +221,36 @@ FSCVirtualFile* FSCVirtualFile_Host::OpenFile(const fs::path& path, FSC_ACCESS_F
|
|||||||
class fscDeviceHostFSC : public fscDeviceC
|
class fscDeviceHostFSC : public fscDeviceC
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FSCVirtualFile* fscDeviceOpenByPath(std::wstring_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
||||||
{
|
{
|
||||||
*fscStatus = FSC_STATUS_OK;
|
*fscStatus = FSC_STATUS_OK;
|
||||||
FSCVirtualFile* vf = FSCVirtualFile_Host::OpenFile(path, accessFlags, *fscStatus);
|
FSCVirtualFile* vf = FSCVirtualFile_Host::OpenFile(_utf8ToPath(path), accessFlags, *fscStatus);
|
||||||
cemu_assert_debug((bool)vf == (*fscStatus == FSC_STATUS_OK));
|
cemu_assert_debug((bool)vf == (*fscStatus == FSC_STATUS_OK));
|
||||||
return vf;
|
return vf;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fscDeviceCreateDir(std::wstring_view path, void* ctx, sint32* fscStatus) override
|
bool fscDeviceCreateDir(std::string_view path, void* ctx, sint32* fscStatus) override
|
||||||
{
|
{
|
||||||
fs::path dirPath(path);
|
fs::path dirPath = _utf8ToPath(path);
|
||||||
if (fs::exists(path))
|
if (fs::exists(dirPath))
|
||||||
{
|
{
|
||||||
if (!fs::is_directory(dirPath))
|
if (!fs::is_directory(dirPath))
|
||||||
cemuLog_force("CreateDir: {} already exists but is not a directory", _utf8Wrapper(dirPath));
|
cemuLog_force("CreateDir: {} already exists but is not a directory", path);
|
||||||
*fscStatus = FSC_STATUS_ALREADY_EXISTS;
|
*fscStatus = FSC_STATUS_ALREADY_EXISTS;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
bool r = fs::create_directories(dirPath, ec);
|
bool r = fs::create_directories(dirPath, ec);
|
||||||
if(!r)
|
if (!r)
|
||||||
cemuLog_force("CreateDir: Failed to create {}", _utf8Wrapper(dirPath));
|
cemuLog_force("CreateDir: Failed to create {}", path);
|
||||||
*fscStatus = FSC_STATUS_OK;
|
*fscStatus = FSC_STATUS_OK;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fscDeviceRemoveFileOrDir(std::wstring_view path, void* ctx, sint32* fscStatus) override
|
bool fscDeviceRemoveFileOrDir(std::string_view path, void* ctx, sint32* fscStatus) override
|
||||||
{
|
{
|
||||||
*fscStatus = FSC_STATUS_OK;
|
*fscStatus = FSC_STATUS_OK;
|
||||||
fs::path _path(path);
|
fs::path _path = _utf8ToPath(path);
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
if (!fs::exists(_path, ec))
|
if (!fs::exists(_path, ec))
|
||||||
{
|
{
|
||||||
@ -266,11 +266,11 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fscDeviceRename(std::wstring_view srcPath, std::wstring_view dstPath, void* ctx, sint32* fscStatus) override
|
bool fscDeviceRename(std::string_view srcPath, std::string_view dstPath, void* ctx, sint32* fscStatus) override
|
||||||
{
|
{
|
||||||
*fscStatus = FSC_STATUS_OK;
|
*fscStatus = FSC_STATUS_OK;
|
||||||
fs::path _srcPath(srcPath);
|
fs::path _srcPath = _utf8ToPath(srcPath);
|
||||||
fs::path _dstPath(dstPath);
|
fs::path _dstPath = _utf8ToPath(dstPath);
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
if (!fs::exists(_srcPath, ec))
|
if (!fs::exists(_srcPath, ec))
|
||||||
{
|
{
|
||||||
@ -293,14 +293,11 @@ public:
|
|||||||
void fscDeviceHostFS_mapBaseDirectories_deprecated()
|
void fscDeviceHostFS_mapBaseDirectories_deprecated()
|
||||||
{
|
{
|
||||||
const auto mlc = ActiveSettings::GetMlcPath();
|
const auto mlc = ActiveSettings::GetMlcPath();
|
||||||
fsc_mount("/cemuBossStorage/", (mlc / "usr/boss/").generic_wstring().c_str(), &fscDeviceHostFSC::instance(), NULL, FSC_PRIORITY_BASE);
|
fsc_mount("/cemuBossStorage/", _pathToUtf8(mlc / "usr/boss/"), &fscDeviceHostFSC::instance(), NULL, FSC_PRIORITY_BASE);
|
||||||
fsc_mount("/vol/storage_mlc01/", (mlc / "").generic_wstring().c_str(), &fscDeviceHostFSC::instance(), NULL, FSC_PRIORITY_BASE);
|
fsc_mount("/vol/storage_mlc01/", _pathToUtf8(mlc / ""), &fscDeviceHostFSC::instance(), NULL, FSC_PRIORITY_BASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FSCDeviceHostFS_Mount(const char* mountPath, const wchar_t* hostFSPath, sint32 priority)
|
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
|
||||||
{
|
{
|
||||||
std::wstring hostTargetPath(hostFSPath);
|
return fsc_mount(mountPath, hostTargetPath, &fscDeviceHostFSC::instance(), nullptr, priority) == FSC_STATUS_OK;
|
||||||
if (!hostTargetPath.empty() && (hostTargetPath.back() != '/' && hostTargetPath.back() != '\\'))
|
|
||||||
hostTargetPath.push_back('/');
|
|
||||||
return fsc_mount(mountPath, hostTargetPath.c_str(), &fscDeviceHostFSC::instance(), nullptr, priority) == FSC_STATUS_OK;
|
|
||||||
}
|
}
|
@ -9,31 +9,30 @@ struct RedirectEntry
|
|||||||
sint32 priority;
|
sint32 priority;
|
||||||
};
|
};
|
||||||
|
|
||||||
FileTree<RedirectEntry, false> redirectTree;
|
FSAFileTree<RedirectEntry> redirectTree;
|
||||||
|
|
||||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority)
|
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority)
|
||||||
{
|
{
|
||||||
std::wstring virtualSourcePathW = boost::nowide::widen(std::string(virtualSourcePath));
|
|
||||||
// check if source already has a redirection
|
// check if source already has a redirection
|
||||||
RedirectEntry* existingEntry;
|
RedirectEntry* existingEntry;
|
||||||
if (redirectTree.getFile(virtualSourcePathW, existingEntry))
|
if (redirectTree.getFile(virtualSourcePath, existingEntry))
|
||||||
{
|
{
|
||||||
if (existingEntry->priority >= priority)
|
if (existingEntry->priority >= priority)
|
||||||
return; // dont replace entries with equal or higher priority
|
return; // dont replace entries with equal or higher priority
|
||||||
// unregister existing entry
|
// unregister existing entry
|
||||||
redirectTree.removeFile(virtualSourcePathW.c_str());
|
redirectTree.removeFile(virtualSourcePath);
|
||||||
delete existingEntry;
|
delete existingEntry;
|
||||||
}
|
}
|
||||||
RedirectEntry* entry = new RedirectEntry(targetFilePath, priority);
|
RedirectEntry* entry = new RedirectEntry(targetFilePath, priority);
|
||||||
redirectTree.addFile(virtualSourcePathW.c_str(), entry);
|
redirectTree.addFile(virtualSourcePath, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
class fscDeviceTypeRedirect : public fscDeviceC
|
class fscDeviceTypeRedirect : public fscDeviceC
|
||||||
{
|
{
|
||||||
FSCVirtualFile* fscDeviceOpenByPath(std::wstring_view pathW, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
||||||
{
|
{
|
||||||
RedirectEntry* redirectionEntry;
|
RedirectEntry* redirectionEntry;
|
||||||
if (redirectTree.getFile(pathW, redirectionEntry))
|
if (redirectTree.getFile(path, redirectionEntry))
|
||||||
return FSCVirtualFile_Host::OpenFile(redirectionEntry->dstPath, accessFlags, *fscStatus);
|
return FSCVirtualFile_Host::OpenFile(redirectionEntry->dstPath, accessFlags, *fscStatus);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -52,6 +51,6 @@ void fscDeviceRedirect_map()
|
|||||||
{
|
{
|
||||||
if (_redirectMapped)
|
if (_redirectMapped)
|
||||||
return;
|
return;
|
||||||
fsc_mount("/", L"/", &fscDeviceTypeRedirect::instance(), nullptr, FSC_PRIORITY_REDIRECT);
|
fsc_mount("/", "/", &fscDeviceTypeRedirect::instance(), nullptr, FSC_PRIORITY_REDIRECT);
|
||||||
_redirectMapped = true;
|
_redirectMapped = true;
|
||||||
}
|
}
|
||||||
|
@ -119,14 +119,12 @@ private:
|
|||||||
|
|
||||||
class fscDeviceWUAC : public fscDeviceC
|
class fscDeviceWUAC : public fscDeviceC
|
||||||
{
|
{
|
||||||
FSCVirtualFile* fscDeviceOpenByPath(std::wstring_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
||||||
{
|
{
|
||||||
ZArchiveReader* archive = (ZArchiveReader*)ctx;
|
ZArchiveReader* archive = (ZArchiveReader*)ctx;
|
||||||
cemu_assert_debug(!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION)); // writing to WUA is not supported
|
cemu_assert_debug(!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION)); // writing to WUA is not supported
|
||||||
|
|
||||||
std::string pathU8 = boost::nowide::narrow(path.data(), path.size());
|
ZArchiveNodeHandle fileHandle = archive->LookUp(path, true, true);
|
||||||
|
|
||||||
ZArchiveNodeHandle fileHandle = archive->LookUp(pathU8, true, true);
|
|
||||||
if (fileHandle == ZARCHIVE_INVALID_NODE)
|
if (fileHandle == ZARCHIVE_INVALID_NODE)
|
||||||
{
|
{
|
||||||
*fscStatus = FSC_STATUS_FILE_NOT_FOUND;
|
*fscStatus = FSC_STATUS_FILE_NOT_FOUND;
|
||||||
@ -169,10 +167,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FSCDeviceWUA_Mount(const char* mountPath, std::string_view destinationBaseDir, ZArchiveReader* archive, sint32 priority)
|
bool FSCDeviceWUA_Mount(std::string_view mountPath, std::string_view destinationBaseDir, ZArchiveReader* archive, sint32 priority)
|
||||||
{
|
{
|
||||||
std::wstring hostTargetPath(boost::nowide::widen(destinationBaseDir.data(), destinationBaseDir.size()));
|
return fsc_mount(mountPath, destinationBaseDir, &fscDeviceWUAC::instance(), archive, priority) == FSC_STATUS_OK;
|
||||||
if (!hostTargetPath.empty() && (hostTargetPath.back() != '/' && hostTargetPath.back() != '\\'))
|
|
||||||
hostTargetPath.push_back('/');
|
|
||||||
return fsc_mount(mountPath, hostTargetPath.c_str(), &fscDeviceWUAC::instance(), archive, priority) == FSC_STATUS_OK;
|
|
||||||
}
|
}
|
@ -120,16 +120,15 @@ private:
|
|||||||
|
|
||||||
class fscDeviceWUDC : public fscDeviceC
|
class fscDeviceWUDC : public fscDeviceC
|
||||||
{
|
{
|
||||||
FSCVirtualFile* fscDeviceOpenByPath(std::wstring_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
|
||||||
{
|
{
|
||||||
FSTVolume* mountedVolume = (FSTVolume*)ctx;
|
FSTVolume* mountedVolume = (FSTVolume*)ctx;
|
||||||
cemu_assert_debug(!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION)); // writing to FST is never allowed
|
cemu_assert_debug(!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION)); // writing to FST is never allowed
|
||||||
|
|
||||||
std::string pathU8 = boost::nowide::narrow(path.data(), path.size());
|
|
||||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
|
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
|
||||||
{
|
{
|
||||||
FSTFileHandle fstFileHandle;
|
FSTFileHandle fstFileHandle;
|
||||||
if (mountedVolume->OpenFile(pathU8, fstFileHandle, true))
|
if (mountedVolume->OpenFile(path, fstFileHandle, true))
|
||||||
{
|
{
|
||||||
*fscStatus = FSC_STATUS_OK;
|
*fscStatus = FSC_STATUS_OK;
|
||||||
return new FSCDeviceWudFileCtx(mountedVolume, fstFileHandle);
|
return new FSCDeviceWudFileCtx(mountedVolume, fstFileHandle);
|
||||||
@ -138,7 +137,7 @@ class fscDeviceWUDC : public fscDeviceC
|
|||||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
|
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
|
||||||
{
|
{
|
||||||
FSTDirectoryIterator dirIterator;
|
FSTDirectoryIterator dirIterator;
|
||||||
if (mountedVolume->OpenDirectoryIterator(pathU8, dirIterator))
|
if (mountedVolume->OpenDirectoryIterator(path, dirIterator))
|
||||||
{
|
{
|
||||||
*fscStatus = FSC_STATUS_OK;
|
*fscStatus = FSC_STATUS_OK;
|
||||||
return new FSCDeviceWudFileCtx(mountedVolume, dirIterator);
|
return new FSCDeviceWudFileCtx(mountedVolume, dirIterator);
|
||||||
@ -157,10 +156,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool FSCDeviceWUD_Mount(const char* mountPath, std::string_view destinationBaseDir, FSTVolume* mountedVolume, sint32 priority)
|
bool FSCDeviceWUD_Mount(std::string_view mountPath, std::string_view destinationBaseDir, FSTVolume* mountedVolume, sint32 priority)
|
||||||
{
|
{
|
||||||
std::wstring hostTargetPath(boost::nowide::widen(destinationBaseDir.data(), destinationBaseDir.size()));
|
return fsc_mount(mountPath, destinationBaseDir, &fscDeviceWUDC::instance(), mountedVolume, priority) == FSC_STATUS_OK;
|
||||||
if (!hostTargetPath.empty() && (hostTargetPath.back() != '/' && hostTargetPath.back() != '\\'))
|
|
||||||
hostTargetPath.push_back('/');
|
|
||||||
return fsc_mount(mountPath, hostTargetPath.c_str(), &fscDeviceWUDC::instance(), mountedVolume, priority) == FSC_STATUS_OK;
|
|
||||||
}
|
}
|
@ -31,12 +31,12 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
|
|||||||
|
|
||||||
if (!iniParser.NextSection())
|
if (!iniParser.NextSection())
|
||||||
{
|
{
|
||||||
cemuLog_force("{}: Does not contain any sections", _utf8Wrapper(rulesPath));
|
cemuLog_force("{}: Does not contain any sections", _pathToUtf8(rulesPath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!boost::iequals(iniParser.GetCurrentSectionName(), "Definition"))
|
if (!boost::iequals(iniParser.GetCurrentSectionName(), "Definition"))
|
||||||
{
|
{
|
||||||
cemuLog_force("{}: [Definition] must be the first section", _utf8Wrapper(rulesPath));
|
cemuLog_force("{}: [Definition] must be the first section", _pathToUtf8(rulesPath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
|
|||||||
auto [ptr, ec] = std::from_chars(option_version->data(), option_version->data() + option_version->size(), versionNum);
|
auto [ptr, ec] = std::from_chars(option_version->data(), option_version->data() + option_version->size(), versionNum);
|
||||||
if (ec != std::errc{})
|
if (ec != std::errc{})
|
||||||
{
|
{
|
||||||
cemuLog_force("{}: Unable to parse version", _utf8Wrapper(rulesPath));
|
cemuLog_force("{}: Unable to parse version", _pathToUtf8(rulesPath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cemuLog_force("{}: Outdated graphic pack", _utf8Wrapper(rulesPath));
|
cemuLog_force("{}: Outdated graphic pack", _pathToUtf8(rulesPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicPack2::LoadAll()
|
void GraphicPack2::LoadAll()
|
||||||
|
@ -109,7 +109,7 @@ namespace coreinit
|
|||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
const auto path = ActiveSettings::GetPath("sdcard/");
|
const auto path = ActiveSettings::GetPath("sdcard/");
|
||||||
fs::create_directories(path, ec);
|
fs::create_directories(path, ec);
|
||||||
FSCDeviceHostFS_Mount("/vol/external01", path.generic_wstring().c_str() , FSC_PRIORITY_BASE);
|
FSCDeviceHostFS_Mount("/vol/external01", _pathToUtf8(path), FSC_PRIORITY_BASE);
|
||||||
|
|
||||||
_sdCard01Mounted = true;
|
_sdCard01Mounted = true;
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ namespace coreinit
|
|||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
const auto path = ActiveSettings::GetPath("sdcard/");
|
const auto path = ActiveSettings::GetPath("sdcard/");
|
||||||
fs::create_directories(path, ec);
|
fs::create_directories(path, ec);
|
||||||
if (!FSCDeviceHostFS_Mount(mountPathOut, path.generic_wstring().c_str(), FSC_PRIORITY_BASE))
|
if (!FSCDeviceHostFS_Mount(mountPathOut, _pathToUtf8(path), FSC_PRIORITY_BASE))
|
||||||
return FS_RESULT::ERR_PLACEHOLDER;
|
return FS_RESULT::ERR_PLACEHOLDER;
|
||||||
_sdCard01Mounted = true;
|
_sdCard01Mounted = true;
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ namespace coreinit
|
|||||||
if (_mlc01Mounted)
|
if (_mlc01Mounted)
|
||||||
return FS_RESULT::ERR_PLACEHOLDER;
|
return FS_RESULT::ERR_PLACEHOLDER;
|
||||||
|
|
||||||
if (!FSCDeviceHostFS_Mount(mountPathOut, ActiveSettings::GetMlcPath().generic_wstring().c_str(), FSC_PRIORITY_BASE))
|
if (!FSCDeviceHostFS_Mount(mountPathOut, _pathToUtf8(ActiveSettings::GetMlcPath()), FSC_PRIORITY_BASE))
|
||||||
return FS_RESULT::ERR_PLACEHOLDER;
|
return FS_RESULT::ERR_PLACEHOLDER;
|
||||||
_mlc01Mounted = true;
|
_mlc01Mounted = true;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ namespace acp
|
|||||||
|
|
||||||
// mount save path
|
// mount save path
|
||||||
const auto mlc = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/", high, low);
|
const auto mlc = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/", high, low);
|
||||||
FSCDeviceHostFS_Mount("/vol/save/", mlc.generic_wstring().c_str(), FSC_PRIORITY_BASE);
|
FSCDeviceHostFS_Mount("/vol/save/", _pathToUtf8(mlc), FSC_PRIORITY_BASE);
|
||||||
nnResult mountResult = BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_ACP, 0);
|
nnResult mountResult = BUILD_NN_RESULT(NN_RESULT_LEVEL_SUCCESS, NN_RESULT_MODULE_NN_ACP, 0);
|
||||||
return _ACPConvertResultToACPStatus(&mountResult, "ACPMountSaveDir", 0x60);
|
return _ACPConvertResultToACPStatus(&mountResult, "ACPMountSaveDir", 0x60);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ void CafeSaveList::RefreshThreadWorker()
|
|||||||
{
|
{
|
||||||
if(!it_titleHigh.is_directory(ec))
|
if(!it_titleHigh.is_directory(ec))
|
||||||
continue;
|
continue;
|
||||||
std::string dirName = _utf8Wrapper(it_titleHigh.path().filename());
|
std::string dirName = _pathToUtf8(it_titleHigh.path().filename());
|
||||||
if(dirName.empty())
|
if(dirName.empty())
|
||||||
continue;
|
continue;
|
||||||
uint32 titleIdHigh;
|
uint32 titleIdHigh;
|
||||||
@ -78,7 +78,7 @@ void CafeSaveList::RefreshThreadWorker()
|
|||||||
{
|
{
|
||||||
if (!it_titleLow.is_directory(ec))
|
if (!it_titleLow.is_directory(ec))
|
||||||
continue;
|
continue;
|
||||||
dirName = _utf8Wrapper(it_titleLow.path().filename());
|
dirName = _pathToUtf8(it_titleLow.path().filename());
|
||||||
if (dirName.empty())
|
if (dirName.empty())
|
||||||
continue;
|
continue;
|
||||||
uint32 titleIdLow;
|
uint32 titleIdLow;
|
||||||
|
@ -177,12 +177,12 @@ bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataF
|
|||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
if (path.has_extension() && fs::is_regular_file(path, ec))
|
if (path.has_extension() && fs::is_regular_file(path, ec))
|
||||||
{
|
{
|
||||||
std::string filenameStr = _utf8Wrapper(path.filename());
|
std::string filenameStr = _pathToUtf8(path.filename());
|
||||||
if (boost::iends_with(filenameStr, ".rpx"))
|
if (boost::iends_with(filenameStr, ".rpx"))
|
||||||
{
|
{
|
||||||
// is in code folder?
|
// is in code folder?
|
||||||
fs::path parentPath = path.parent_path();
|
fs::path parentPath = path.parent_path();
|
||||||
if (boost::iequals(_utf8Wrapper(parentPath.filename()), "code"))
|
if (boost::iequals(_pathToUtf8(parentPath.filename()), "code"))
|
||||||
{
|
{
|
||||||
parentPath = parentPath.parent_path();
|
parentPath = parentPath.parent_path();
|
||||||
// next to content and meta?
|
// next to content and meta?
|
||||||
@ -370,7 +370,7 @@ bool TitleInfo::Mount(std::string_view virtualPath, std::string_view subfolder,
|
|||||||
{
|
{
|
||||||
fs::path hostFSPath = m_fullPath;
|
fs::path hostFSPath = m_fullPath;
|
||||||
hostFSPath.append(subfolder);
|
hostFSPath.append(subfolder);
|
||||||
bool r = FSCDeviceHostFS_Mount(std::string(virtualPath).c_str(), boost::nowide::widen(_utf8Wrapper(hostFSPath)).c_str(), mountPriority);
|
bool r = FSCDeviceHostFS_Mount(std::string(virtualPath).c_str(), _pathToUtf8(hostFSPath), mountPriority);
|
||||||
cemu_assert_debug(r);
|
cemu_assert_debug(r);
|
||||||
if (!r)
|
if (!r)
|
||||||
{
|
{
|
||||||
@ -387,7 +387,7 @@ bool TitleInfo::Mount(std::string_view virtualPath, std::string_view subfolder,
|
|||||||
}
|
}
|
||||||
if (!m_wudVolume)
|
if (!m_wudVolume)
|
||||||
return false;
|
return false;
|
||||||
bool r = FSCDeviceWUD_Mount(std::string(virtualPath).c_str(), subfolder, m_wudVolume, mountPriority);
|
bool r = FSCDeviceWUD_Mount(virtualPath, subfolder, m_wudVolume, mountPriority);
|
||||||
cemu_assert_debug(r);
|
cemu_assert_debug(r);
|
||||||
if (!r)
|
if (!r)
|
||||||
{
|
{
|
||||||
@ -404,7 +404,7 @@ bool TitleInfo::Mount(std::string_view virtualPath, std::string_view subfolder,
|
|||||||
if (!m_zarchive)
|
if (!m_zarchive)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool r = FSCDeviceWUA_Mount(std::string(virtualPath).c_str(), std::string(m_subPath).append("/").append(subfolder), m_zarchive, mountPriority);
|
bool r = FSCDeviceWUA_Mount(virtualPath, std::string(m_subPath).append("/").append(subfolder), m_zarchive, mountPriority);
|
||||||
if (!r)
|
if (!r)
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Failed to mount {} to {}", virtualPath, subfolder);
|
cemuLog_log(LogType::Force, "Failed to mount {} to {}", virtualPath, subfolder);
|
||||||
@ -495,7 +495,7 @@ bool TitleInfo::ParseXmlInfo()
|
|||||||
if (!m_parsedMetaXml || !m_parsedAppXml || !m_parsedCosXml)
|
if (!m_parsedMetaXml || !m_parsedAppXml || !m_parsedCosXml)
|
||||||
{
|
{
|
||||||
if (hasAnyXml)
|
if (hasAnyXml)
|
||||||
cemuLog_log(LogType::Force, "Title has missing meta .xml files. Title path: {}", _utf8Wrapper(m_fullPath));
|
cemuLog_log(LogType::Force, "Title has missing meta .xml files. Title path: {}", _pathToUtf8(m_fullPath));
|
||||||
delete m_parsedMetaXml;
|
delete m_parsedMetaXml;
|
||||||
delete m_parsedAppXml;
|
delete m_parsedAppXml;
|
||||||
delete m_parsedCosXml;
|
delete m_parsedCosXml;
|
||||||
@ -621,7 +621,7 @@ std::string TitleInfo::GetPrintPath() const
|
|||||||
if (!m_isValid)
|
if (!m_isValid)
|
||||||
return "invalid";
|
return "invalid";
|
||||||
std::string tmp;
|
std::string tmp;
|
||||||
tmp.append(_utf8Wrapper(m_fullPath));
|
tmp.append(_pathToUtf8(m_fullPath));
|
||||||
switch (m_titleFormat)
|
switch (m_titleFormat)
|
||||||
{
|
{
|
||||||
case TitleDataFormat::HOST_FS:
|
case TitleDataFormat::HOST_FS:
|
||||||
|
@ -79,7 +79,7 @@ void CafeTitleList::LoadCacheFile()
|
|||||||
cacheEntry.titleDataFormat = format;
|
cacheEntry.titleDataFormat = format;
|
||||||
cacheEntry.region = region;
|
cacheEntry.region = region;
|
||||||
cacheEntry.titleName = name;
|
cacheEntry.titleName = name;
|
||||||
cacheEntry.path = _asUtf8(path);
|
cacheEntry.path = _utf8ToPath(path);
|
||||||
cacheEntry.subPath = sub_path;
|
cacheEntry.subPath = sub_path;
|
||||||
cacheEntry.group_id = group_id;
|
cacheEntry.group_id = group_id;
|
||||||
cacheEntry.app_type = app_type;
|
cacheEntry.app_type = app_type;
|
||||||
@ -120,16 +120,16 @@ void CafeTitleList::StoreCacheFile()
|
|||||||
titleInfoNode.append_child("region").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (uint32)info.region).c_str());
|
titleInfoNode.append_child("region").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (uint32)info.region).c_str());
|
||||||
titleInfoNode.append_child("name").append_child(pugi::node_pcdata).set_value(info.titleName.c_str());
|
titleInfoNode.append_child("name").append_child(pugi::node_pcdata).set_value(info.titleName.c_str());
|
||||||
titleInfoNode.append_child("format").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (uint32)info.titleDataFormat).c_str());
|
titleInfoNode.append_child("format").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (uint32)info.titleDataFormat).c_str());
|
||||||
titleInfoNode.append_child("path").append_child(pugi::node_pcdata).set_value(_utf8Wrapper(info.path).c_str());
|
titleInfoNode.append_child("path").append_child(pugi::node_pcdata).set_value(_pathToUtf8(info.path).c_str());
|
||||||
if(!info.subPath.empty())
|
if(!info.subPath.empty())
|
||||||
titleInfoNode.append_child("sub_path").append_child(pugi::node_pcdata).set_value(_utf8Wrapper(info.subPath).c_str());
|
titleInfoNode.append_child("sub_path").append_child(pugi::node_pcdata).set_value(_pathToUtf8(info.subPath).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path tmpPath = fs::path(sTLCacheFilePath.parent_path()).append(fmt::format("{}__tmp", _utf8Wrapper(sTLCacheFilePath.filename())));
|
fs::path tmpPath = fs::path(sTLCacheFilePath.parent_path()).append(fmt::format("{}__tmp", _pathToUtf8(sTLCacheFilePath.filename())));
|
||||||
std::ofstream fileOut(tmpPath, std::ios::out | std::ios::binary | std::ios::trunc);
|
std::ofstream fileOut(tmpPath, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||||
if (!fileOut.is_open())
|
if (!fileOut.is_open())
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Unable to store title list in {}", _utf8Wrapper(tmpPath));
|
cemuLog_log(LogType::Force, "Unable to store title list in {}", _pathToUtf8(tmpPath));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
doc.save(fileOut, " ", 1, pugi::xml_encoding::encoding_utf8);
|
doc.save(fileOut, " ", 1, pugi::xml_encoding::encoding_utf8);
|
||||||
@ -158,7 +158,7 @@ void CafeTitleList::SetMLCPath(fs::path path)
|
|||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
if (!fs::is_directory(path, ec))
|
if (!fs::is_directory(path, ec))
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "MLC set to invalid path: {}", _utf8Wrapper(path));
|
cemuLog_log(LogType::Force, "MLC set to invalid path: {}", _pathToUtf8(path));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sTLMLCPath = path;
|
sTLMLCPath = path;
|
||||||
@ -211,12 +211,12 @@ void _RemoveTitleFromMultimap(TitleInfo* titleInfo)
|
|||||||
// in the special case that path points to a WUA file, all contained titles will be added
|
// in the special case that path points to a WUA file, all contained titles will be added
|
||||||
void CafeTitleList::AddTitleFromPath(fs::path path)
|
void CafeTitleList::AddTitleFromPath(fs::path path)
|
||||||
{
|
{
|
||||||
if (path.has_extension() && boost::iequals(_utf8Wrapper(path.extension()), ".wua"))
|
if (path.has_extension() && boost::iequals(_pathToUtf8(path.extension()), ".wua"))
|
||||||
{
|
{
|
||||||
ZArchiveReader* zar = ZArchiveReader::OpenFromFile(path);
|
ZArchiveReader* zar = ZArchiveReader::OpenFromFile(path);
|
||||||
if (!zar)
|
if (!zar)
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Found {} but it is not a valid Wii U archive file", _utf8Wrapper(path));
|
cemuLog_log(LogType::Force, "Found {} but it is not a valid Wii U archive file", _pathToUtf8(path));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// enumerate all contained titles
|
// enumerate all contained titles
|
||||||
@ -233,7 +233,7 @@ void CafeTitleList::AddTitleFromPath(fs::path path)
|
|||||||
uint16 parsedVersion;
|
uint16 parsedVersion;
|
||||||
if (!TitleInfo::ParseWuaTitleFolderName(dirEntry.name, parsedId, parsedVersion))
|
if (!TitleInfo::ParseWuaTitleFolderName(dirEntry.name, parsedId, parsedVersion))
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Invalid title directory in {}: \"{}\"", _utf8Wrapper(path), dirEntry.name);
|
cemuLog_log(LogType::Force, "Invalid title directory in {}: \"{}\"", _pathToUtf8(path), dirEntry.name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// valid subdirectory
|
// valid subdirectory
|
||||||
@ -351,7 +351,7 @@ void CafeTitleList::ScanGamePath(const fs::path& path)
|
|||||||
{
|
{
|
||||||
dirsInDirectory.emplace_back(it.path());
|
dirsInDirectory.emplace_back(it.path());
|
||||||
|
|
||||||
std::string dirName = _utf8Wrapper(it.path().filename());
|
std::string dirName = _pathToUtf8(it.path().filename());
|
||||||
if (boost::iequals(dirName, "content"))
|
if (boost::iequals(dirName, "content"))
|
||||||
hasContentFolder = true;
|
hasContentFolder = true;
|
||||||
else if (boost::iequals(dirName, "code"))
|
else if (boost::iequals(dirName, "code"))
|
||||||
@ -366,7 +366,7 @@ void CafeTitleList::ScanGamePath(const fs::path& path)
|
|||||||
// since checking files is slow, we only do it for known file extensions
|
// since checking files is slow, we only do it for known file extensions
|
||||||
if (!it.has_extension())
|
if (!it.has_extension())
|
||||||
continue;
|
continue;
|
||||||
if (!_IsKnownFileExtension(_utf8Wrapper(it.extension())))
|
if (!_IsKnownFileExtension(_pathToUtf8(it.extension())))
|
||||||
continue;
|
continue;
|
||||||
AddTitleFromPath(it);
|
AddTitleFromPath(it);
|
||||||
}
|
}
|
||||||
@ -384,7 +384,7 @@ void CafeTitleList::ScanGamePath(const fs::path& path)
|
|||||||
{
|
{
|
||||||
for (auto& it : dirsInDirectory)
|
for (auto& it : dirsInDirectory)
|
||||||
{
|
{
|
||||||
std::string dirName = _utf8Wrapper(it.filename());
|
std::string dirName = _pathToUtf8(it.filename());
|
||||||
if (!boost::iequals(dirName, "content") &&
|
if (!boost::iequals(dirName, "content") &&
|
||||||
!boost::iequals(dirName, "code") &&
|
!boost::iequals(dirName, "code") &&
|
||||||
!boost::iequals(dirName, "meta"))
|
!boost::iequals(dirName, "meta"))
|
||||||
@ -408,7 +408,7 @@ void CafeTitleList::ScanMLCPath(const fs::path& path)
|
|||||||
if (!it.is_directory())
|
if (!it.is_directory())
|
||||||
continue;
|
continue;
|
||||||
// only scan directories which match the title id naming scheme
|
// only scan directories which match the title id naming scheme
|
||||||
std::string dirName = _utf8Wrapper(it.path().filename());
|
std::string dirName = _pathToUtf8(it.path().filename());
|
||||||
if(dirName.size() != 8)
|
if(dirName.size() != 8)
|
||||||
continue;
|
continue;
|
||||||
bool containsNoHexCharacter = false;
|
bool containsNoHexCharacter = false;
|
||||||
|
@ -420,25 +420,19 @@ inline std::string_view _utf8Wrapper(std::u8string_view input)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a std::u8string as std::string, the contents are left as-is
|
// convert fs::path to utf8 encoded string
|
||||||
inline std::string _utf8Wrapper(const std::u8string& u8str)
|
inline std::string _pathToUtf8(const fs::path& path)
|
||||||
{
|
{
|
||||||
std::string v;
|
std::u8string strU8 = path.generic_u8string();
|
||||||
v.resize(u8str.size());
|
std::string v((const char*)strU8.data(), strU8.size());
|
||||||
memcpy(v.data(), u8str.data(), u8str.size());
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get utf8 generic path string directly from std::filesystem::path
|
// convert utf8 encoded string to fs::path
|
||||||
inline std::string _utf8Wrapper(const fs::path& path)
|
inline fs::path _utf8ToPath(std::string_view input)
|
||||||
{
|
{
|
||||||
return _utf8Wrapper(path.generic_u8string());
|
std::basic_string_view<char8_t> v((char8_t*)input.data(), input.size());
|
||||||
}
|
return fs::path(v);
|
||||||
|
|
||||||
inline std::u8string_view _asUtf8(std::string_view input)
|
|
||||||
{
|
|
||||||
std::basic_string_view<char8_t> v((char8_t*)input.data(), input.size());
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RunAtCemuBoot // -> replaces this with direct function calls. Linkers other than MSVC may optimize way object files entirely if they are not referenced from outside. So a source file self-registering using this would be causing issues
|
class RunAtCemuBoot // -> replaces this with direct function calls. Linkers other than MSVC may optimize way object files entirely if they are not referenced from outside. So a source file self-registering using this would be causing issues
|
||||||
|
@ -47,7 +47,7 @@ public:
|
|||||||
{
|
{
|
||||||
cemu_assert_debug(format.empty() || (format[0] != '/' && format[0] != '\\'));
|
cemu_assert_debug(format.empty() || (format[0] != '/' && format[0] != '\\'));
|
||||||
auto tmp = fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...);
|
auto tmp = fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...);
|
||||||
return GetMlcPath() / fs::path(_asUtf8(tmp));
|
return GetMlcPath() / _utf8ToPath(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ...TArgs>
|
template <typename ...TArgs>
|
||||||
|
@ -417,7 +417,7 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
|||||||
for (const auto& game : graphic_pack_entries)
|
for (const auto& game : graphic_pack_entries)
|
||||||
{
|
{
|
||||||
auto entry = graphic_pack_parser.set("Entry");
|
auto entry = graphic_pack_parser.set("Entry");
|
||||||
entry.set_attribute("filename",_utf8Wrapper(game.first).c_str());
|
entry.set_attribute("filename",_pathToUtf8(game.first).c_str());
|
||||||
for(const auto& kv : game.second)
|
for(const auto& kv : game.second)
|
||||||
{
|
{
|
||||||
// TODO: less hacky pls
|
// TODO: less hacky pls
|
||||||
|
@ -251,7 +251,7 @@ void GameUpdateWindow::ThreadWork()
|
|||||||
error_msg << GetSystemErrorMessage(ex);
|
error_msg << GetSystemErrorMessage(ex);
|
||||||
|
|
||||||
if(currentDirEntry != fs::directory_entry{})
|
if(currentDirEntry != fs::directory_entry{})
|
||||||
error_msg << fmt::format("\n{}\n{}",_("Current file:").ToStdString(), _utf8Wrapper(currentDirEntry.path()));
|
error_msg << fmt::format("\n{}\n{}",_("Current file:").ToStdString(), _pathToUtf8(currentDirEntry.path()));
|
||||||
|
|
||||||
m_thread_exception = error_msg.str();
|
m_thread_exception = error_msg.str();
|
||||||
m_thread_state = ThreadCanceled;
|
m_thread_state = ThreadCanceled;
|
||||||
|
@ -1947,7 +1947,7 @@ public:
|
|||||||
"/*****************************************************************************/\r\n"
|
"/*****************************************************************************/\r\n"
|
||||||
);
|
);
|
||||||
delete fs;
|
delete fs;
|
||||||
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _utf8Wrapper(tempPath))));
|
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(tempPath))));
|
||||||
});
|
});
|
||||||
lineSizer->Add(noticeLink, 0);
|
lineSizer->Add(noticeLink, 0);
|
||||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||||
|
@ -275,7 +275,7 @@ void MemorySearcherTool::Load()
|
|||||||
if (!memSearcherIniContents)
|
if (!memSearcherIniContents)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IniParser iniParser(*memSearcherIniContents, _utf8Wrapper(memorySearcherPath));
|
IniParser iniParser(*memSearcherIniContents, _pathToUtf8(memorySearcherPath));
|
||||||
while (iniParser.NextSection())
|
while (iniParser.NextSection())
|
||||||
{
|
{
|
||||||
auto option_description = iniParser.FindOption("description");
|
auto option_description = iniParser.FindOption("description");
|
||||||
|
@ -480,7 +480,7 @@ void TitleManager::OnSaveOpenDirectory(wxCommandEvent& event)
|
|||||||
if (!fs::exists(target) || !fs::is_directory(target))
|
if (!fs::exists(target) || !fs::is_directory(target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _utf8Wrapper(target))));
|
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(target))));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TitleManager::OnSaveDelete(wxCommandEvent& event)
|
void TitleManager::OnSaveDelete(wxCommandEvent& event)
|
||||||
|
@ -563,7 +563,7 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event)
|
|||||||
{
|
{
|
||||||
fs::path path(gameInfo.GetBase().GetPath());
|
fs::path path(gameInfo.GetBase().GetPath());
|
||||||
_stripPathFilename(path);
|
_stripPathFilename(path);
|
||||||
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _utf8Wrapper(path))));
|
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(path))));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kWikiPage:
|
case kWikiPage:
|
||||||
@ -584,21 +584,21 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event)
|
|||||||
|
|
||||||
case kContextMenuSaveFolder:
|
case kContextMenuSaveFolder:
|
||||||
{
|
{
|
||||||
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _utf8Wrapper(gameInfo.GetSaveFolder()))));
|
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(gameInfo.GetSaveFolder()))));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kContextMenuUpdateFolder:
|
case kContextMenuUpdateFolder:
|
||||||
{
|
{
|
||||||
fs::path path(gameInfo.GetUpdate().GetPath());
|
fs::path path(gameInfo.GetUpdate().GetPath());
|
||||||
_stripPathFilename(path);
|
_stripPathFilename(path);
|
||||||
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _utf8Wrapper(path))));
|
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(path))));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kContextMenuDLCFolder:
|
case kContextMenuDLCFolder:
|
||||||
{
|
{
|
||||||
fs::path path(gameInfo.GetAOC().front().GetPath());
|
fs::path path(gameInfo.GetAOC().front().GetPath());
|
||||||
_stripPathFilename(path);
|
_stripPathFilename(path);
|
||||||
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _utf8Wrapper(path))));
|
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(path))));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kContextMenuEditGraphicPacks:
|
case kContextMenuEditGraphicPacks:
|
||||||
|
@ -293,21 +293,21 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId)
|
|||||||
std::string msg = wxHelper::MakeUTF8(_("The following content will be converted to a compressed Wii U archive file (.wua):\n \n"));
|
std::string msg = wxHelper::MakeUTF8(_("The following content will be converted to a compressed Wii U archive file (.wua):\n \n"));
|
||||||
|
|
||||||
if (titleInfo_base.IsValid())
|
if (titleInfo_base.IsValid())
|
||||||
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game: {}"))), _utf8Wrapper(titleInfo_base.GetPath())));
|
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game: {}"))), _pathToUtf8(titleInfo_base.GetPath())));
|
||||||
else
|
else
|
||||||
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game: Not installed")))));
|
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Base game: Not installed")))));
|
||||||
|
|
||||||
msg.append("\n");
|
msg.append("\n");
|
||||||
|
|
||||||
if (titleInfo_update.IsValid())
|
if (titleInfo_update.IsValid())
|
||||||
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update: {}"))), _utf8Wrapper(titleInfo_update.GetPath())));
|
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update: {}"))), _pathToUtf8(titleInfo_update.GetPath())));
|
||||||
else
|
else
|
||||||
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update: Not installed")))));
|
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("Update: Not installed")))));
|
||||||
|
|
||||||
msg.append("\n");
|
msg.append("\n");
|
||||||
|
|
||||||
if (titleInfo_aoc.IsValid())
|
if (titleInfo_aoc.IsValid())
|
||||||
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC: {}"))), _utf8Wrapper(titleInfo_aoc.GetPath())));
|
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC: {}"))), _pathToUtf8(titleInfo_aoc.GetPath())));
|
||||||
else
|
else
|
||||||
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC: Not installed")))));
|
msg.append(fmt::format(fmt::runtime(wxHelper::MakeUTF8(_("DLC: Not installed")))));
|
||||||
|
|
||||||
@ -778,9 +778,9 @@ bool wxTitleManagerList::DeleteEntry(long index, const TitleEntry& entry)
|
|||||||
wxString msg;
|
wxString msg;
|
||||||
const bool is_directory = fs::is_directory(entry.path);
|
const bool is_directory = fs::is_directory(entry.path);
|
||||||
if(is_directory)
|
if(is_directory)
|
||||||
msg = wxStringFormat2(_("Are you really sure that you want to delete the following folder:\n{}"), wxHelper::FromUtf8(_utf8Wrapper(entry.path)));
|
msg = wxStringFormat2(_("Are you really sure that you want to delete the following folder:\n{}"), wxHelper::FromUtf8(_pathToUtf8(entry.path)));
|
||||||
else
|
else
|
||||||
msg = wxStringFormat2(_("Are you really sure that you want to delete the following file:\n{}"), wxHelper::FromUtf8(_utf8Wrapper(entry.path)));
|
msg = wxStringFormat2(_("Are you really sure that you want to delete the following file:\n{}"), wxHelper::FromUtf8(_pathToUtf8(entry.path)));
|
||||||
|
|
||||||
const auto result = wxMessageBox(msg, _("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this);
|
const auto result = wxMessageBox(msg, _("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this);
|
||||||
if (result == wxNO)
|
if (result == wxNO)
|
||||||
@ -852,7 +852,7 @@ void wxTitleManagerList::OnContextMenuSelected(wxCommandEvent& event)
|
|||||||
case kContextMenuOpenDirectory:
|
case kContextMenuOpenDirectory:
|
||||||
{
|
{
|
||||||
const auto path = fs::is_directory(entry->path) ? entry->path : entry->path.parent_path();
|
const auto path = fs::is_directory(entry->path) ? entry->path : entry->path.parent_path();
|
||||||
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _utf8Wrapper(path))));
|
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(path))));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case kContextMenuDelete:
|
case kContextMenuDelete:
|
||||||
|
@ -170,7 +170,7 @@ void SaveImportWindow::OnImport(wxCommandEvent& event)
|
|||||||
{
|
{
|
||||||
if (!fs::is_directory(target_path))
|
if (!fs::is_directory(target_path))
|
||||||
{
|
{
|
||||||
const auto msg = wxStringFormat2(_("There's already a file at the target directory:\n{}"), _utf8Wrapper(target_path));
|
const auto msg = wxStringFormat2(_("There's already a file at the target directory:\n{}"), _pathToUtf8(target_path));
|
||||||
wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
||||||
m_return_code = wxCANCEL;
|
m_return_code = wxCANCEL;
|
||||||
Close();
|
Close();
|
||||||
|
@ -108,7 +108,7 @@ void SaveTransfer::OnTransfer(wxCommandEvent& event)
|
|||||||
{
|
{
|
||||||
if(!fs::is_directory(target_path))
|
if(!fs::is_directory(target_path))
|
||||||
{
|
{
|
||||||
const auto msg = wxStringFormat2(_("There's already a file at the target directory:\n{}"), _utf8Wrapper(target_path));
|
const auto msg = wxStringFormat2(_("There's already a file at the target directory:\n{}"), _pathToUtf8(target_path));
|
||||||
wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
||||||
m_return_code = wxCANCEL;
|
m_return_code = wxCANCEL;
|
||||||
Close();
|
Close();
|
||||||
|
@ -166,7 +166,7 @@ void reconfigureGLDrivers()
|
|||||||
fs::create_directories(nvCacheDir, err);
|
fs::create_directories(nvCacheDir, err);
|
||||||
|
|
||||||
std::string nvCacheDirEnvOption("__GL_SHADER_DISK_CACHE_PATH=");
|
std::string nvCacheDirEnvOption("__GL_SHADER_DISK_CACHE_PATH=");
|
||||||
nvCacheDirEnvOption.append(_utf8Wrapper(nvCacheDir));
|
nvCacheDirEnvOption.append(_pathToUtf8(nvCacheDir));
|
||||||
|
|
||||||
#if BOOST_OS_WINDOWS
|
#if BOOST_OS_WINDOWS
|
||||||
std::wstring tmpW = boost::nowide::widen(nvCacheDirEnvOption);
|
std::wstring tmpW = boost::nowide::widen(nvCacheDirEnvOption);
|
||||||
|
Loading…
Reference in New Issue
Block a user