1
0
forked from 0ad/0ad
0ad/source/lib/file/file_system_util.cpp
janwas c0ed950657 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 18:48:32 +00:00

143 lines
4.2 KiB
C++

/**
* =========================================================================
* File : file_system_util.cpp
* Project : 0 A.D.
* Description : helper functions for directory access
* =========================================================================
*/
// license: GPL; see lib/license.txt
#include "precompiled.h"
#include "file_system_util.h"
#include <queue>
#include <cstring>
#include "lib/path_util.h"
#include "lib/regex.h"
LibError fs_GetPathnames(PIVFS fs, const VfsPath& path, const char* filter, VfsPaths& pathnames)
{
std::vector<FileInfo> files;
RETURN_ERR(fs->GetDirectoryEntries(path, &files, 0));
pathnames.clear();
pathnames.reserve(files.size());
for(size_t i = 0; i < files.size(); i++)
{
if(match_wildcard(files[i].Name().c_str(), filter))
pathnames.push_back(path/files[i].Name());
}
return INFO::OK;
}
struct FileInfoNameLess : public std::binary_function<const FileInfo, const FileInfo, bool>
{
bool operator()(const FileInfo& fileInfo1, const FileInfo& fileInfo2) const
{
return strcasecmp(fileInfo1.Name().c_str(), fileInfo2.Name().c_str()) < 0;
}
};
void fs_SortFiles(FileInfos& files)
{
std::sort(files.begin(), files.end(), FileInfoNameLess());
}
struct NameLess : public std::binary_function<const std::string, const std::string, bool>
{
bool operator()(const std::string& name1, const std::string& name2) const
{
return strcasecmp(name1.c_str(), name2.c_str()) < 0;
}
};
void fs_SortDirectories(DirectoryNames& directories)
{
std::sort(directories.begin(), directories.end(), NameLess());
}
LibError fs_ForEachFile(PIVFS fs, const VfsPath& path, FileCallback cb, uintptr_t cbData, const char* pattern, int flags)
{
debug_assert(vfs_path_IsDirectory(path));
// (declare here to avoid reallocations)
FileInfos files; DirectoryNames subdirectoryNames;
// (a FIFO queue is more efficient than recursion because it uses less
// stack space and avoids seeks due to breadth-first traversal.)
std::queue<VfsPath> pendingDirectories;
pendingDirectories.push(path);
while(!pendingDirectories.empty())
{
const VfsPath& path = pendingDirectories.front();
RETURN_ERR(fs->GetDirectoryEntries(path/"/", &files, &subdirectoryNames));
for(size_t i = 0; i < files.size(); i++)
{
const FileInfo fileInfo = files[i];
if(!match_wildcard(fileInfo.Name().c_str(), pattern))
continue;
const VfsPath pathname(path/fileInfo.Name()); // (FileInfo only stores the name)
cb(pathname, fileInfo, cbData);
}
if(!(flags & DIR_RECURSIVE))
break;
for(size_t i = 0; i < subdirectoryNames.size(); i++)
pendingDirectories.push(path/subdirectoryNames[i]);
pendingDirectories.pop();
}
return INFO::OK;
}
void fs_NextNumberedFilename(PIVFS fs, const VfsPath& pathnameFormat, size_t& nextNumber, VfsPath& nextPathname)
{
// (first call only:) scan directory and set nextNumber according to
// highest matching filename found. this avoids filling "holes" in
// the number series due to deleted files, which could be confusing.
// example: add 1st and 2nd; [exit] delete 1st; [restart]
// add 3rd -> without this measure it would get number 1, not 3.
if(nextNumber == 0)
{
const std::string nameFormat = pathnameFormat.leaf();
const VfsPath path = pathnameFormat.branch_path()/"/";
size_t maxNumber = 0;
FileInfos files;
fs->GetDirectoryEntries(path, &files, 0);
for(size_t i = 0; i < files.size(); i++)
{
size_t number;
if(sscanf(files[i].Name().c_str(), nameFormat.c_str(), &number) == 1)
maxNumber = std::max(number, maxNumber);
}
nextNumber = maxNumber+1;
}
// now increment number until that file doesn't yet exist.
// this is fairly slow, but typically only happens once due
// to scan loop above. (we still need to provide for looping since
// someone may have added files in the meantime)
// we don't bother with binary search - this isn't a bottleneck.
do
{
char pathnameBuf[PATH_MAX];
snprintf(pathnameBuf, PATH_MAX, pathnameFormat.string().c_str(), nextNumber++);
nextPathname = VfsPath(pathnameBuf);
}
while(fs->GetFileInfo(nextPathname, 0) == INFO::OK);
}