0ad/source/lib/file/vfs/vfs_populate.cpp
Ralph Sennhauser 4324dc1447
Fix some includes in source/lib
Make include-what-you-use happy with some files in source/lib and fix
what needs to be fixed.

Ref: #8086
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2025-07-17 20:07:25 +02:00

206 lines
6.9 KiB
C++

/* Copyright (C) 2025 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* populate VFS directories with files
*/
#include "precompiled.h"
#include "vfs_populate.h"
#include "lib/code_annotation.h"
#include "lib/file/archive/archive.h"
#include "lib/file/archive/archive_zip.h"
#include "lib/file/file_system.h"
#include "lib/file/vfs/vfs.h" // error codes
#include "lib/file/vfs/vfs_lookup.h"
#include "lib/file/vfs/vfs_path.h"
#include "lib/file/vfs/vfs_tree.h"
#include "lib/os_path.h"
#include "lib/path.h"
#include <cstddef>
#include <cstdint>
struct CompareFileInfoByName
{
bool operator()(const CFileInfo& a, const CFileInfo& b)
{
return a.Name() < b.Name();
}
};
// helper class that allows breaking up the logic into sub-functions without
// always having to pass directory/realDirectory as parameters.
class PopulateHelper
{
NONCOPYABLE(PopulateHelper);
public:
PopulateHelper(VfsDirectory* directory, const PRealDirectory& realDirectory)
: m_directory(directory), m_realDirectory(realDirectory)
{
}
Status AddEntries() const
{
CFileInfos files; files.reserve(500);
DirectoryNames subdirectoryNames; subdirectoryNames.reserve(50);
RETURN_STATUS_IF_ERR(GetDirectoryEntries(m_realDirectory->Path(), &files, &subdirectoryNames));
// Since .DELETED files only remove files in lower priority mods
// loose files and archive files have no conflicts so we do not need
// to sort them.
// We add directories after they might have been removed by .DELETED
// files (as they did not contain any files at that point). The order
// of GetDirectoryEntries is undefined, but that does not really matter (TODO really?)
// so we do not need to sort its output.
RETURN_STATUS_IF_ERR(AddFiles(files));
AddSubdirectories(subdirectoryNames);
return INFO::OK;
}
private:
void AddFile(const CFileInfo& fileInfo) const
{
const VfsPath name = fileInfo.Name();
const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory);
if(name.Extension() == L".DELETED")
{
m_directory->DeleteSubtree(file);
if(!(m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))
return;
}
m_directory->AddFile(file);
}
static void AddArchiveFile(const VfsPath& pathname, const CFileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData)
{
PopulateHelper* this_ = (PopulateHelper*)cbData;
// (we have to create missing subdirectoryNames because archivers
// don't always place directory entries before their files)
const size_t flags = VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE;
VfsDirectory* directory;
WARN_IF_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags));
const VfsPath name = fileInfo.Name();
const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile);
if(name.Extension() == L".DELETED")
{
directory->DeleteSubtree(file);
if(!(this_->m_realDirectory->Flags() & VFS_MOUNT_KEEP_DELETED))
return;
}
directory->AddFile(file);
}
Status AddFiles(const CFileInfos& files) const
{
const OsPath path(m_realDirectory->Path());
for(size_t i = 0; i < files.size(); i++)
{
const OsPath pathname = path / files[i].Name();
if(pathname.Extension() == L".zip")
{
PIArchiveReader archiveReader = CreateArchiveReader_Zip(pathname);
// archiveReader == nullptr if file could not be opened (e.g. because
// archive is currently open in another program)
if(archiveReader)
RETURN_STATUS_IF_ERR(archiveReader->ReadEntries(AddArchiveFile, (uintptr_t)this));
}
else // regular (non-archive) file
AddFile(files[i]);
}
return INFO::OK;
}
void AddSubdirectories(const DirectoryNames& subdirectoryNames) const
{
for(size_t i = 0; i < subdirectoryNames.size(); i++)
{
// skip version control directories - this avoids cluttering the
// VFS with hundreds of irrelevant files.
if(subdirectoryNames[i] == L".svn" || subdirectoryNames[i] == L".git")
continue;
VfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]);
PRealDirectory realDirectory = CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i]);
vfs_Attach(subdirectory, realDirectory);
}
}
VfsDirectory* const m_directory;
PRealDirectory m_realDirectory;
};
Status vfs_Populate(VfsDirectory* directory)
{
if(!directory->ShouldPopulate())
return INFO::OK;
const PRealDirectory& realDirectory = directory->AssociatedDirectory();
if(realDirectory->Flags() & VFS_MOUNT_WATCH)
realDirectory->Watch();
PopulateHelper helper(directory, realDirectory);
RETURN_STATUS_IF_ERR(helper.AddEntries());
return INFO::OK;
}
Status vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory)
{
PRealDirectory existingRealDir = directory->AssociatedDirectory();
// Don't allow replacing the real directory by a lower-priority one.
if (!existingRealDir || existingRealDir->Priority() < realDirectory->Priority())
{
// This ordering is peculiar but useful, as it "defers" the population call.
// If there is already a real directory, we will replace it (and lose track of it),
// so we'll populate it right away, but the 'new' real directory can wait until we access it.
RETURN_STATUS_IF_ERR(vfs_Populate(directory));
directory->SetAssociatedDirectory(realDirectory);
return INFO::OK;
}
// We are attaching a lower-priority real directory.
// Because of deferred population, we need to immediately populate this new directory.
bool shouldPop = directory->ShouldPopulate();
// This sets "should populate" to true, so the vfs_Populate call below immediately populates.
directory->SetAssociatedDirectory(realDirectory);
RETURN_STATUS_IF_ERR(vfs_Populate(directory));
// Reset to the higher priority realDirectory, which resets ShouldPopulate to true.
directory->SetAssociatedDirectory(existingRealDir);
// Avoid un-necessary repopulation by clearing the flag.
if (!shouldPop)
directory->ShouldPopulate();
return INFO::OK;
}