From b8462cec8b8b6d7adb75d621268ddb187b358f25 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 9 Sep 2022 01:29:27 +0200 Subject: [PATCH] FSC: Refactor to use FSCPath instead of legacy code --- src/Cafe/Filesystem/FST/fstUtil.h | 218 +++++++++++++++--------------- src/Cafe/Filesystem/fsc.cpp | 147 ++++---------------- src/Cafe/Filesystem/fsc.h | 17 --- 3 files changed, 135 insertions(+), 247 deletions(-) diff --git a/src/Cafe/Filesystem/FST/fstUtil.h b/src/Cafe/Filesystem/FST/fstUtil.h index d0779e59..a1848d61 100644 --- a/src/Cafe/Filesystem/FST/fstUtil.h +++ b/src/Cafe/Filesystem/FST/fstUtil.h @@ -1,7 +1,115 @@ #pragma once #include -class parsedPathW +#include + +// 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 m_nodes; + boost::container::small_vector 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(); + return std::basic_string_view(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 class FileTree { @@ -342,113 +449,6 @@ private: node_t rootNode; }; -#include - -// 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 m_nodes; - boost::container::small_vector 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(); - return std::basic_string_view(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 diff --git a/src/Cafe/Filesystem/fsc.cpp b/src/Cafe/Filesystem/fsc.cpp index 5f9351e8..0f281bab 100644 --- a/src/Cafe/Filesystem/fsc.cpp +++ b/src/Cafe/Filesystem/fsc.cpp @@ -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; inumNodes; 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(); diff --git a/src/Cafe/Filesystem/fsc.h b/src/Cafe/Filesystem/fsc.h index 37e437fb..a475bfb2 100644 --- a/src/Cafe/Filesystem/fsc.h +++ b/src/Cafe/Filesystem/fsc.h @@ -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);