FSC: Refactor to use FSCPath instead of legacy code

This commit is contained in:
Exzap 2022-09-09 01:29:27 +02:00
parent 0e0602e8d9
commit b8462cec8b
3 changed files with 135 additions and 247 deletions

View File

@ -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

View File

@ -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();

View File

@ -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);