mirror of https://github.com/cemu-project/Cemu.git
FSC: Refactor to use FSCPath instead of legacy code
This commit is contained in:
parent
0e0602e8d9
commit
b8462cec8b
|
@ -1,7 +1,115 @@
|
|||
#pragma once
|
||||
#include <wchar.h>
|
||||
|
||||
class parsedPathW
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
// path parser and utility class for Wii U paths
|
||||
// optimized to be allocation-free for common path lengths
|
||||
class FSCPath
|
||||
{
|
||||
struct PathNode
|
||||
{
|
||||
PathNode(uint16 offset, uint16 len) : offset(offset), len(len) {};
|
||||
|
||||
uint16 offset;
|
||||
uint16 len;
|
||||
};
|
||||
|
||||
boost::container::small_vector<PathNode, 8> m_nodes;
|
||||
boost::container::small_vector<char, 64> m_names;
|
||||
bool m_isAbsolute{};
|
||||
|
||||
inline bool isSlash(char c)
|
||||
{
|
||||
return c == '\\' || c == '/';
|
||||
}
|
||||
|
||||
void appendNode(const char* name, uint16 nameLen)
|
||||
{
|
||||
if (m_names.size() > 0xFFFF)
|
||||
return;
|
||||
m_nodes.emplace_back((uint16)m_names.size(), nameLen);
|
||||
m_names.insert(m_names.end(), name, name + nameLen);
|
||||
}
|
||||
|
||||
public:
|
||||
FSCPath(std::string_view path)
|
||||
{
|
||||
if (path.empty())
|
||||
return;
|
||||
if (isSlash(path.front()))
|
||||
{
|
||||
m_isAbsolute = true;
|
||||
path.remove_prefix(1);
|
||||
// skip any additional leading slashes
|
||||
while (!path.empty() && isSlash(path.front()))
|
||||
path.remove_prefix(1);
|
||||
}
|
||||
// parse nodes
|
||||
size_t n = 0;
|
||||
size_t nodeNameStartIndex = 0;
|
||||
while (n < path.size())
|
||||
{
|
||||
if (isSlash(path[n]))
|
||||
{
|
||||
size_t nodeNameLen = n - nodeNameStartIndex;
|
||||
if (nodeNameLen > 0xFFFF)
|
||||
nodeNameLen = 0xFFFF; // truncate suspiciously long node names
|
||||
cemu_assert_debug(nodeNameLen > 0);
|
||||
appendNode(path.data() + nodeNameStartIndex, (uint16)nodeNameLen);
|
||||
// skip any repeating slashes
|
||||
while (n < path.size() && isSlash(path[n]))
|
||||
n++;
|
||||
nodeNameStartIndex = n;
|
||||
continue;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
if (nodeNameStartIndex < n)
|
||||
{
|
||||
size_t nodeNameLen = n - nodeNameStartIndex;
|
||||
if (nodeNameLen > 0xFFFF)
|
||||
nodeNameLen = 0xFFFF; // truncate suspiciously long node names
|
||||
appendNode(path.data() + nodeNameStartIndex, (uint16)nodeNameLen);
|
||||
}
|
||||
}
|
||||
|
||||
size_t GetNodeCount() const
|
||||
{
|
||||
return m_nodes.size();
|
||||
}
|
||||
|
||||
std::string_view GetNodeName(size_t index) const
|
||||
{
|
||||
if (index < 0 || index >= m_nodes.size())
|
||||
return std::basic_string_view<char>();
|
||||
return std::basic_string_view<char>(m_names.data() + m_nodes[index].offset, m_nodes[index].len);
|
||||
}
|
||||
|
||||
// returns true if the node names match according to FSA case-insensitivity rules
|
||||
bool MatchNode(sint32 index, std::string_view name) const
|
||||
{
|
||||
if (index < 0 || index >= (sint32)m_nodes.size())
|
||||
return false;
|
||||
auto nodeName = GetNodeName(index);
|
||||
if (nodeName.size() != name.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < nodeName.size(); i++)
|
||||
{
|
||||
char c1 = nodeName[i];
|
||||
char c2 = name[i];
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += ('a' - 'A');
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += ('a' - 'A');
|
||||
if (c1 != c2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class parsedPathW // todo - replaces this with FSCPath (using ascii/utf8 strings instead of wchar)
|
||||
{
|
||||
static const int MAX_NODES = 32;
|
||||
|
||||
|
@ -182,7 +290,6 @@ public:
|
|||
sint32 numNodes;
|
||||
};
|
||||
|
||||
|
||||
template<typename F, bool isCaseSensitive>
|
||||
class FileTree
|
||||
{
|
||||
|
@ -342,113 +449,6 @@ private:
|
|||
node_t rootNode;
|
||||
};
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
// path parser and utility class for Wii U paths
|
||||
// optimized to be allocation-free for common path lengths
|
||||
class FSCPath
|
||||
{
|
||||
struct PathNode
|
||||
{
|
||||
PathNode(uint16 offset, uint16 len) : offset(offset), len(len) {};
|
||||
|
||||
uint16 offset;
|
||||
uint16 len;
|
||||
};
|
||||
|
||||
boost::container::small_vector<PathNode, 8> m_nodes;
|
||||
boost::container::small_vector<char, 64> m_names;
|
||||
bool m_isAbsolute{};
|
||||
|
||||
inline bool isSlash(char c)
|
||||
{
|
||||
return c == '\\' || c == '/';
|
||||
}
|
||||
|
||||
void appendNode(const char* name, uint16 nameLen)
|
||||
{
|
||||
if (m_names.size() > 0xFFFF)
|
||||
return;
|
||||
m_nodes.emplace_back((uint16)m_names.size(), nameLen);
|
||||
m_names.insert(m_names.end(), name, name + nameLen);
|
||||
}
|
||||
|
||||
public:
|
||||
FSCPath(std::string_view path)
|
||||
{
|
||||
if (path.empty())
|
||||
return;
|
||||
if (isSlash(path.front()))
|
||||
{
|
||||
m_isAbsolute = true;
|
||||
path.remove_prefix(1);
|
||||
// skip any additional leading slashes
|
||||
while(!path.empty() && isSlash(path.front()))
|
||||
path.remove_prefix(1);
|
||||
}
|
||||
// parse nodes
|
||||
size_t n = 0;
|
||||
size_t nodeNameStartIndex = 0;
|
||||
while (n < path.size())
|
||||
{
|
||||
if (isSlash(path[n]))
|
||||
{
|
||||
size_t nodeNameLen = n - nodeNameStartIndex;
|
||||
if (nodeNameLen > 0xFFFF)
|
||||
nodeNameLen = 0xFFFF; // truncate suspiciously long node names
|
||||
cemu_assert_debug(nodeNameLen > 0);
|
||||
appendNode(path.data() + nodeNameStartIndex, (uint16)nodeNameLen);
|
||||
// skip any repeating slashes
|
||||
while (n < path.size() && isSlash(path[n]))
|
||||
n++;
|
||||
nodeNameStartIndex = n;
|
||||
continue;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
if (nodeNameStartIndex < n)
|
||||
{
|
||||
size_t nodeNameLen = n - nodeNameStartIndex;
|
||||
if (nodeNameLen > 0xFFFF)
|
||||
nodeNameLen = 0xFFFF; // truncate suspiciously long node names
|
||||
appendNode(path.data() + nodeNameStartIndex, (uint16)nodeNameLen);
|
||||
}
|
||||
}
|
||||
|
||||
size_t GetNodeCount() const
|
||||
{
|
||||
return m_nodes.size();
|
||||
}
|
||||
|
||||
std::string_view GetNodeName(size_t index) const
|
||||
{
|
||||
if (index < 0 || index >= m_nodes.size())
|
||||
return std::basic_string_view<char>();
|
||||
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
|
||||
{
|
||||
if (index < 0 || index >= (sint32)m_nodes.size())
|
||||
return false;
|
||||
auto nodeName = GetNodeName(index);
|
||||
if (nodeName.size() != name.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < nodeName.size(); i++)
|
||||
{
|
||||
char c1 = nodeName[i];
|
||||
char c2 = name[i];
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += ('a' - 'A');
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += ('a' - 'A');
|
||||
if (c1 != c2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static void FSTPathUnitTest()
|
||||
{
|
||||
// test 1
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "Cafe/Filesystem/fsc.h"
|
||||
#include "Cafe/Filesystem/FST/fstUtil.h"
|
||||
|
||||
struct FSCMountPathNode
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
if (a.size() != b.size())
|
||||
|
@ -74,25 +75,25 @@ void fsc_reset()
|
|||
* /vol/content/data -> Map to HostFS
|
||||
* 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);
|
||||
fscEnter();
|
||||
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
|
||||
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.MatchNode(i, nodeItr->path))
|
||||
{
|
||||
// subnode found
|
||||
nodeSub = nodeItr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( nodeSub )
|
||||
if (nodeSub)
|
||||
{
|
||||
// traverse subnode
|
||||
nodeParent = nodeSub;
|
||||
|
@ -100,10 +101,10 @@ FSCMountPathNode* fsc_createMountPath(CoreinitFSParsedPath* parsedMountPath, sin
|
|||
}
|
||||
// no matching subnode, add new entry
|
||||
nodeSub = new FSCMountPathNode(nodeParent);
|
||||
nodeSub->path = coreinitFS_getNodeName(parsedMountPath, i);
|
||||
nodeSub->path = mountPath.GetNodeName(i);
|
||||
nodeSub->priority = priority;
|
||||
nodeParent->subnodes.emplace_back(nodeSub);
|
||||
if( i == (parsedMountPath->numNodes-1) )
|
||||
if (i == (mountPath.GetNodeCount() - 1))
|
||||
{
|
||||
// last node
|
||||
fscLeave();
|
||||
|
@ -114,7 +115,7 @@ FSCMountPathNode* fsc_createMountPath(CoreinitFSParsedPath* parsedMountPath, sin
|
|||
}
|
||||
// path is empty or already mounted
|
||||
fscLeave();
|
||||
if (parsedMountPath->numNodes == 0)
|
||||
if (mountPath.GetNodeCount() == 0)
|
||||
return nodeParent;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -129,12 +130,10 @@ sint32 fsc_mount(std::string_view mountPath, std::string_view targetPath, fscDev
|
|||
if (!targetPathWithSlash.empty() && (targetPathWithSlash.back() != '/' && targetPathWithSlash.back() != '\\'))
|
||||
targetPathWithSlash.push_back('/');
|
||||
|
||||
// parse mount path
|
||||
CoreinitFSParsedPath parsedMountPath;
|
||||
coreinitFS_parsePath(&parsedMountPath, mountPathTmp.c_str());
|
||||
FSCPath parsedMountPath(mountPathTmp);
|
||||
// register path
|
||||
fscEnter();
|
||||
FSCMountPathNode* node = fsc_createMountPath(&parsedMountPath, priority);
|
||||
FSCMountPathNode* node = fsc_createMountPath(parsedMountPath, priority);
|
||||
if( !node )
|
||||
{
|
||||
// path empty, invalid or already used
|
||||
|
@ -152,9 +151,6 @@ sint32 fsc_mount(std::string_view mountPath, std::string_view targetPath, fscDev
|
|||
bool fsc_unmount(std::string_view mountPath, sint32 priority)
|
||||
{
|
||||
std::string _tmp(mountPath);
|
||||
CoreinitFSParsedPath parsedMountPath;
|
||||
coreinitFS_parsePath(&parsedMountPath, _tmp.c_str());
|
||||
|
||||
fscEnter();
|
||||
FSCMountPathNode* mountPathNode = fsc_lookupPathVirtualNode(_tmp.c_str(), priority);
|
||||
if (!mountPathNode)
|
||||
|
@ -189,19 +185,17 @@ void fsc_unmountAll()
|
|||
// 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)
|
||||
{
|
||||
// parse path
|
||||
CoreinitFSParsedPath parsedPath;
|
||||
coreinitFS_parsePath(&parsedPath, path);
|
||||
FSCPath parsedPath(path);
|
||||
FSCMountPathNode* nodeParent = s_fscRootNodePerPrio[priority];
|
||||
sint32 i;
|
||||
size_t i;
|
||||
fscEnter();
|
||||
for (i = 0; i < parsedPath.numNodes; i++)
|
||||
for (i = 0; i < parsedPath.GetNodeCount(); i++)
|
||||
{
|
||||
// search for subdirectory
|
||||
FSCMountPathNode* nodeSub = nullptr;
|
||||
for(auto& nodeItr : nodeParent->subnodes)
|
||||
{
|
||||
if (coreinitFS_checkNodeName(&parsedPath, i, nodeItr->path.c_str()))
|
||||
if (parsedPath.MatchNode(i, nodeItr->path))
|
||||
{
|
||||
nodeSub = nodeItr;
|
||||
break;
|
||||
|
@ -215,17 +209,17 @@ bool fsc_lookupPath(const char* path, std::wstring& devicePathOut, fscDeviceC**
|
|||
// no matching subnode
|
||||
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)
|
||||
{
|
||||
if (nodeParent->device)
|
||||
{
|
||||
devicePathOut = boost::nowide::widen(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));
|
||||
if (f < (parsedPath.numNodes - 1))
|
||||
if (f < (parsedPath.GetNodeCount() - 1))
|
||||
devicePathOut.push_back('/');
|
||||
}
|
||||
*fscDeviceOut = nodeParent->device;
|
||||
|
@ -243,19 +237,16 @@ bool fsc_lookupPath(const char* path, std::wstring& devicePathOut, fscDeviceC**
|
|||
// lookup path and find virtual device node
|
||||
FSCMountPathNode* fsc_lookupPathVirtualNode(const char* path, sint32 priority)
|
||||
{
|
||||
// parse path
|
||||
CoreinitFSParsedPath parsedPath;
|
||||
coreinitFS_parsePath(&parsedPath, path);
|
||||
FSCPath parsedPath(path);
|
||||
FSCMountPathNode* nodeCurrentDir = s_fscRootNodePerPrio[priority];
|
||||
sint32 i;
|
||||
fscEnter();
|
||||
for (i = 0; i < parsedPath.numNodes; i++)
|
||||
for (size_t i = 0; i < parsedPath.GetNodeCount(); i++)
|
||||
{
|
||||
// search for subdirectory
|
||||
FSCMountPathNode* nodeSub = nullptr;
|
||||
for (auto& nodeItr : nodeCurrentDir->subnodes)
|
||||
{
|
||||
if (coreinitFS_checkNodeName(&parsedPath, i, nodeItr->path.c_str()))
|
||||
if (parsedPath.MatchNode(i, nodeItr->path))
|
||||
{
|
||||
nodeSub = nodeItr;
|
||||
break;
|
||||
|
@ -693,7 +684,7 @@ bool fsc_doesFileExist(const char* path, sint32 maxPriority)
|
|||
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)
|
||||
{
|
||||
fscDeviceC* fscDevice = nullptr;
|
||||
|
@ -710,93 +701,7 @@ bool fsc_doesDirectoryExist(const char* path, sint32 maxPriority)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
// initialize Cemu's virtual filesystem
|
||||
void fsc_init()
|
||||
{
|
||||
fsc_reset();
|
||||
|
|
|
@ -200,20 +200,3 @@ bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTarg
|
|||
// redirect device
|
||||
void fscDeviceRedirect_map();
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue