mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-07-13 11:15:55 +00:00
c0ed950657
this snowballed into a massive search+destroy of the hodgepodge of mostly equivalent types we had in use (int, uint, unsigned, unsigned int, i32, u32, ulong, uintN). it is more efficient to use 64-bit types in 64-bit mode, so the preferred default is size_t (for anything remotely resembling a size or index). tile coordinates are ssize_t to allow more efficient conversion to/from floating point. flags are int because we almost never need more than 15 distinct bits, bit test/set is not slower and int is fastest to type. finally, some data that is pretty much directly passed to OpenGL is now typed accordingly. after several hours, the code now requires fewer casts and less guesswork. other changes: - unit and player IDs now have an "invalid id" constant in the respective class to avoid casting and -1 - fix some endian/64-bit bugs in the map (un)packing. added a convenience function to write/read a size_t. - ia32: change CPUID interface to allow passing in ecx (required for cache topology detection, which I need at work). remove some unneeded functions from asm, replace with intrinsics where possible. This was SVN commit r5942.
201 lines
5.2 KiB
C++
201 lines
5.2 KiB
C++
/**
|
|
* =========================================================================
|
|
* File : vfs_tree.cpp
|
|
* Project : 0 A.D.
|
|
* Description : 'tree' of VFS directories and files
|
|
* =========================================================================
|
|
*/
|
|
|
|
// license: GPL; see lib/license.txt
|
|
|
|
#include "precompiled.h"
|
|
#include "vfs_tree.h"
|
|
|
|
#include "lib/file/common/file_stats.h"
|
|
#include "lib/sysdep/cpu.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
VfsFile::VfsFile(const std::string& name, off_t size, time_t mtime, size_t priority, PIFileLoader loader)
|
|
: m_name(name), m_size(size), m_mtime(mtime), m_priority(priority), m_loader(loader)
|
|
{
|
|
}
|
|
|
|
|
|
bool VfsFile::IsSupersededBy(const VfsFile& file) const
|
|
{
|
|
// 1) priority is lower => no.
|
|
if(file.m_priority < m_priority)
|
|
return false;
|
|
|
|
// 2) timestamp is older => no.
|
|
// (note: we need to account for FAT's 2 sec. resolution)
|
|
if(difftime(file.MTime(), MTime()) < -2.0)
|
|
return false;
|
|
|
|
// 3) provider is less efficient => no.
|
|
if(file.m_loader->Precedence() < m_loader->Precedence())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void VfsFile::GenerateDescription(char* text, size_t maxChars) const
|
|
{
|
|
char timestamp[25];
|
|
const time_t mtime = MTime();
|
|
strftime(timestamp, ARRAY_SIZE(timestamp), "%a %b %d %H:%M:%S %Y", localtime(&mtime));
|
|
|
|
// build format string (set width of name field so that everything
|
|
// lines up correctly)
|
|
const char* fmt = "(%c; %6d; %s)\n";
|
|
sprintf_s(text, maxChars, fmt, m_loader->LocationCode(), Size(), timestamp);
|
|
}
|
|
|
|
|
|
LibError VfsFile::Load(shared_ptr<u8> buf) const
|
|
{
|
|
return m_loader->Load(Name(), buf, Size());
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
VfsDirectory::VfsDirectory()
|
|
: m_shouldPopulate(0)
|
|
{
|
|
}
|
|
|
|
|
|
VfsFile* VfsDirectory::AddFile(const VfsFile& file)
|
|
{
|
|
std::pair<std::string, VfsFile> value = std::make_pair(file.Name(), file);
|
|
std::pair<VfsFiles::iterator, bool> ret = m_files.insert(value);
|
|
if(!ret.second) // already existed
|
|
{
|
|
VfsFile& previousFile = ret.first->second;
|
|
const VfsFile& newFile = value.second;
|
|
if(previousFile.IsSupersededBy(newFile))
|
|
previousFile = newFile;
|
|
}
|
|
else
|
|
stats_vfs_file_add(file.Size());
|
|
|
|
return &(*ret.first).second;
|
|
}
|
|
|
|
|
|
// rationale: passing in a pre-constructed VfsDirectory and copying that into
|
|
// our map would be slower and less convenient for the caller.
|
|
VfsDirectory* VfsDirectory::AddSubdirectory(const std::string& name)
|
|
{
|
|
std::pair<std::string, VfsDirectory> value = std::make_pair(name, VfsDirectory());
|
|
std::pair<VfsSubdirectories::iterator, bool> ret = m_subdirectories.insert(value);
|
|
return &(*ret.first).second;
|
|
}
|
|
|
|
|
|
VfsFile* VfsDirectory::GetFile(const std::string& name)
|
|
{
|
|
VfsFiles::iterator it = m_files.find(name);
|
|
if(it == m_files.end())
|
|
return 0;
|
|
return &it->second;
|
|
}
|
|
|
|
|
|
VfsDirectory* VfsDirectory::GetSubdirectory(const std::string& name)
|
|
{
|
|
VfsSubdirectories::iterator it = m_subdirectories.find(name);
|
|
if(it == m_subdirectories.end())
|
|
return 0;
|
|
return &it->second;
|
|
}
|
|
|
|
|
|
void VfsDirectory::GetEntries(FileInfos* files, DirectoryNames* subdirectoryNames) const
|
|
{
|
|
if(files)
|
|
{
|
|
files->clear();
|
|
files->reserve(m_files.size());
|
|
for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it)
|
|
files->push_back(FileInfo(it->second.Name(), it->second.Size(), it->second.MTime()));
|
|
}
|
|
|
|
if(subdirectoryNames)
|
|
{
|
|
subdirectoryNames->clear();
|
|
subdirectoryNames->reserve(m_subdirectories.size());
|
|
for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
|
subdirectoryNames->push_back(it->first);
|
|
}
|
|
}
|
|
|
|
|
|
void VfsDirectory::DisplayR(size_t depth) const
|
|
{
|
|
static const char indent[] = " ";
|
|
|
|
const int maxNameChars = 80 - depth*(sizeof(indent)-1);
|
|
char fmt[20];
|
|
sprintf_s(fmt, ARRAY_SIZE(fmt), "%%-%d.%ds %%s", maxNameChars, maxNameChars);
|
|
|
|
for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it)
|
|
{
|
|
const std::string& name = it->first;
|
|
const VfsFile& file = it->second;
|
|
|
|
char description[100];
|
|
file.GenerateDescription(description, ARRAY_SIZE(description));
|
|
|
|
for(size_t i = 0; i < depth+1; i++)
|
|
printf(indent);
|
|
printf(fmt, name.c_str(), description);
|
|
}
|
|
|
|
for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
|
{
|
|
const std::string& name = it->first;
|
|
const VfsDirectory& directory = it->second;
|
|
|
|
for(size_t i = 0; i < depth+1; i++)
|
|
printf(indent);
|
|
printf("[%s/]\n", name.c_str());
|
|
|
|
directory.DisplayR(depth+1);
|
|
}
|
|
}
|
|
|
|
|
|
void VfsDirectory::ClearR()
|
|
{
|
|
for(VfsSubdirectories::iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
|
it->second.ClearR();
|
|
|
|
m_files.clear();
|
|
m_subdirectories.clear();
|
|
m_realDirectory.reset();
|
|
m_shouldPopulate = 0;
|
|
}
|
|
|
|
|
|
void VfsDirectory::Attach(PRealDirectory realDirectory)
|
|
{
|
|
if(!cpu_CAS(&m_shouldPopulate, 0, 1))
|
|
{
|
|
debug_assert(0); // multiple Attach() calls without an intervening ShouldPopulate()
|
|
return;
|
|
}
|
|
|
|
m_realDirectory = realDirectory;
|
|
}
|
|
|
|
|
|
bool VfsDirectory::ShouldPopulate()
|
|
{
|
|
return cpu_CAS(&m_shouldPopulate, 1, 0); // test and reset
|
|
}
|