Implemented fixed ordering in DirectoryIterator

This should alleviate loading order differences from different
OSes somewhat. Since the iterator currently employs a lexicographic
ordering, there are probably still problems when one player has
their packages unpacked, while another one has theirs packed.

This should go away once we employ a sane format for game packages.
stable-5.2
Nicolas Hake 2010-01-25 16:54:38 +01:00
parent b8ba66ed5b
commit be39732a8f
3 changed files with 141 additions and 124 deletions

View File

@ -31,12 +31,12 @@
#include <C4InputValidation.h>
#include <C4Config.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <sys/utime.h>
#include <shellapi.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>
#include <fcntl.h>
#include <unistd.h>
@ -570,23 +570,10 @@ C4GroupEntry::~C4GroupEntry()
delete [] bpMemBuf;
}
}
#ifdef _WIN32
void C4GroupEntry::Set(const DirectoryIterator &iter, const char *szPath)
{
ZeroMem(this,sizeof(C4GroupEntry));
SCopy(iter.fdt.name,FileName,_MAX_FNAME);
Size=iter.fdt.size;
Time=iter.fdt.time_create;
//SCopy(szPath,DiskPath,_MAX_PATH-1); AppendBackslash(DiskPath); SAppend(FileName,DiskPath,_MAX_PATH);
SCopy(*iter, DiskPath, _MAX_PATH-1);
Status=C4GRES_OnDisk;
Packed=false;
ChildGroup=false; //FileGroupCheck(DiskPath);
// Notice folder entries are not checked for ChildGroup status.
// This would cause extreme performance loss and be good for
// use in entry list display only.
}
#else
#ifdef WIN32
#define stat _stat
#endif
void C4GroupEntry::Set(const DirectoryIterator &iter, const char * path)
{
ZeroMem(this,sizeof(C4GroupEntry));
@ -608,7 +595,7 @@ void C4GroupEntry::Set(const DirectoryIterator &iter, const char * path)
// This would cause extreme performance loss and be good for
// use in entry list display only.
}
#endif
C4Group::C4Group()
{
Init();

View File

@ -811,112 +811,141 @@ bool ItemIdentical(const char *szFilename1, const char *szFilename2)
//------------------------- Multi File Processing --------------------------------------------------------------------------------------------------------
#ifdef _WIN32
struct DirectoryIteratorP
{
DirectoryIteratorP() : ref(1) {}
DirectoryIterator::FileList files;
std::string directory;
int ref;
};
DirectoryIterator::DirectoryIterator (): fdthnd(0) { *filename=0; }
DirectoryIterator::DirectoryIterator (const DirectoryIterator &): fdthnd(0) { *filename=0; }
void DirectoryIterator::Reset () {
if (fdthnd) {
_findclose(fdthnd);
fdthnd = 0;
}
filename[0] = 0;
DirectoryIterator::DirectoryIterator()
: p(new DirectoryIteratorP), iter(p->files.end())
{}
DirectoryIterator::DirectoryIterator(const DirectoryIterator &other)
: p(other.p), iter(p->files.begin())
{
++p->ref;
}
DirectoryIterator::~DirectoryIterator()
{
if (--p->ref == 0)
delete p;
}
void DirectoryIterator::Reset (const char * dirname) {
if (fdthnd) {
_findclose(fdthnd);
void DirectoryIterator::Reset ()
{
iter = p->files.begin();
}
void DirectoryIterator::Reset (const char * dirname)
{
if (p->directory == dirname)
{
// Skip reinitialisation and just reset the iterator
iter = p->files.begin();
return;
}
if (!dirname[0]) dirname = ".";
SCopy(dirname,filename);
AppendBackslash(filename);
SAppend("*",filename,_MAX_PATH);
if ((fdthnd = _findfirst(filename, &fdt)) < 0) {
filename[0] = 0;
} else {
if (fdt.name[0] == '.' && (fdt.name[1] == '.' || fdt.name[1] == 0)) {
operator++(); return;
if (p->ref > 1)
{
// Detach from shared memory
--p->ref;
p = new DirectoryIteratorP;
}
p->files.clear();
iter = p->files.end();
Read(dirname);
}
DirectoryIterator::DirectoryIterator (const char * dirname)
: p(new DirectoryIteratorP), iter(p->files.end())
{
Read(dirname);
}
void DirectoryIterator::Read(const char *dirname)
{
assert(dirname && *dirname);
assert(p->files.empty());
std::string search_path(dirname);
search_path.push_back(DirectorySeparator);
#ifdef WIN32
WIN32_FIND_DATA file = {0};
HANDLE fh = FindFirstFile((search_path + '*').c_str(), &file);
if (fh == INVALID_HANDLE_VALUE)
{
switch (GetLastError())
{
case ERROR_FILE_NOT_FOUND:
// This is okay, either the directory doesn't exist or there are no files
return;
default:
// Something else broke
throw std::runtime_error("DirectoryIterator::Read(const char*): Unable to read file system");
}
SCopy(fdt.name,GetFilename(filename));
}
}
DirectoryIterator::DirectoryIterator (const char * dirname) {
if (!dirname[0]) dirname = ".";
SCopy(dirname,filename);
AppendBackslash(filename);
SAppend("*",filename,_MAX_PATH);
if ((fdthnd = _findfirst(filename, &fdt)) < 0) {
filename[0] = 0;
} else {
if (fdt.name[0] == '.' && (fdt.name[1] == '.' || fdt.name[1] == 0)) {
operator++(); return;
}
SCopy(fdt.name,GetFilename(filename));
}
}
DirectoryIterator& DirectoryIterator::operator++() {
if (_findnext(fdthnd,&fdt)==0) {
if (fdt.name[0] == '.' && (fdt.name[1] == '.' || fdt.name[1] == 0))
return operator++();
SCopy(fdt.name,GetFilename(filename));
} else {
filename[0] = 0;
}
return *this;
}
DirectoryIterator::~DirectoryIterator () {
if (fdthnd) _findclose(fdthnd);
}
// Insert files into list
do
{
// ...unless they're . or ..
if (file.cFileName[0] == '.' && (file.cFileName[1] == '\0' || (file.cFileName[1] == '.' && file.cFileName[2] == '\0')))
continue;
p->files.push_back(file.cFileName);
} while (FindNextFile(fh, &file));
FindClose(fh);
#else
DirectoryIterator::DirectoryIterator (): d(0) { filename[0] = 0; }
DirectoryIterator::DirectoryIterator (const DirectoryIterator &): d(0) { filename[0] = 0; }
void DirectoryIterator::Reset () {
if (d) {
closedir(d);
d = 0;
DIR *fh = opendir(dirname);
if (fh == NULL)
{
switch (errno)
{
case ENOENT:
case ENOTDIR:
// Okay, so there's no files here.
return;
default:
// Something else broke
throw std::runtime_error("DirectoryIterator::Read(const char*): Unable to read file system");
}
}
filename[0] = 0;
}
void DirectoryIterator::Reset (const char * dirname) {
if (d) {
closedir(d);
dirent *file;
// Insert files into list
while ((file = readdir(fh)) != NULL)
{
// ...unless they're . or ..
if (file->d_name[0] == '.' && (file->d_name[1] == '\0' || (file->d_name[1] == '.' && file->d_name[2] == '\0')))
continue;
p->files.push_back(file.cFileName);
}
if (!dirname[0]) dirname = ".";
SCopy(dirname,filename);
AppendBackslash(filename);
d = opendir(dirname);
this->operator++();
closedir(fh);
#endif
// Sort list
std::sort(p->files.begin(), p->files.end());
for (FileList::iterator it = p->files.begin(); it != p->files.end(); ++it)
it->insert(0, search_path); // prepend path to all file entries
iter = p->files.begin();
p->directory = dirname;
}
DirectoryIterator::DirectoryIterator (const char * dirname) {
if (!dirname[0]) dirname = ".";
SCopy(dirname,filename);
AppendBackslash(filename);
d = opendir(dirname);
this->operator++();
}
DirectoryIterator& DirectoryIterator::operator++() {
if (d && (ent = readdir(d))) {
if (ent->d_name[0] == '.' && (ent->d_name[1] == '.' || ent->d_name[1] == 0))
return operator++();
SCopy(ent->d_name,GetFilename(filename));
} else {
filename[0] = 0;
}
DirectoryIterator& DirectoryIterator::operator++()
{
if (iter != p->files.end())
++iter;
return *this;
}
DirectoryIterator::~DirectoryIterator () {
if (d) closedir(d);
const char * DirectoryIterator::operator*() const
{
if (iter == p->files.end())
return NULL;
return iter->c_str();
}
DirectoryIterator DirectoryIterator::operator++(int)
{
DirectoryIterator tmp(*this);
++*this;
return tmp;
}
#endif
const char * DirectoryIterator::operator*() const { return filename[0] ? filename : false; }
void DirectoryIterator::operator++(int) { operator++(); }
int ForEachFile(const char *szDirName, bool (*fnCallback)(const char *)) {
if (!szDirName || !fnCallback)

View File

@ -28,6 +28,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>
#ifdef _WIN32
#include <io.h>
@ -118,27 +119,27 @@ bool MoveItem(const char *szSource, const char *szTarget);
//int ForEachFile(const char *szPath, int lAttrib, bool (*fnCallback)(const char *));
int ForEachFile(const char *szDirName, bool (*fnCallback)(const char *));
class DirectoryIterator {
struct DirectoryIteratorP;
class DirectoryIterator
{
// Shallow copyable, ordered directory iterator
public:
DirectoryIterator(const char * dirname);
DirectoryIterator();
~DirectoryIterator();
// Does not actually copy anything, but does prevent misuses from crashing (I hope)
DirectoryIterator(const DirectoryIterator &);
~DirectoryIterator();
const char * operator * () const;
DirectoryIterator& operator ++ ();
void operator ++ (int);
DirectoryIterator operator ++ (int);
void Reset(const char * dirname);
void Reset();
protected:
char filename[_MAX_PATH+1];
#ifdef _WIN32
struct _finddata_t fdt; int fdthnd;
friend class C4GroupEntry;
#else
DIR * d;
dirent * ent;
#endif
private:
void Read(const char *dirname);
friend struct DirectoryIteratorP;
typedef std::vector<std::string> FileList;
DirectoryIteratorP *p;
FileList::iterator iter;
};
#endif // STDFILE_INCLUDED