2007-11-20 10:51:07 -08:00
|
|
|
/**
|
|
|
|
|
* =========================================================================
|
|
|
|
|
* File : vfs_tree.cpp
|
|
|
|
|
* Project : 0 A.D.
|
2007-12-01 10:32:43 -08:00
|
|
|
* Description : 'tree' of VFS directories and files
|
2007-11-20 10:51:07 -08:00
|
|
|
* =========================================================================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// license: GPL; see lib/license.txt
|
|
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
#include "vfs_tree.h"
|
|
|
|
|
|
2007-12-20 12:14:21 -08:00
|
|
|
#include "lib/file/common/file_stats.h"
|
|
|
|
|
#include "lib/sysdep/cpu.h"
|
2007-11-20 10:51:07 -08:00
|
|
|
|
|
|
|
|
|
2007-12-01 10:32:43 -08:00
|
|
|
//-----------------------------------------------------------------------------
|
2007-11-20 10:51:07 -08:00
|
|
|
|
2008-07-17 07:23:51 -07:00
|
|
|
VfsFile::VfsFile(const std::string& name, off_t size, time_t mtime, size_t priority, const PIFileLoader& loader)
|
2007-12-22 10:15:52 -08:00
|
|
|
: m_name(name), m_size(size), m_mtime(mtime), m_priority(priority), m_loader(loader)
|
2007-12-01 10:32:43 -08:00
|
|
|
{
|
|
|
|
|
}
|
2007-11-20 10:51:07 -08:00
|
|
|
|
2007-12-01 10:32:43 -08:00
|
|
|
|
2007-12-22 10:15:52 -08:00
|
|
|
bool VfsFile::IsSupersededBy(const VfsFile& file) const
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2008-05-31 11:50:39 -07:00
|
|
|
// 1) priority (override mods)
|
|
|
|
|
if(file.m_priority < m_priority) // lower priority
|
2007-12-01 10:32:43 -08:00
|
|
|
return false;
|
2007-11-20 10:51:07 -08:00
|
|
|
|
2008-05-31 11:50:39 -07:00
|
|
|
// 2) timestamp
|
|
|
|
|
{
|
|
|
|
|
const double howMuchNewer = difftime(file.MTime(), MTime());
|
|
|
|
|
const double threshold = 2.0; // [seconds]; resolution provided by FAT
|
|
|
|
|
if(howMuchNewer > threshold) // newer timestamp
|
|
|
|
|
return true;
|
|
|
|
|
if(howMuchNewer < threshold) // older timestamp
|
|
|
|
|
return false;
|
|
|
|
|
// else: "equal" (tolerating small differences due to FAT's low
|
|
|
|
|
// mtime resolution)
|
|
|
|
|
}
|
2007-12-01 10:32:43 -08:00
|
|
|
|
2008-05-31 11:50:39 -07:00
|
|
|
// 3) precedence (efficiency of file provider)
|
|
|
|
|
if(file.m_loader->Precedence() < m_loader->Precedence()) // less efficient
|
2007-12-01 10:32:43 -08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
2007-12-01 10:32:43 -08:00
|
|
|
|
|
|
|
|
void VfsFile::GenerateDescription(char* text, size_t maxChars) const
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-01 10:32:43 -08:00
|
|
|
char timestamp[25];
|
2007-12-22 10:15:52 -08:00
|
|
|
const time_t mtime = MTime();
|
2007-12-01 10:32:43 -08:00
|
|
|
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";
|
2007-12-22 10:15:52 -08:00
|
|
|
sprintf_s(text, maxChars, fmt, m_loader->LocationCode(), Size(), timestamp);
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-07-17 07:23:51 -07:00
|
|
|
LibError VfsFile::Load(const shared_ptr<u8>& buf) const
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-22 10:15:52 -08:00
|
|
|
return m_loader->Load(Name(), buf, Size());
|
2007-12-01 10:32:43 -08:00
|
|
|
}
|
2007-11-20 10:51:07 -08:00
|
|
|
|
|
|
|
|
|
2007-12-01 10:32:43 -08:00
|
|
|
//-----------------------------------------------------------------------------
|
2007-11-20 10:51:07 -08:00
|
|
|
|
2007-12-01 10:32:43 -08:00
|
|
|
VfsDirectory::VfsDirectory()
|
2007-12-20 12:14:21 -08:00
|
|
|
: m_shouldPopulate(0)
|
2007-12-01 10:32:43 -08:00
|
|
|
{
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-12-22 10:15:52 -08:00
|
|
|
VfsFile* VfsDirectory::AddFile(const VfsFile& file)
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-22 10:15:52 -08:00
|
|
|
std::pair<std::string, VfsFile> value = std::make_pair(file.Name(), file);
|
|
|
|
|
std::pair<VfsFiles::iterator, bool> ret = m_files.insert(value);
|
2007-11-20 10:51:07 -08:00
|
|
|
if(!ret.second) // already existed
|
|
|
|
|
{
|
2007-12-22 10:15:52 -08:00
|
|
|
VfsFile& previousFile = ret.first->second;
|
|
|
|
|
const VfsFile& newFile = value.second;
|
|
|
|
|
if(previousFile.IsSupersededBy(newFile))
|
|
|
|
|
previousFile = newFile;
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
2007-12-01 10:32:43 -08:00
|
|
|
else
|
2007-12-22 10:15:52 -08:00
|
|
|
stats_vfs_file_add(file.Size());
|
2007-11-20 10:51:07 -08:00
|
|
|
|
2007-12-01 10:32:43 -08:00
|
|
|
return &(*ret.first).second;
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-12-01 10:32:43 -08:00
|
|
|
// rationale: passing in a pre-constructed VfsDirectory and copying that into
|
2007-12-20 12:14:21 -08:00
|
|
|
// our map would be slower and less convenient for the caller.
|
|
|
|
|
VfsDirectory* VfsDirectory::AddSubdirectory(const std::string& name)
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-20 12:14:21 -08:00
|
|
|
std::pair<std::string, VfsDirectory> value = std::make_pair(name, VfsDirectory());
|
2007-12-22 10:15:52 -08:00
|
|
|
std::pair<VfsSubdirectories::iterator, bool> ret = m_subdirectories.insert(value);
|
2007-12-01 10:32:43 -08:00
|
|
|
return &(*ret.first).second;
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-12-20 12:14:21 -08:00
|
|
|
VfsFile* VfsDirectory::GetFile(const std::string& name)
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-22 10:15:52 -08:00
|
|
|
VfsFiles::iterator it = m_files.find(name);
|
|
|
|
|
if(it == m_files.end())
|
2007-11-20 10:51:07 -08:00
|
|
|
return 0;
|
|
|
|
|
return &it->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-12-20 12:14:21 -08:00
|
|
|
VfsDirectory* VfsDirectory::GetSubdirectory(const std::string& name)
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-22 10:15:52 -08:00
|
|
|
VfsSubdirectories::iterator it = m_subdirectories.find(name);
|
|
|
|
|
if(it == m_subdirectories.end())
|
2007-11-20 10:51:07 -08:00
|
|
|
return 0;
|
|
|
|
|
return &it->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-12-20 12:14:21 -08:00
|
|
|
void VfsDirectory::GetEntries(FileInfos* files, DirectoryNames* subdirectoryNames) const
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
|
|
|
|
if(files)
|
|
|
|
|
{
|
2007-12-22 10:15:52 -08:00
|
|
|
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()));
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
2007-12-20 12:14:21 -08:00
|
|
|
if(subdirectoryNames)
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-20 12:14:21 -08:00
|
|
|
subdirectoryNames->clear();
|
2007-12-22 10:15:52 -08:00
|
|
|
subdirectoryNames->reserve(m_subdirectories.size());
|
|
|
|
|
for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
2007-12-20 12:14:21 -08:00
|
|
|
subdirectoryNames->push_back(it->first);
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
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.
2008-05-11 11:48:32 -07:00
|
|
|
void VfsDirectory::DisplayR(size_t depth) const
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-20 12:14:21 -08:00
|
|
|
static const char indent[] = " ";
|
2007-11-20 10:51:07 -08:00
|
|
|
|
2008-05-31 11:50:39 -07:00
|
|
|
const size_t maxNameChars = 80 - depth*(sizeof(indent)-1);
|
2007-12-01 10:32:43 -08:00
|
|
|
char fmt[20];
|
2007-12-20 12:14:21 -08:00
|
|
|
sprintf_s(fmt, ARRAY_SIZE(fmt), "%%-%d.%ds %%s", maxNameChars, maxNameChars);
|
2007-11-20 10:51:07 -08:00
|
|
|
|
2007-12-22 10:15:52 -08:00
|
|
|
for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it)
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-20 12:14:21 -08:00
|
|
|
const std::string& name = it->first;
|
2007-12-22 10:15:52 -08:00
|
|
|
const VfsFile& file = it->second;
|
2007-11-20 10:51:07 -08:00
|
|
|
|
2007-12-01 10:32:43 -08:00
|
|
|
char description[100];
|
2007-12-22 10:15:52 -08:00
|
|
|
file.GenerateDescription(description, ARRAY_SIZE(description));
|
2007-11-20 10:51:07 -08:00
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
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.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t i = 0; i < depth+1; i++)
|
2007-11-20 10:51:07 -08:00
|
|
|
printf(indent);
|
2007-12-20 12:14:21 -08:00
|
|
|
printf(fmt, name.c_str(), description);
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
2007-12-22 10:15:52 -08:00
|
|
|
for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-20 12:14:21 -08:00
|
|
|
const std::string& name = it->first;
|
2007-12-22 10:15:52 -08:00
|
|
|
const VfsDirectory& directory = it->second;
|
2007-11-20 10:51:07 -08:00
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
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.
2008-05-11 11:48:32 -07:00
|
|
|
for(size_t i = 0; i < depth+1; i++)
|
2007-11-20 10:51:07 -08:00
|
|
|
printf(indent);
|
2007-12-20 12:14:21 -08:00
|
|
|
printf("[%s/]\n", name.c_str());
|
2007-12-01 10:32:43 -08:00
|
|
|
|
2007-12-22 10:15:52 -08:00
|
|
|
directory.DisplayR(depth+1);
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void VfsDirectory::ClearR()
|
|
|
|
|
{
|
2007-12-22 10:15:52 -08:00
|
|
|
for(VfsSubdirectories::iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it)
|
2007-11-20 10:51:07 -08:00
|
|
|
it->second.ClearR();
|
|
|
|
|
|
2007-12-22 10:15:52 -08:00
|
|
|
m_files.clear();
|
|
|
|
|
m_subdirectories.clear();
|
2007-12-20 12:14:21 -08:00
|
|
|
m_realDirectory.reset();
|
|
|
|
|
m_shouldPopulate = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-07-12 03:45:11 -07:00
|
|
|
void VfsDirectory::Attach(const PRealDirectory& realDirectory)
|
2007-12-20 12:14:21 -08:00
|
|
|
{
|
|
|
|
|
if(!cpu_CAS(&m_shouldPopulate, 0, 1))
|
|
|
|
|
{
|
|
|
|
|
debug_assert(0); // multiple Attach() calls without an intervening ShouldPopulate()
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_realDirectory = realDirectory;
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-12-20 12:14:21 -08:00
|
|
|
bool VfsDirectory::ShouldPopulate()
|
2007-11-20 10:51:07 -08:00
|
|
|
{
|
2007-12-20 12:14:21 -08:00
|
|
|
return cpu_CAS(&m_shouldPopulate, 1, 0); // test and reset
|
2007-11-20 10:51:07 -08:00
|
|
|
}
|