FSC: Refactor FileTree and remove redundant path parser class

This commit is contained in:
Exzap 2022-09-09 03:14:21 +02:00
parent b8462cec8b
commit 1ce629126a
3 changed files with 61 additions and 245 deletions

View File

@ -87,17 +87,14 @@ public:
} }
// returns true if the node names match according to FSA case-insensitivity rules // returns true if the node names match according to FSA case-insensitivity rules
bool MatchNode(sint32 index, std::string_view name) const 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')
@ -107,191 +104,18 @@ public:
} }
return true; return true;
} }
};
class parsedPathW // todo - replaces this with FSCPath (using ascii/utf8 strings instead of wchar) bool MatchNodeName(sint32 index, std::string_view name) const
{
static const int MAX_NODES = 32;
public:
parsedPathW(std::wstring_view path)
{ {
// init vars if (index < 0 || index >= (sint32)m_nodes.size())
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; return false;
const wchar_t* nodeName = this->pathData.data() + this->nodeOffset[index]; auto nodeName = GetNodeName(index);
if (boost::iequals(nodeName, name)) return MatchNodeName(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> template<typename F>
class FileTree class FSAFileTree
{ {
public: public:
@ -303,27 +127,27 @@ private:
NODETYPE_FILE, NODETYPE_FILE,
}; };
typedef struct _node_t struct node_t
{ {
std::wstring name; std::string name;
std::vector<_node_t*> subnodes; std::vector<node_t*> subnodes;
F* custom; F* custom;
NODETYPE type; NODETYPE type;
}node_t; };
node_t* getByNodePath(parsedPathW& p, sint32 numNodes, bool createAsDirectories) node_t* getByNodePath(FSCPath& p, sint32 numNodes, bool createAsDirectories)
{ {
node_t* currentNode = &rootNode; node_t* currentNode = &rootNode;
for (sint32 i = 0; i < numNodes; i++) for (sint32 i = 0; i < numNodes; i++)
{ {
// find subnode by path // find subnode by path
node_t* foundSubnode = getSubnode(currentNode, p.getNodeName(i)); node_t* foundSubnode = getSubnode(currentNode, p.GetNodeName(i));
if (foundSubnode == nullptr) if (foundSubnode == nullptr)
{ {
// no subnode found -> create new directory node (if requested) // no subnode found -> create new directory node (if requested)
if (createAsDirectories == false) if (createAsDirectories == false)
return nullptr; // path not found return nullptr; // path not found
currentNode = newNode(currentNode, NODETYPE_DIRECTORY, p.getNodeName(i)); currentNode = newNode(currentNode, NODETYPE_DIRECTORY, p.GetNodeName(i));
} }
else else
{ {
@ -333,28 +157,20 @@ private:
return currentNode; return currentNode;
} }
node_t* getSubnode(node_t* parentNode, std::wstring_view name) node_t* getSubnode(node_t* parentNode, std::string_view name)
{ {
for (auto& sn : parentNode->subnodes) for (auto& sn : parentNode->subnodes)
{ {
if constexpr (isCaseSensitive) if (FSCPath::MatchNodeName(sn->name, name))
{ return sn;
if (parsedPathW::compareNodeName(sn->name.c_str(), name))
return sn;
}
else
{
if (parsedPathW::compareNodeNameCaseInsensitive(sn->name.c_str(), name))
return sn;
}
} }
return nullptr; return nullptr;
} }
node_t* newNode(node_t* parentNode, NODETYPE type, std::wstring_view name) node_t* newNode(node_t* parentNode, NODETYPE type, std::string_view name)
{ {
node_t* newNode = new node_t; node_t* newNode = new node_t;
newNode->name = std::wstring(name); newNode->name.assign(name);
newNode->type = type; newNode->type = type;
newNode->custom = nullptr; newNode->custom = nullptr;
parentNode->subnodes.push_back(newNode); parentNode->subnodes.push_back(newNode);
@ -362,32 +178,32 @@ private:
} }
public: public:
FileTree() FSAFileTree()
{ {
rootNode.type = NODETYPE_DIRECTORY; rootNode.type = NODETYPE_DIRECTORY;
} }
bool addFile(const wchar_t* path, F* custom) bool addFile(std::string_view path, F* custom)
{ {
parsedPathW p(path); FSCPath p(path);
if (p.numNodes == 0) if (p.GetNodeCount() == 0)
return false; return false;
node_t* directoryNode = getByNodePath(p, p.numNodes - 1, true); node_t* directoryNode = getByNodePath(p, p.GetNodeCount() - 1, true);
// check if a node with same name already exists // check if a node with same name already exists
if (getSubnode(directoryNode, p.getNodeName(p.numNodes - 1)) != nullptr) if (getSubnode(directoryNode, p.GetNodeName(p.GetNodeCount() - 1)) != nullptr)
return false; // node already exists return false; // node already exists
// add file node // add file node
node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.getNodeName(p.numNodes - 1)); node_t* fileNode = newNode(directoryNode, NODETYPE_FILE, p.GetNodeName(p.GetNodeCount() - 1));
fileNode->custom = custom; fileNode->custom = custom;
return true; return true;
} }
bool getFile(std::wstring_view path, F* &custom) bool getFile(std::string_view path, F* &custom)
{ {
parsedPathW p(path); FSCPath p(path);
if (p.numNodes == 0) if (p.GetNodeCount() == 0)
return false; return false;
node_t* node = getByNodePath(p, p.numNodes, false); node_t* node = getByNodePath(p, p.GetNodeCount(), false);
if (node == nullptr) if (node == nullptr)
return false; return false;
if (node->type != NODETYPE_FILE) if (node->type != NODETYPE_FILE)
@ -396,16 +212,16 @@ public:
return true; return true;
} }
bool removeFile(std::wstring_view path) bool removeFile(std::string_view path)
{ {
parsedPathW p(path); FSCPath p(path);
if (p.numNodes == 0) if (p.GetNodeCount() == 0)
return false; return false;
node_t* directoryNode = getByNodePath(p, p.numNodes - 1, false); node_t* directoryNode = getByNodePath(p, p.GetNodeCount() - 1, false);
if (directoryNode == nullptr) if (directoryNode == nullptr)
return false; return false;
// find node // find node
node_t* fileNode = getSubnode(directoryNode, p.getNodeName(p.numNodes - 1)); node_t* fileNode = getSubnode(directoryNode, p.GetNodeName(p.GetNodeCount() - 1));
if (fileNode == nullptr) if (fileNode == nullptr)
return false; return false;
if (fileNode->type != NODETYPE_FILE) if (fileNode->type != NODETYPE_FILE)
@ -423,10 +239,10 @@ public:
} }
template<typename TFunc> template<typename TFunc>
bool listDirectory(const wchar_t* path, TFunc fn) bool listDirectory(std::string_view path, TFunc fn)
{ {
parsedPathW p(path); FSCPath p(path);
node_t* node = getByNodePath(p, p.numNodes, false); node_t* node = getByNodePath(p, p.GetNodeCount(), false);
if (node == nullptr) if (node == nullptr)
return false; return false;
if (node->type != NODETYPE_DIRECTORY) if (node->type != NODETYPE_DIRECTORY)
@ -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);

View File

@ -86,7 +86,7 @@ FSCMountPathNode* fsc_createMountPath(const FSCPath& mountPath, sint32 priority)
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 (mountPath.MatchNode(i, nodeItr->path)) if (mountPath.MatchNodeName(i, nodeItr->path))
{ {
// subnode found // subnode found
nodeSub = nodeItr; nodeSub = nodeItr;
@ -195,7 +195,7 @@ bool fsc_lookupPath(const char* path, std::wstring& devicePathOut, fscDeviceC**
FSCMountPathNode* nodeSub = nullptr; FSCMountPathNode* nodeSub = nullptr;
for(auto& nodeItr : nodeParent->subnodes) for(auto& nodeItr : nodeParent->subnodes)
{ {
if (parsedPath.MatchNode(i, nodeItr->path)) if (parsedPath.MatchNodeName(i, nodeItr->path))
{ {
nodeSub = nodeItr; nodeSub = nodeItr;
break; break;
@ -246,7 +246,7 @@ FSCMountPathNode* fsc_lookupPathVirtualNode(const char* path, sint32 priority)
FSCMountPathNode* nodeSub = nullptr; FSCMountPathNode* nodeSub = nullptr;
for (auto& nodeItr : nodeCurrentDir->subnodes) for (auto& nodeItr : nodeCurrentDir->subnodes)
{ {
if (parsedPath.MatchNode(i, nodeItr->path)) if (parsedPath.MatchNodeName(i, nodeItr->path))
{ {
nodeSub = nodeItr; nodeSub = nodeItr;
break; break;

View File

@ -9,23 +9,22 @@ 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
@ -33,7 +32,8 @@ class fscDeviceTypeRedirect : public fscDeviceC
FSCVirtualFile* fscDeviceOpenByPath(std::wstring_view pathW, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override FSCVirtualFile* fscDeviceOpenByPath(std::wstring_view pathW, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
{ {
RedirectEntry* redirectionEntry; RedirectEntry* redirectionEntry;
if (redirectTree.getFile(pathW, redirectionEntry)) std::string pathTmp = boost::nowide::narrow(pathW);
if (redirectTree.getFile(pathTmp, redirectionEntry))
return FSCVirtualFile_Host::OpenFile(redirectionEntry->dstPath, accessFlags, *fscStatus); return FSCVirtualFile_Host::OpenFile(redirectionEntry->dstPath, accessFlags, *fscStatus);
return nullptr; return nullptr;
} }