Skip to content

Commit

Permalink
#5567: Add VirtualFileSystem.getFileInfo method to specifically ask f…
Browse files Browse the repository at this point in the history
…or the vfs::FileInfo structure of a particular file.

Add some unit test coverage to these new functions.
  • Loading branch information
codereader committed Apr 3, 2021
1 parent ca2fbe5 commit f02882f
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 11 deletions.
3 changes: 2 additions & 1 deletion include/iarchive.h
Expand Up @@ -81,7 +81,8 @@ typedef std::shared_ptr<ArchiveTextFile> ArchiveTextFilePtr;
*
* \ingroup vfs
*/
class IArchive
class IArchive :
public IArchiveFileInfoProvider
{
public:
typedef std::shared_ptr<IArchive> Ptr;
Expand Down
16 changes: 14 additions & 2 deletions include/ifilesystem.h
Expand Up @@ -63,8 +63,11 @@ class FileInfo
// Info provider to load additional info on demand, used by e.g. getSize()
IArchiveFileInfoProvider* _infoProvider;
public:
FileInfo(const std::string& topDir_, const std::string& name_,
Visibility visibility_) :
FileInfo() :
FileInfo(std::string(), std::string(), Visibility::HIDDEN)
{}

FileInfo(const std::string& topDir_, const std::string& name_, Visibility visibility_) :
_infoProvider(nullptr),
topDir(topDir_),
name(name_),
Expand All @@ -90,6 +93,11 @@ class FileInfo
/// Visibility of the file
Visibility visibility = Visibility::NORMAL;

bool isEmpty() const
{
return name.empty();
}

/// Return the full mod-relative path, including the containing directory
std::string fullPath() const
{
Expand Down Expand Up @@ -250,6 +258,10 @@ class VirtualFileSystem :

// Returns the list of registered VFS paths, ordered by search priority
virtual const SearchPaths& getVfsSearchPaths() = 0;

// Gets the file info structure for the given VFS file.
// The info structure will be empty if the file was not located in the current VFS tree
virtual vfs::FileInfo getFileInfo(const std::string& vfsRelativePath) = 0;
};

}
Expand Down
23 changes: 23 additions & 0 deletions libs/os/path.h
Expand Up @@ -228,6 +228,29 @@ namespace os
return path.substr(0, lastSlash + 1);
}

/**
* Returns the outermost directory name of the given path, which must be
* provided in standardised form (forward slashes only).
*
* E.g.
* blah/bleh/file.ext -> "blah/"
* blah/bloog -> "blah/"
* blah -> ""
* test.mtr -> ""
* "" -> ""
*/
inline std::string getToplevelDirectory(const std::string& path)
{
std::size_t firstSlash = path.find('/');

if (firstSlash == std::string::npos)
{
return std::string();
}

return path.substr(0, firstSlash + 1); // include the slash
}

/**
* Returns true if the given string qualifies as path to a directory,
* which is equal to the path string ending with the slash '/' character.
Expand Down
3 changes: 1 addition & 2 deletions radiantcore/vfs/DirectoryArchive.h
Expand Up @@ -10,8 +10,7 @@
* added to the list of PK4 archives, using this class.
*/
class DirectoryArchive final :
public IArchive,
public IArchiveFileInfoProvider
public IArchive
{
std::string _root;

Expand Down
43 changes: 39 additions & 4 deletions radiantcore/vfs/Doom3FileSystem.cpp
Expand Up @@ -197,6 +197,34 @@ int Doom3FileSystem::getFileCount(const std::string& filename)
return count;
}

FileInfo Doom3FileSystem::getFileInfo(const std::string& vfsRelativePath)
{
for (const ArchiveDescriptor& descriptor : _archives)
{
if (!descriptor.archive->containsFile(vfsRelativePath))
{
continue;
}

// Determine the visibility of this file
auto topLevelDir = os::getToplevelDirectory(vfsRelativePath);

auto visibility = Visibility::NORMAL;
auto assetsList = findAssetsList(topLevelDir);

if (assetsList)
{
// Information in the assets list file are relative to the top-level dir
auto relativePath = os::getRelativePath(vfsRelativePath, topLevelDir);
visibility = assetsList->getVisibility(relativePath);
}

return FileInfo("", vfsRelativePath, visibility, *descriptor.archive);
}

return FileInfo();
}

ArchiveFilePtr Doom3FileSystem::openFile(const std::string& filename)
{
if (filename.find("\\") != std::string::npos)
Expand Down Expand Up @@ -271,6 +299,15 @@ IArchive::Ptr Doom3FileSystem::openArchiveInAbsolutePath(const std::string& path
return std::make_shared<archive::ZipArchive>(pathToArchive);
}

std::shared_ptr<AssetsList> Doom3FileSystem::findAssetsList(const std::string& topLevelDir)
{
// Look for an assets.lst in the top-level dir (can be an empty empty)
std::string assetsLstName = topLevelDir + AssetsList::FILENAME;

ArchiveTextFilePtr assetsLstFile = openTextFile(assetsLstName);
return std::make_shared<AssetsList>(assetsLstFile);
}

void Doom3FileSystem::forEachFile(const std::string& basedir,
const std::string& extension,
const VisitorFunc& visitorFunc,
Expand All @@ -279,13 +316,11 @@ void Doom3FileSystem::forEachFile(const std::string& basedir,
std::string dirWithSlash = os::standardPathWithSlash(basedir);

// Look for an assets.lst in the base dir
std::string assetsLstName = dirWithSlash + AssetsList::FILENAME;
ArchiveTextFilePtr assetsLstFile = openTextFile(assetsLstName);
AssetsList assetsList(assetsLstFile);
auto assetsList = findAssetsList(dirWithSlash);

// Construct our FileVisitor filtering out the right elements
FileVisitor fileVisitor(visitorFunc, dirWithSlash, extension, depth);
fileVisitor.setAssetsList(assetsList);
fileVisitor.setAssetsList(*assetsList);

// Visit each Archive, applying the FileVisitor to each one (which in
// turn calls the callback for each matching file.
Expand Down
5 changes: 5 additions & 0 deletions radiantcore/vfs/Doom3FileSystem.h
Expand Up @@ -6,6 +6,8 @@
namespace vfs
{

class AssetsList;

class Doom3FileSystem :
public VirtualFileSystem
{
Expand Down Expand Up @@ -67,6 +69,7 @@ class Doom3FileSystem :
void removeObserver(Observer& observer) override;

const SearchPaths& getVfsSearchPaths() override;
FileInfo getFileInfo(const std::string& vfsRelativePath) override;

// RegisterableModule implementation
const std::string& getName() const override;
Expand All @@ -77,6 +80,8 @@ class Doom3FileSystem :
private:
void initDirectory(const std::string& path);
void initPakFile(const std::string& filename);

std::shared_ptr<AssetsList> findAssetsList(const std::string& topLevelPath);
};

}
3 changes: 1 addition & 2 deletions radiantcore/vfs/ZipArchive.h
Expand Up @@ -18,8 +18,7 @@ namespace archive
* Archives are owned and instantiated by the GlobalFileSystem instance.
*/
class ZipArchive final :
public IArchive,
public IArchiveFileInfoProvider
public IArchive
{
private:
class ZipRecord
Expand Down
12 changes: 12 additions & 0 deletions test/Basic.cpp
Expand Up @@ -62,4 +62,16 @@ TEST(PathTests, RemoveFileExtension)
EXPECT_EQ(os::removeExtension("dds/textures/darkmod/test.dds"), "dds/textures/darkmod/test");
}

TEST(PathTests, GetToplevelDirectory)
{
EXPECT_EQ(os::getToplevelDirectory(""), "");
EXPECT_EQ(os::getToplevelDirectory("file55"), "");
EXPECT_EQ(os::getToplevelDirectory("file.tga"), "");
EXPECT_EQ(os::getToplevelDirectory("dir22/"), "dir22/");
EXPECT_EQ(os::getToplevelDirectory("relativefolder/file.tga"), "relativefolder/");
EXPECT_EQ(os::getToplevelDirectory("c:/absolutepath/tork.bak"), "c:/");
EXPECT_EQ(os::getToplevelDirectory("/absolutepath/tork.doc"), "/");
EXPECT_EQ(os::getToplevelDirectory("dds/textures/darkmod/test.dds"), "dds/");
}

}
31 changes: 31 additions & 0 deletions test/VFS.cpp
Expand Up @@ -209,4 +209,35 @@ TEST_F(VfsTest, VisitEachFileInAbsolutePath)
EXPECT_EQ(foundFiles.count("materials/___NONEXISTENTFILE.mtr"), 0);
}

TEST_F(VfsTest, GetFileInfo)
{
// Use a visitor to walk the tree
std::map<std::string, vfs::FileInfo> foundFiles;

auto info = GlobalFileSystem().getFileInfo("models/moss_patch.ase");
EXPECT_FALSE(info.isEmpty());
EXPECT_TRUE(info.getIsPhysicalFile());
EXPECT_EQ(info.visibility, vfs::Visibility::NORMAL);

info = GlobalFileSystem().getFileInfo("materials/example.mtr");
EXPECT_FALSE(info.isEmpty());
EXPECT_TRUE(info.getIsPhysicalFile());
EXPECT_EQ(info.visibility, vfs::Visibility::NORMAL);

// Unit cube should be hidden
info = GlobalFileSystem().getFileInfo("models/darkmod/test/unit_cube.ase");
EXPECT_FALSE(info.isEmpty());
EXPECT_FALSE(info.getIsPhysicalFile());
EXPECT_EQ(info.visibility, vfs::Visibility::HIDDEN);

info = GlobalFileSystem().getFileInfo("models/darkmod/test/unit_cube.lwo");
EXPECT_FALSE(info.isEmpty());
EXPECT_FALSE(info.getIsPhysicalFile());
EXPECT_EQ(info.visibility, vfs::Visibility::NORMAL);

info = GlobalFileSystem().getFileInfo("nonexistentfile.lwo");
EXPECT_TRUE(info.isEmpty());
EXPECT_EQ(info.visibility, vfs::Visibility::HIDDEN);
}

}

0 comments on commit f02882f

Please sign in to comment.