Skip to content

Commit

Permalink
#5108: Move AssetsList and FileVisitor classes to libs/vfs/ to make i…
Browse files Browse the repository at this point in the history
…t usable from other places
  • Loading branch information
codereader committed Nov 19, 2020
1 parent f8265f0 commit 1987863
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 194 deletions.
83 changes: 83 additions & 0 deletions libs/vfs/AssetsList.h
@@ -0,0 +1,83 @@
#pragma once

#include <map>
#include "ifilesystem.h"
#include "string/predicate.h"
#include "string/split.h"

namespace vfs
{

// Representation of an assets.lst file, containing visibility information for
// assets within a particular folder.
class AssetsList
{
std::map<std::string, Visibility> _visibilities;

// Convert visibility string to enum value
static Visibility toVisibility(const std::string& input)
{
if (string::starts_with(input, "hid" /* 'hidden' or 'hide'*/))
{
return Visibility::HIDDEN;
}
else if (input == "normal")
{
return Visibility::NORMAL;
}
else
{
rWarning() << "AssetsList: failed to parse visibility '" << input
<< "'" << std::endl;
return Visibility::NORMAL;
}
}

public:

static constexpr const char* FILENAME = "assets.lst";

// Construct with possible ArchiveTextFile pointer containing an assets.lst
// file to parse.
explicit AssetsList(const ArchiveTextFilePtr& inputFile)
{
if (inputFile)
{
// Read lines from the file
std::istream stream(&inputFile->getInputStream());
while (stream.good())
{
std::string line;
std::getline(stream, line);

// Attempt to parse the line as "asset=visibility"
std::vector<std::string> tokens;
string::split(tokens, line, "=");

// Parsing was a success if we have two tokens
if (tokens.size() == 2)
{
std::string filename = tokens[0];
Visibility v = toVisibility(tokens[1]);
_visibilities[filename] = v;
}
}
}
}

// Return visibility for a given file
Visibility getVisibility(const std::string& fileName) const
{
auto i = _visibilities.find(fileName);
if (i == _visibilities.end())
{
return Visibility::NORMAL;
}
else
{
return i->second;
}
}
};

}
131 changes: 131 additions & 0 deletions libs/vfs/FileVisitor.h
@@ -0,0 +1,131 @@
#pragma once

#include <set>

#include "ifilesystem.h"
#include "AssetsList.h"

namespace vfs
{

/*
* IArchive::Visitor class used in GlobalFileSystem().foreachFile().
* It's filtering out the files matching the defined extension only.
* The directory part is cut off the filename.
* On top of that, this class maintains a list of visited files to avoid
* hitting the same file twice (it might be present in more than one Archive).
*/
class FileVisitor :
public IArchive::Visitor
{
// Maximum traversal depth
std::size_t _maxDepth;

// User-supplied functor to invoke for each file
VirtualFileSystem::VisitorFunc _visitorFunc;

// Optional AssetsList containing visibility information
const AssetsList* _assetsList = nullptr;

// Set of already-visited files to avoid visiting the same file twice
std::set<std::string> _visitedFiles;

// Directory to search within
std::string _directory;

// Extension to match
std::string _extension;

// The length of the directory name
std::size_t _dirPrefixLength;

bool _visitAll;

std::size_t _extLength;

public:

// Constructor. Pass "*" as extension to have it visit all files.
FileVisitor(const VirtualFileSystem::VisitorFunc& visitorFunc,
const std::string& dir, const std::string& ext,
std::size_t maxDepth) :
_maxDepth(maxDepth),
_visitorFunc(visitorFunc),
_directory(dir),
_extension(ext),
_dirPrefixLength(_directory.length()),
_visitAll(_extension == "*"),
_extLength(_extension.length())
{}

void setAssetsList(const AssetsList& list)
{
_assetsList = &list;
}

// Archive::Visitor interface
void visitFile(const std::string& name) override
{
#ifdef OS_CASE_INSENSITIVE
// The name should start with the directory, "def/" for instance, case-insensitively.
assert(string::to_lower_copy(name.substr(0, _dirPrefixLength)) == _directory);
#else
// Linux: The name should start with the directory, "def/" for instance, including case.
assert(name.substr(0, _dirPrefixLength) == _directory);
#endif

// Cut off the base directory prefix
std::string subname = name.substr(_dirPrefixLength);

// Check for matching file extension
if (!_visitAll)
{
// The dot must be at the right position
if (subname.length() <= _extLength || subname[subname.length() - _extLength - 1] != '.')
{
return;
}

// And the extension must match
std::string ext = subname.substr(subname.length() - _extLength);

#ifdef OS_CASE_INSENSITIVE
// Treat extensions case-insensitively in Windows
string::to_lower(ext);
#endif

if (ext != _extension)
{
return; // extension mismatch
}
}

if (_visitedFiles.find(subname) != _visitedFiles.end())
{
return; // already visited
}

// Don't return assets.lst itself
if (subname == AssetsList::FILENAME)
return;

// Suitable file, call the callback and add to visited file set
vfs::Visibility vis = _assetsList ? _assetsList->getVisibility(subname)
: Visibility::NORMAL;
_visitorFunc(vfs::FileInfo{ _directory, subname, vis });

_visitedFiles.insert(subname);
}

bool visitDirectory(const std::string& name, std::size_t depth) override
{
if (depth == _maxDepth)
{
return true;
}

return false;
}
};

}

0 comments on commit 1987863

Please sign in to comment.