Moved Filesystem API to the latest code from ScummVM. In the process,

large directory loads become *much* faster, and leaked memory drops from
259KB to 5KB!  Now that's what I call a performance increase :)

Still TODO is port OSX and Win32 to the new API, and fix some final
memory leaks in the debugger YACC parser (I suspect this accounts for
much of the remaining leaks).


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1609 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2009-01-11 19:10:40 +00:00
parent 85cc67bdc2
commit 4f8a9efa3b
12 changed files with 808 additions and 345 deletions

View File

@ -0,0 +1,220 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2009 by Bradford W. Mott and the Stella team
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SharedPtr.hxx,v 1.1 2009-01-11 19:10:40 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
//============================================================================
#ifndef SHARED_PTR_HXX
#define SHARED_PTR_HXX
#include <cassert>
namespace Common {
class SharedPtrDeletionInternal
{
public:
virtual ~SharedPtrDeletionInternal() {}
};
template<class T>
class SharedPtrDeletionImpl : public SharedPtrDeletionInternal
{
public:
SharedPtrDeletionImpl(T *ptr) : _ptr(ptr) {}
~SharedPtrDeletionImpl()
{
// Checks if the supplied type is not just a plain
// forward definition, taken from boost::checked_delete
// This makes the user really aware what he tries to do
// when using this with an incomplete type.
typedef char completeCheck[sizeof(T) ? 1 : -1];
(void)sizeof(completeCheck);
delete _ptr;
}
private:
T *_ptr;
};
template<class T, class D>
class SharedPtrDeletionDeleterImpl : public SharedPtrDeletionInternal
{
public:
SharedPtrDeletionDeleterImpl(T *ptr, D d) : _ptr(ptr), _deleter(d) {}
~SharedPtrDeletionDeleterImpl() { _deleter(_ptr); }
private:
T *_ptr;
D _deleter;
};
/**
* A simple shared pointer implementation modelled after boost.
*
* This object keeps track of the assigned pointer and automatically
* frees it when no more SharedPtr references to it exist.
*
* To achieve that the object implements an internal reference counting.
* Thus you should try to avoid using the plain pointer after assigning
* it to a SharedPtr object for the first time. If you still use the
* plain pointer be sure you do not delete it on your own. You may also
* not use the plain pointer to create a new SharedPtr object, since that
* would result in a double deletion of the pointer sooner or later.
*
* Example creation:
* Common::SharedPtr<int> pointer(new int(1));
* would create a pointer to int. Later on usage via *pointer is the same
* as for a normal pointer. If you need to access the plain pointer value
* itself later on use the get method. The class also supplies a operator
* ->, which does the same as the -> operator on a normal pointer.
*
* Be sure you are using new to initialize the pointer you want to manage.
* If you do not use new for allocating, you have to supply a deleter as
* second parameter when creating a SharedPtr object. The deleter has to
* implement operator() which takes the pointer it should free as argument.
*
* Note that you have to specify the type itself not the pointer type as
* template parameter.
*
* When creating a SharedPtr object from a normal pointer you need a real
* definition of the type you want SharedPtr to manage, a simple forward
* definition is not enough.
*
* The class has implicit upcast support, so if you got a class B derived
* from class A, you can assign a pointer to B without any problems to a
* SharedPtr object with template parameter A. The very same applies to
* assignment of a SharedPtr<B> object to a SharedPtr<A> object.
*
* There are also operators != and == to compare two SharedPtr objects
* with compatible pointers. Comparison between a SharedPtr object and
* a plain pointer is only possible via SharedPtr::get.
*/
template<class T>
class SharedPtr
{
#if !((__GNUC__ == 2) && (__GNUC_MINOR__ >= 95))
template<class T2> friend class SharedPtr;
#endif
public:
typedef int RefValue;
typedef T ValueType;
typedef T *Pointer;
SharedPtr() : _refCount(0), _deletion(0), _pointer(0) {}
template<class T2> explicit SharedPtr(T2 *p) : _refCount(new RefValue(1)), _deletion(new SharedPtrDeletionImpl<T2>(p)), _pointer(p) {}
template<class T2, class D> SharedPtr(T2 *p, D d) : _refCount(new RefValue(1)), _deletion(new SharedPtrDeletionDeleterImpl<T2, D>(p, d)), _pointer(p) {}
SharedPtr(const SharedPtr &r) : _refCount(r._refCount), _deletion(r._deletion), _pointer(r._pointer) { if (_refCount) ++(*_refCount); }
template<class T2> SharedPtr(const SharedPtr<T2> &r) : _refCount(r._refCount), _deletion(r._deletion), _pointer(r._pointer) { if (_refCount) ++(*_refCount); }
~SharedPtr() { decRef(); }
SharedPtr &operator =(const SharedPtr &r)
{
if (r._refCount)
++(*r._refCount);
decRef();
_refCount = r._refCount;
_deletion = r._deletion;
_pointer = r._pointer;
return *this;
}
template<class T2>
SharedPtr &operator =(const SharedPtr<T2> &r)
{
if (r._refCount)
++(*r._refCount);
decRef();
_refCount = r._refCount;
_deletion = r._deletion;
_pointer = r._pointer;
return *this;
}
ValueType &operator *() const { assert(_pointer); return *_pointer; }
Pointer operator ->() const { assert(_pointer); return _pointer; }
/**
* Returns the plain pointer value. Be sure you know what you
* do if you are continuing to use that pointer.
*
* @return the pointer the SharedPtr object manages
*/
Pointer get() const { return _pointer; }
/**
* Implicit conversion operator to bool for convenience, to make
* checks like "if (sharedPtr) ..." possible.
*/
operator bool() const { return _pointer != 0; }
/**
* Checks if the SharedPtr object is the only object refering
* to the assigned pointer. This should just be used for
* debugging purposes.
*/
bool unique() const { return refCount() == 1; }
/**
* Returns the number of references to the assigned pointer.
* This should just be used for debugging purposes.
*/
RefValue refCount() const { return _refCount ? *_refCount : 0; }
#if !((__GNUC__ == 2) && (__GNUC_MINOR__ >= 95))
private:
#endif
void decRef()
{
if (_refCount)
{
--(*_refCount);
if (!*_refCount)
{
delete _refCount;
delete _deletion;
_deletion = 0;
_refCount = 0;
_pointer = 0;
}
}
}
RefValue *_refCount;
SharedPtrDeletionInternal *_deletion;
T *_pointer;
};
} // end of namespace Common
template<class T1, class T2>
bool operator ==(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
return l.get() == r.get();
}
template<class T1, class T2>
bool operator !=(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
return l.get() != r.get();
}
#endif

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: mainSDL.cxx,v 1.85 2009-01-04 02:28:12 stephena Exp $ // $Id: mainSDL.cxx,v 1.86 2009-01-11 19:10:40 stephena Exp $
//============================================================================ //============================================================================
#include <SDL.h> #include <SDL.h>
@ -100,6 +100,7 @@ int main(int argc, char* argv[])
// Take care of commandline arguments // Take care of commandline arguments
string romfile = theOSystem->settings().loadCommandLine(argc, argv); string romfile = theOSystem->settings().loadCommandLine(argc, argv);
FilesystemNode romnode(romfile);
// Finally, make sure the settings are valid // Finally, make sure the settings are valid
// We do it once here, so the rest of the program can assume valid settings // We do it once here, so the rest of the program can assume valid settings
@ -120,7 +121,7 @@ int main(int argc, char* argv[])
} }
else if(theOSystem->settings().getBool("rominfo")) else if(theOSystem->settings().getBool("rominfo"))
{ {
if(argc > 1 && FilesystemNode::fileExists(romfile)) if(argc > 1 && romnode.exists())
cout << theOSystem->getROMInfo(romfile); cout << theOSystem->getROMInfo(romfile);
else else
cout << "ERROR: ROM doesn't exist" << endl; cout << "ERROR: ROM doesn't exist" << endl;
@ -153,7 +154,9 @@ int main(int argc, char* argv[])
// the ROM actually exists, use it to create a new console. // the ROM actually exists, use it to create a new console.
// If not, use the built-in ROM launcher. In this case, we enter 'launcher' // If not, use the built-in ROM launcher. In this case, we enter 'launcher'
// mode and let the main event loop take care of opening a new console/ROM. // mode and let the main event loop take care of opening a new console/ROM.
if(argc == 1 || romfile == "" || !FilesystemNode::fileExists(romfile)) FilesystemNode node(romfile);
if(argc == 1 || romfile == "" || !romnode.exists())
{ {
if(!theOSystem->createLauncher()) if(!theOSystem->createLauncher())
{ {

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: EventHandler.cxx,v 1.236 2009-01-05 22:05:35 stephena Exp $ // $Id: EventHandler.cxx,v 1.237 2009-01-11 19:10:40 stephena Exp $
//============================================================================ //============================================================================
#include <sstream> #include <sstream>
@ -114,6 +114,14 @@ EventHandler::EventHandler(OSystem* osystem)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandler::~EventHandler() EventHandler::~EventHandler()
{ {
// Free strings created with strdup
for(uInt32 i = 0; i < kEmulActionListSize; ++i)
if(ourEmulActionList[i].key)
free(ourEmulActionList[i].key);
for(uInt32 i = 0; i < kMenuActionListSize; ++i)
if(ourMenuActionList[i].key)
free(ourMenuActionList[i].key);
delete myEvent; delete myEvent;
#ifdef JOYSTICK_SUPPORT #ifdef JOYSTICK_SUPPORT
@ -1063,8 +1071,7 @@ void EventHandler::setActionMappings(EventMode mode)
for(int i = 0; i < listsize; ++i) for(int i = 0; i < listsize; ++i)
{ {
Event::Type event = list[i].event; Event::Type event = list[i].event;
if(list[i].key) free(list[i].key); list[i].key = NULL;
free(list[i].key);
list[i].key = strdup("None"); list[i].key = strdup("None");
string key = ""; string key = "";
for(int j = 0; j < SDLK_LAST; ++j) // key mapping for(int j = 0; j < SDLK_LAST; ++j) // key mapping
@ -1170,8 +1177,7 @@ void EventHandler::setActionMappings(EventMode mode)
if(key != "") if(key != "")
{ {
if(list[i].key) free(list[i].key); list[i].key = NULL;
free(list[i].key);
list[i].key = strdup(key.c_str()); list[i].key = strdup(key.c_str());
} }
} }
@ -1776,14 +1782,16 @@ void EventHandler::takeSnapshot()
// Determine if the file already exists, checking each successive filename // Determine if the file already exists, checking each successive filename
// until one doesn't exist // until one doesn't exist
filename = sspath + ".png"; filename = sspath + ".png";
if(FilesystemNode::fileExists(filename)) FilesystemNode node(filename);
if(node.exists())
{ {
ostringstream buf; ostringstream buf;
for(uInt32 i = 1; ;++i) for(uInt32 i = 1; ;++i)
{ {
buf.str(""); buf.str("");
buf << sspath << "_" << i << ".png"; buf << sspath << "_" << i << ".png";
if(!FilesystemNode::fileExists(buf.str())) FilesystemNode node(buf.str());
if(!node.exists())
break; break;
} }
filename = buf.str(); filename = buf.str();

View File

@ -13,107 +13,143 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: FSNode.cxx,v 1.13 2009-01-01 18:13:35 stephena Exp $ // $Id: FSNode.cxx,v 1.14 2009-01-11 19:10:40 stephena Exp $
// //
// Based on code from ScummVM - Scumm Interpreter // Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
//============================================================================ //============================================================================
#include "bspf.hxx" #include "bspf.hxx"
#include "SharedPtr.hxx"
#include "FSNode.hxx" #include "FSNode.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FSList::sort()
{
// Simple selection sort
for (Int32 i = 0; i < _size-1; i++)
{
Int32 min = i;
for (Int32 j = i+1; j < _size; j++)
{
if (_data[j] < _data[min])
min = j;
}
if (min != i)
BSPF_swap(_data[min], _data[i]);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode AbstractFilesystemNode::wrap(AbstractFilesystemNode *node)
{
FilesystemNode wrapper;
wrapper._realNode = node;
return wrapper;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode::FilesystemNode() FilesystemNode::FilesystemNode()
{ {
_realNode = getRoot();
_refCount = new int(1);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode::FilesystemNode(const FilesystemNode &node) FilesystemNode::FilesystemNode(AbstractFilesystemNode *realNode)
: AbstractFilesystemNode() : _realNode(realNode)
{ {
_realNode = node._realNode;
_refCount = node._refCount;
++(*_refCount);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode::FilesystemNode(const string& p) FilesystemNode::FilesystemNode(const string& p)
{ {
_realNode = getNodeForPath(p); AbstractFilesystemNode *tmp = 0;
_refCount = new int(1); if (p.empty() || p == ".")
} tmp = AbstractFilesystemNode::makeCurrentDirectoryFileNode();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode::~FilesystemNode()
{
decRefCount();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FilesystemNode::decRefCount()
{
--(*_refCount);
if (*_refCount <= 0)
{
delete _refCount;
delete _realNode;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode &FilesystemNode::operator =(const FilesystemNode &node)
{
++(*node._refCount);
decRefCount();
_realNode = node._realNode;
_refCount = node._refCount;
return *this;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode FilesystemNode::getParent() const
{
AbstractFilesystemNode *node = _realNode->parent();
if(node == 0)
return *this;
else else
return AbstractFilesystemNode::wrap(node); tmp = AbstractFilesystemNode::makeFileNodePath(p);
_realNode = Common::SharedPtr<AbstractFilesystemNode>(tmp);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::operator<(const FilesystemNode& node) const
{
if (isDirectory() != node.isDirectory())
return isDirectory();
return BSPF_strcasecmp(getDisplayName().c_str(), node.getDisplayName().c_str()) < 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::exists() const
{
if (_realNode == 0)
return false;
return _realNode->exists();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::getChildren(FSList& fslist, ListMode mode, bool hidden) const
{
if (!_realNode || !_realNode->isDirectory())
return false;
AbstractFSList tmp;
if (!_realNode->getChildren(tmp, mode, hidden))
return false;
fslist.clear();
for (AbstractFSList::iterator i = tmp.begin(); i != tmp.end(); ++i)
{
fslist.push_back(FilesystemNode(*i));
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FilesystemNode::getDisplayName() const
{
assert(_realNode);
return _realNode->getDisplayName();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FilesystemNode::getName() const
{
assert(_realNode);
return _realNode->getName();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::hasParent() const bool FilesystemNode::hasParent() const
{ {
return _realNode->parent() != 0; if (_realNode == 0)
return false;
return _realNode->getParent() != 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNode FilesystemNode::getParent() const
{
if (_realNode == 0)
return *this;
AbstractFilesystemNode* node = _realNode->getParent();
if (node == 0)
return *this;
else
return FilesystemNode(node);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string FilesystemNode::getPath() const
{
assert(_realNode);
return _realNode->getPath();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::isDirectory() const
{
if (_realNode == 0)
return false;
return _realNode->isDirectory();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::isReadable() const
{
if (_realNode == 0)
return false;
return _realNode->isReadable();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::isWritable() const
{
if (_realNode == 0)
return false;
return _realNode->isWritable();
} }

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: FSNode.hxx,v 1.16 2009-01-01 18:13:35 stephena Exp $ // $Id: FSNode.hxx,v 1.17 2009-01-11 19:10:40 stephena Exp $
// //
// Based on code from ScummVM - Scumm Interpreter // Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
@ -55,85 +55,279 @@
*/ */
#include "Array.hxx" #include "Array.hxx"
#include "SharedPtr.hxx"
class FilesystemNode; class FilesystemNode;
class AbstractFilesystemNode;
/** /**
* List of multiple file system nodes. E.g. the contents of a given directory. * List of multiple file system nodes. E.g. the contents of a given directory.
* This is subclass instead of just a typedef so that we can use forward
* declarations of it in other places.
*/ */
class FSList : public Common::Array<FilesystemNode> class FSList : public Common::Array<FilesystemNode> { };
/**
* FilesystemNode provides an abstraction for file paths, allowing for portable
* file system browsing. To this ends, multiple or single roots have to be supported
* (compare Unix with a single root, Windows with multiple roots C:, D:, ...).
*
* To this end, we abstract away from paths; implementations can be based on
* paths (and it's left to them whether / or \ or : is the path separator :-);
* but it is also possible to use inodes or vrefs (MacOS 9) or anything else.
*
* NOTE: Backends still have to provide a way to extract a path from a FSIntern
*
* You may ask now: "isn't this cheating? Why do we go through all this when we use
* a path in the end anyway?!?".
* Well, for once as long as we don't provide our own file open/read/write API, we
* still have to use fopen(). Since all our targets already support fopen(), it should
* be possible to get a fopen() compatible string for any file system node.
*
* Secondly, with this abstraction layer, we still avoid a lot of complications based on
* differences in FS roots, different path separators, or even systems with no real
* paths (MacOS 9 doesn't even have the notion of a "current directory").
* And if we ever want to support devices with no FS in the classical sense (Palm...),
* we can build upon this.
*
* This class acts as a wrapper around the AbstractFilesystemNode class defined in backends/fs.
*/
class FilesystemNode
{ {
public: public:
void sort(); /**
* Flag to tell listDir() which kind of files to list.
*/
enum ListMode {
kListFilesOnly = 1,
kListDirectoriesOnly = 2,
kListAll = 3
};
/**
* Create a new pathless FilesystemNode. Since there's no path associated
* with this node, path-related operations (i.e. exists(), isDirectory(),
* getPath()) will always return false or raise an assertion.
*/
FilesystemNode();
/**
* Create a new FilesystemNode referring to the specified path. This is
* the counterpart to the path() method.
*
* If path is empty or equals ".", then a node representing the "current
* directory" will be created. If that is not possible (since e.g. the
* operating system doesn't support the concept), some other directory is
* used (usually the root directory).
*/
explicit FilesystemNode(const string& path);
virtual ~FilesystemNode() {}
/**
* Compare the name of this node to the name of another. Directories
* go before normal files.
*/
bool operator<(const FilesystemNode& node) const;
/**
* Indicates whether the object referred by this path exists in the filesystem or not.
*
* @return bool true if the path exists, false otherwise.
*/
virtual bool exists() const;
/**
* Return a list of child nodes of this directory node. If called on a node
* that does not represent a directory, false is returned.
*
* @return true if successful, false otherwise (e.g. when the directory does not exist).
*/
virtual bool getChildren(FSList &fslist, ListMode mode = kListDirectoriesOnly, bool hidden = false) const;
/**
* Return a human readable string for this node, usable for display (e.g.
* in the GUI code). Do *not* rely on it being usable for anything else,
* like constructing paths!
*
* @return the display name
*/
virtual string getDisplayName() const;
/**
* Return a string representation of the name of the file. This is can be
* used e.g. by detection code that relies on matching the name of a given
* file. But it is *not* suitable for use with fopen / File::open, nor
* should it be archived.
*
* @return the file name
*/
virtual string getName() const;
/**
* Return a string representation of the file which can be passed to fopen(),
* and is suitable for archiving (i.e. writing to the config file).
* This will usually be a 'path' (hence the name of the method), but can
* be anything that fulfills the above criterions.
*
* @note Do not assume that this string contains (back)slashes or any
* other kind of 'path separators'.
*
* @return the 'path' represented by this filesystem node
*/
virtual string getPath() const;
/**
* Determine whether this node has a parent.
*/
bool hasParent() const;
/**
* Get the parent node of this node. If this node has no parent node,
* then it returns a duplicate of this node.
*/
FilesystemNode getParent() const;
/**
* Indicates whether the path refers to a directory or not.
*
* @todo Currently we assume that a node that is not a directory
* automatically is a file (ignoring things like symlinks or pipes).
* That might actually be OK... but we could still add an isFile method.
* Or even replace isDirectory by a getType() method that can return values like
* kDirNodeType, kFileNodeType, kInvalidNodeType.
*/
virtual bool isDirectory() const;
/**
* Indicates whether the object referred by this path can be read from or not.
*
* If the path refers to a directory, readability implies being able to read
* and list the directory entries.
*
* If the path refers to a file, readability implies being able to read the
* contents of the file.
*
* @return bool true if the object can be read, false otherwise.
*/
virtual bool isReadable() const;
/**
* Indicates whether the object referred by this path can be written to or not.
*
* If the path refers to a directory, writability implies being able to modify
* the directory entry (i.e. rename the directory, remove it or write files inside of it).
*
* If the path refers to a file, writability implies being able to write data
* to the file.
*
* @return bool true if the object can be written to, false otherwise.
*/
virtual bool isWritable() const;
private:
Common::SharedPtr<AbstractFilesystemNode> _realNode;
FilesystemNode(AbstractFilesystemNode* realNode);
}; };
/** /**
* File system node. * Abstract file system node. Private subclasses implement the actual
* functionality.
*
* Most of the methods correspond directly to methods in class FSNode,
* so if they are not documented here, look there for more information about
* the semantics.
*/ */
typedef Common::Array<AbstractFilesystemNode *> AbstractFSList;
class AbstractFilesystemNode class AbstractFilesystemNode
{ {
protected:
friend class FilesystemNode;
typedef FilesystemNode::ListMode ListMode;
public: public:
/** /**
Flag to tell listDir() which kind of files to list. * Destructor.
*/ */
typedef enum {
kListFilesOnly = 1,
kListDirectoriesOnly = 2,
kListAll = 3
} ListMode;
virtual ~AbstractFilesystemNode() {} virtual ~AbstractFilesystemNode() {}
/** /*
Return display name, used by e.g. the GUI to present the file in the file browser. * Indicates whether the object referred by this path exists in the filesystem or not.
@return the display name
*/ */
virtual string displayName() const = 0; virtual bool exists() const = 0;
/** /**
Is this node valid (i.e. referring to an actual FS object)? * Return a list of child nodes of this directory node. If called on a node
* that does not represent a directory, false is returned.
*
* @param list List to put the contents of the directory in.
* @param mode Mode to use while listing the directory.
* @param hidden Whether to include hidden files or not in the results.
*
* @return true if succesful, false otherwise (e.g. when the directory does not exist).
*/ */
virtual bool isValid() const = 0; virtual bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const = 0;
/** /**
Is this node a directory or not? * Returns a human readable path string.
*
* @note By default, this method returns the value of getName().
*/
virtual string getDisplayName() const { return getName(); }
/**
* Returns the last component of the path pointed by this FilesystemNode.
*
* Examples (POSIX):
* /foo/bar.txt would return /bar.txt
* /foo/bar/ would return /bar/
*
* @note This method is very architecture dependent, please check the concrete implementation for more information.
*/
virtual string getName() const = 0;
/**
* Returns the 'path' of the current node, usable in fopen().
*/
virtual string getPath() const = 0;
/**
* Indicates whether this path refers to a directory or not.
*/ */
virtual bool isDirectory() const = 0; virtual bool isDirectory() const = 0;
/** /**
A path representation suitable for use with fopen() * Indicates whether the object referred by this path can be read from or not.
*
* If the path refers to a directory, readability implies being able to read
* and list the directory entries.
*
* If the path refers to a file, readability implies being able to read the
* contents of the file.
*
* @return bool true if the object can be read, false otherwise.
*/ */
virtual string path() const = 0; virtual bool isReadable() const = 0;
/** /**
List the content of this directory node. * Indicates whether the object referred by this path can be written to or not.
If this node is not a directory, throw an exception or call error(). *
* If the path refers to a directory, writability implies being able to modify
* the directory entry (i.e. rename the directory, remove it or write files inside of it).
*
* If the path refers to a file, writability implies being able to write data
* to the file.
*
* @return bool true if the object can be written to, false otherwise.
*/ */
virtual FSList listDir(ListMode mode = kListDirectoriesOnly) const = 0; virtual bool isWritable() const = 0;
/** /* TODO:
Compare the name of this node to the name of another. bool isFile();
*/ */
virtual bool operator< (const AbstractFilesystemNode& node) const
{
string first = displayName();
string second = node.displayName();
transform(first.begin(), first.end(), first.begin(), (int(*)(int)) tolower);
transform(second.begin(), second.end(), second.begin(), (int(*)(int)) tolower);
return first < second;
}
/**
Test whether given path exists as a file.
*/
static bool fileExists(const string& path);
/**
Test whether given path exists as a directory.
*/
static bool dirExists(const string& path);
/** /**
Create a directory from the given path. Create a directory from the given path.
@ -145,79 +339,41 @@ class AbstractFilesystemNode
*/ */
static bool renameFile(const string& oldfile, const string& newfile); static bool renameFile(const string& oldfile, const string& newfile);
/* TODO:
bool isReadable();
bool isWriteable();
*/
protected: protected:
friend class FilesystemNode; /**
* The parent node of this directory.
* The parent of the root is the root itself.
*/
virtual AbstractFilesystemNode* getParent() const = 0;
/** /**
The parent node of this directory. * Returns a node representing the "current directory".
The parent of the root is the root itself. * If your system does not support this concept, you can either try to
* emulate it or simply return some "sensible" default directory node,
* e.g. the same value as getRoot() returns.
*/ */
virtual AbstractFilesystemNode *parent() const = 0; static AbstractFilesystemNode* makeCurrentDirectoryFileNode();
/** /**
* This method is a rather ugly hack which is used internally by the
* actual node implementions to wrap up raw nodes inside FilesystemNode
* objects. We probably want to get rid of this eventually and replace it
* with a cleaner / more elegant solution, but for now it works.
* @note This takes over ownership of node. Do not delete it yourself,
* else you'll get ugly crashes. You've been warned!
*/
static FilesystemNode wrap(AbstractFilesystemNode *node);
};
class FilesystemNode : public AbstractFilesystemNode
{
friend class AbstractFilesystemNode;
public:
FilesystemNode();
FilesystemNode(const FilesystemNode& node);
FilesystemNode(const string& path);
~FilesystemNode();
FilesystemNode &operator =(const FilesystemNode &node);
FilesystemNode getParent() const;
bool hasParent() const;
virtual string displayName() const { return _realNode->displayName(); }
virtual bool isValid() const { return _realNode->isValid(); }
virtual bool isDirectory() const { return _realNode->isDirectory(); }
virtual string path() const { return _realNode->path(); }
virtual FSList listDir(ListMode mode = kListDirectoriesOnly) const
{ return _realNode->listDir(mode); }
protected:
void decRefCount();
virtual AbstractFilesystemNode* parent() const { return 0; }
private:
AbstractFilesystemNode *_realNode;
int *_refCount;
/**
* Returns a special node representing the FS root. The starting point for
* any file system browsing.
* On Unix, this will be simply the node for / (the root directory).
* On Windows, it will be a special node which "contains" all drives (C:, D:, E:).
*/
static AbstractFilesystemNode* getRoot();
/*
* Construct a node based on a path; the path is in the same format as it * Construct a node based on a path; the path is in the same format as it
* would be for calls to fopen(). * would be for calls to fopen().
* *
* I.e. getNodeForPath(oldNode.path()) should create a new node identical to oldNode. * Furthermore getNodeForPath(oldNode.path()) should create a new node
* identical to oldNode. Hence, we can use the "path" value for persistent
* storage e.g. in the config file.
*
* @param path The path string to create a FilesystemNode for.
*/ */
static AbstractFilesystemNode* getNodeForPath(const string& path); static AbstractFilesystemNode* makeFileNodePath(const string& path);
/**
* Returns a special node representing the filesystem root.
* The starting point for any file system browsing.
*
* On Unix, this will be simply the node for / (the root directory).
* On Windows, it will be a special node which "contains" all drives (C:, D:, E:).
*/
static AbstractFilesystemNode* makeRootFileNode();
}; };
#endif #endif

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: OSystem.cxx,v 1.144 2009-01-11 15:01:36 stephena Exp $ // $Id: OSystem.cxx,v 1.145 2009-01-11 19:10:40 stephena Exp $
//============================================================================ //============================================================================
#include <cassert> #include <cassert>
@ -270,18 +270,22 @@ bool OSystem::create()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void OSystem::setConfigPaths() void OSystem::setConfigPaths()
{ {
FilesystemNode node;
myStateDir = mySettings->getString("statedir"); myStateDir = mySettings->getString("statedir");
if(myStateDir == "") if(myStateDir == "")
myStateDir = myBaseDir + BSPF_PATH_SEPARATOR + "state"; myStateDir = myBaseDir + BSPF_PATH_SEPARATOR + "state";
if(!FilesystemNode::dirExists(myStateDir)) node = FilesystemNode(myStateDir);
FilesystemNode::makeDir(myStateDir); if(!node.isDirectory())
AbstractFilesystemNode::makeDir(myStateDir);
mySettings->setString("statedir", myStateDir); mySettings->setString("statedir", myStateDir);
mySnapshotDir = mySettings->getString("ssdir"); mySnapshotDir = mySettings->getString("ssdir");
if(mySnapshotDir == "") if(mySnapshotDir == "")
mySnapshotDir = myBaseDir + BSPF_PATH_SEPARATOR + "snapshots"; mySnapshotDir = myBaseDir + BSPF_PATH_SEPARATOR + "snapshots";
if(!FilesystemNode::dirExists(mySnapshotDir)) node = FilesystemNode(mySnapshotDir);
FilesystemNode::makeDir(mySnapshotDir); if(!node.isDirectory())
AbstractFilesystemNode::makeDir(mySnapshotDir);
mySettings->setString("ssdir", mySnapshotDir); mySettings->setString("ssdir", mySnapshotDir);
myCheatFile = mySettings->getString("cheatfile"); myCheatFile = mySettings->getString("cheatfile");
@ -313,8 +317,9 @@ void OSystem::setUIPalette()
void OSystem::setBaseDir(const string& basedir) void OSystem::setBaseDir(const string& basedir)
{ {
myBaseDir = basedir; myBaseDir = basedir;
if(!FilesystemNode::dirExists(myBaseDir)) FilesystemNode node(myBaseDir);
FilesystemNode::makeDir(myBaseDir); if(!node.isDirectory())
;//FIXME SAFilesystemNode::makeDir(myBaseDir);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: bspf.hxx,v 1.20 2008-11-02 16:46:05 stephena Exp $ // $Id: bspf.hxx,v 1.21 2009-01-11 19:10:40 stephena Exp $
//============================================================================ //============================================================================
#ifndef BSPF_HXX #ifndef BSPF_HXX
@ -24,7 +24,7 @@
that need to be defined for different operating systems. that need to be defined for different operating systems.
@author Bradford W. Mott @author Bradford W. Mott
@version $Id: bspf.hxx,v 1.20 2008-11-02 16:46:05 stephena Exp $ @version $Id: bspf.hxx,v 1.21 2009-01-11 19:10:40 stephena Exp $
*/ */
// Types for 8-bit signed and unsigned integers // Types for 8-bit signed and unsigned integers
@ -48,7 +48,7 @@ typedef unsigned int uInt32;
#else #else
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <string> #include <cstring>
using namespace std; using namespace std;
#endif #endif

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: BrowserDialog.cxx,v 1.35 2009-01-04 22:27:43 stephena Exp $ // $Id: BrowserDialog.cxx,v 1.36 2009-01-11 19:10:40 stephena Exp $
// //
// Based on code from ScummVM - Scumm Interpreter // Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
@ -45,7 +45,7 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font)
_fileList(NULL), _fileList(NULL),
_currentPath(NULL), _currentPath(NULL),
_nodeList(NULL), _nodeList(NULL),
_mode(AbstractFilesystemNode::kListDirectoriesOnly) _mode(FilesystemNode::kListDirectoriesOnly)
{ {
const int lineHeight = font.getLineHeight(), const int lineHeight = font.getLineHeight(),
buttonWidth = font.getStringWidth("Defaults") + 20, buttonWidth = font.getStringWidth("Defaults") + 20,
@ -137,7 +137,7 @@ void BrowserDialog::show(const string& title, const string& startpath,
// go back to the root/default dir. // go back to the root/default dir.
_node = FilesystemNode(startpath); _node = FilesystemNode(startpath);
if(!_node.isValid()) if(!_node.exists())
_node = FilesystemNode(); _node = FilesystemNode();
// Generally, we always want a directory listing // Generally, we always want a directory listing
@ -158,27 +158,25 @@ void BrowserDialog::updateListing()
_nodeList->clear(); _nodeList->clear();
// Update the path display // Update the path display
_currentPath->setLabel(_node.path()); _currentPath->setLabel(_node.getPath());
// Read in the data from the file system // Read in the data from the file system
FSList content = _node.listDir(_mode); FSList content;
_node.getChildren(content, _mode);
// Add '[..]' to indicate previous folder // Add '[..]' to indicate previous folder
if(_node.hasParent()) if(_node.hasParent())
{ _nodeList->appendGame(" [..]", _node.getParent().getPath(), "", true);
const string& parent = _node.getParent().path();
_nodeList->appendGame(" [..]", parent, "", true);
}
// Now add the directory entries // Now add the directory entries
for(unsigned int idx = 0; idx < content.size(); idx++) for(unsigned int idx = 0; idx < content.size(); idx++)
{ {
string name = content[idx].displayName(); string name = content[idx].getDisplayName();
bool isDir = content[idx].isDirectory(); bool isDir = content[idx].isDirectory();
if(isDir) if(isDir)
name = " [" + name + "]"; name = " [" + name + "]";
_nodeList->appendGame(name, content[idx].path(), "", isDir); _nodeList->appendGame(name, content[idx].getPath(), "", isDir);
} }
_nodeList->sortByName(); _nodeList->sortByName();
@ -219,7 +217,7 @@ void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
int item = _fileList->getSelected(); int item = _fileList->getSelected();
if(item >= 0) if(item >= 0)
{ {
_node = _nodeList->path(item); _node = FilesystemNode(_nodeList->path(item));
updateListing(); updateListing();
} }
break; break;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: FileSnapDialog.cxx,v 1.25 2009-01-04 22:27:43 stephena Exp $ // $Id: FileSnapDialog.cxx,v 1.26 2009-01-11 19:10:40 stephena Exp $
// //
// Based on code from ScummVM - Scumm Interpreter // Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
@ -253,42 +253,42 @@ void FileSnapDialog::handleCommand(CommandSender* sender, int cmd,
case kRomDirChosenCmd: case kRomDirChosenCmd:
{ {
FilesystemNode dir(myBrowser->getResult()); FilesystemNode dir(myBrowser->getResult());
myRomPath->setEditString(dir.path()); myRomPath->setEditString(dir.getPath());
break; break;
} }
case kStateDirChosenCmd: case kStateDirChosenCmd:
{ {
FilesystemNode dir(myBrowser->getResult()); FilesystemNode dir(myBrowser->getResult());
myStatePath->setEditString(dir.path()); myStatePath->setEditString(dir.getPath());
break; break;
} }
case kCheatFileChosenCmd: case kCheatFileChosenCmd:
{ {
FilesystemNode dir(myBrowser->getResult()); FilesystemNode dir(myBrowser->getResult());
myCheatFile->setEditString(dir.path()); myCheatFile->setEditString(dir.getPath());
break; break;
} }
case kPaletteFileChosenCmd: case kPaletteFileChosenCmd:
{ {
FilesystemNode dir(myBrowser->getResult()); FilesystemNode dir(myBrowser->getResult());
myPaletteFile->setEditString(dir.path()); myPaletteFile->setEditString(dir.getPath());
break; break;
} }
case kPropsFileChosenCmd: case kPropsFileChosenCmd:
{ {
FilesystemNode dir(myBrowser->getResult()); FilesystemNode dir(myBrowser->getResult());
myPropsFile->setEditString(dir.path()); myPropsFile->setEditString(dir.getPath());
break; break;
} }
case kSnapDirChosenCmd: case kSnapDirChosenCmd:
{ {
FilesystemNode dir(myBrowser->getResult()); FilesystemNode dir(myBrowser->getResult());
mySnapPath->setEditString(dir.path()); mySnapPath->setEditString(dir.getPath());
break; break;
} }

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: LauncherDialog.cxx,v 1.99 2009-01-05 20:33:03 stephena Exp $ // $Id: LauncherDialog.cxx,v 1.100 2009-01-11 19:10:40 stephena Exp $
// //
// Based on code from ScummVM - Scumm Interpreter // Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
@ -222,7 +222,7 @@ void LauncherDialog::loadConfig()
if(myList->getList().isEmpty()) if(myList->getList().isEmpty())
{ {
myPrevDirButton->setEnabled(false); myPrevDirButton->setEnabled(false);
myCurrentNode = instance().settings().getString("romdir"); myCurrentNode = FilesystemNode(instance().settings().getString("romdir"));
updateListing(); updateListing();
} }
@ -255,7 +255,7 @@ void LauncherDialog::updateListing()
myPrevDirButton->setEnabled(myCurrentNode.hasParent()); myPrevDirButton->setEnabled(myCurrentNode.hasParent());
// Show current directory // Show current directory
myDir->setLabel(myCurrentNode.path()); myDir->setLabel(myCurrentNode.getPath());
// Now fill the list widget with the contents of the GameList // Now fill the list widget with the contents of the GameList
StringList l; StringList l;
@ -302,7 +302,8 @@ void LauncherDialog::loadDirListing()
if(!myCurrentNode.isDirectory()) if(!myCurrentNode.isDirectory())
return; return;
FSList files = myCurrentNode.listDir(FilesystemNode::kListAll); FSList files;
myCurrentNode.getChildren(files, FilesystemNode::kListAll);
// Add '[..]' to indicate previous folder // Add '[..]' to indicate previous folder
if(myCurrentNode.hasParent()) if(myCurrentNode.hasParent())
@ -311,7 +312,7 @@ void LauncherDialog::loadDirListing()
// Now add the directory entries // Now add the directory entries
for(unsigned int idx = 0; idx < files.size(); idx++) for(unsigned int idx = 0; idx < files.size(); idx++)
{ {
string name = files[idx].displayName(); string name = files[idx].getDisplayName();
bool isDir = files[idx].isDirectory(); bool isDir = files[idx].isDirectory();
// Honour the filtering settings // Honour the filtering settings
@ -327,7 +328,7 @@ void LauncherDialog::loadDirListing()
continue; continue;
} }
myGameList->appendGame(name, files[idx].path(), "", isDir); myGameList->appendGame(name, files[idx].getPath(), "", isDir);
} }
// Sort the list by rom name (since that's what we see in the listview) // Sort the list by rom name (since that's what we see in the listview)
@ -433,7 +434,7 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
if(myGameList->name(item) == " [..]") if(myGameList->name(item) == " [..]")
myCurrentNode = myCurrentNode.getParent(); myCurrentNode = myCurrentNode.getParent();
else else
myCurrentNode = rom; myCurrentNode = FilesystemNode(rom);
updateListing(); updateListing();
} }
else if(!LauncherFilterDialog::isValidRomName(rom, extension) || else if(!LauncherFilterDialog::isValidRomName(rom, extension) ||
@ -470,7 +471,7 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
break; break;
case kRomDirChosenCmd: case kRomDirChosenCmd:
myCurrentNode = instance().settings().getString("romdir"); myCurrentNode = FilesystemNode(instance().settings().getString("romdir"));
updateListing(); updateListing();
break; break;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: RomAuditDialog.cxx,v 1.11 2009-01-06 23:02:18 stephena Exp $ // $Id: RomAuditDialog.cxx,v 1.12 2009-01-11 19:10:40 stephena Exp $
// //
// Based on code from ScummVM - Scumm Interpreter // Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
@ -115,7 +115,8 @@ void RomAuditDialog::auditRoms()
myResults2->setLabel(""); myResults2->setLabel("");
FilesystemNode node(auditPath); FilesystemNode node(auditPath);
FSList files = node.listDir(FilesystemNode::kListFilesOnly); FSList files;
node.getChildren(files, FilesystemNode::kListFilesOnly);
// Create a progress dialog box to show the progress of processing // Create a progress dialog box to show the progress of processing
// the ROMs, since this is usually a time-consuming operation // the ROMs, since this is usually a time-consuming operation
@ -130,16 +131,16 @@ void RomAuditDialog::auditRoms()
{ {
string extension; string extension;
if(!files[idx].isDirectory() && if(!files[idx].isDirectory() &&
LauncherFilterDialog::isValidRomName(files[idx].path(), extension)) LauncherFilterDialog::isValidRomName(files[idx].getPath(), extension))
{ {
// Calculate the MD5 so we can get the rest of the info // Calculate the MD5 so we can get the rest of the info
// from the PropertiesSet (stella.pro) // from the PropertiesSet (stella.pro)
const string& md5 = instance().MD5FromFile(files[idx].path()); const string& md5 = instance().MD5FromFile(files[idx].getPath());
instance().propSet().getMD5(md5, props); instance().propSet().getMD5(md5, props);
const string& name = props.get(Cartridge_Name); const string& name = props.get(Cartridge_Name);
// Only rename the file if we found a valid properties entry // Only rename the file if we found a valid properties entry
if(name != "" && name != files[idx].displayName()) if(name != "" && name != files[idx].getDisplayName())
{ {
// Check for terminating separator // Check for terminating separator
string newfile = auditPath; string newfile = auditPath;
@ -147,8 +148,8 @@ void RomAuditDialog::auditRoms()
newfile += BSPF_PATH_SEPARATOR; newfile += BSPF_PATH_SEPARATOR;
newfile += name + "." + extension; newfile += name + "." + extension;
if(files[idx].path() != newfile) if(files[idx].getPath() != newfile)
if(FilesystemNode::renameFile(files[idx].path(), newfile)) if(AbstractFilesystemNode::renameFile(files[idx].getPath(), newfile))
renamed++; renamed++;
} }
else else
@ -182,7 +183,7 @@ void RomAuditDialog::handleCommand(CommandSender* sender, int cmd,
case kAuditDirChosenCmd: case kAuditDirChosenCmd:
{ {
FilesystemNode dir(myBrowser->getResult()); FilesystemNode dir(myBrowser->getResult());
myRomPath->setEditString(dir.path()); myRomPath->setEditString(dir.getPath());
myResults1->setLabel(""); myResults1->setLabel("");
myResults2->setLabel(""); myResults2->setLabel("");
break; break;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: FSNodePOSIX.cxx,v 1.16 2009-01-01 18:13:39 stephena Exp $ // $Id: FSNodePOSIX.cxx,v 1.17 2009-01-11 19:10:40 stephena Exp $
// //
// Based on code from ScummVM - Scumm Interpreter // Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project // Copyright (C) 2002-2004 The ScummVM project
@ -36,193 +36,228 @@
/* /*
* Implementation of the Stella file system API based on POSIX (for Linux and OSX) * Implementation of the Stella file system API based on POSIX (for Linux and OSX)
*
* Parts of this class are documented in the base interface class, AbstractFilesystemNode.
*/ */
class POSIXFilesystemNode : public AbstractFilesystemNode class POSIXFilesystemNode : public AbstractFilesystemNode
{ {
public: public:
/**
* Creates a POSIXFilesystemNode with the root node as path.
*/
POSIXFilesystemNode(); POSIXFilesystemNode();
POSIXFilesystemNode(const string& path);
POSIXFilesystemNode(const POSIXFilesystemNode* node);
virtual string displayName() const { return _displayName; } /**
virtual bool isValid() const { return _isValid; } * Creates a POSIXFilesystemNode for a given path.
*
* @param path String with the path the new node should point to.
* @param verify true if the isValid and isDirectory flags should be verified during the construction.
*/
POSIXFilesystemNode(const string& path, bool verify);
virtual bool exists() const { return access(_path.c_str(), F_OK) == 0; }
virtual string getDisplayName() const { return _displayName; }
virtual string getName() const { return _displayName; }
virtual string getPath() const { return _path; }
virtual bool isDirectory() const { return _isDirectory; } virtual bool isDirectory() const { return _isDirectory; }
virtual string path() const { return _path; } virtual bool isReadable() const { return access(_path.c_str(), R_OK) == 0; }
virtual bool isWritable() const { return access(_path.c_str(), W_OK) == 0; }
virtual FSList listDir(ListMode mode = kListDirectoriesOnly) const; virtual bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const;
virtual AbstractFilesystemNode* parent() const; virtual AbstractFilesystemNode* getParent() const;
protected: protected:
string _displayName; string _displayName;
string _path;
bool _isDirectory; bool _isDirectory;
bool _isValid; bool _isValid;
string _path;
private:
/**
* Tests and sets the _isValid and _isDirectory flags, using the stat() function.
*/
virtual void setFlags();
}; };
/**
* Returns the last component of a given path.
*
* Examples:
* /foo/bar.txt would return /bar.txt
* /foo/bar/ would return /bar/
*
* @param str String containing the path.
* @return Pointer to the first char of the last component inside str.
*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const char* lastPathComponent(const string& str) const char* lastPathComponent(const string& str)
{ {
if(str.empty())
return "";
const char *start = str.c_str(); const char *start = str.c_str();
const char *cur = start + str.size() - 2; const char *cur = start + str.size() - 2;
while (cur > start && *cur != '/') while (cur >= start && *cur != '/')
--cur; --cur;
return cur+1; return cur + 1;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static string validatePath(const string& p) void POSIXFilesystemNode::setFlags()
{ {
string path = p; struct stat st;
if(p.size() <= 0 || p[0] != '/')
path = "/";
return path; _isValid = (0 == stat(_path.c_str(), &st));
} _isDirectory = _isValid ? S_ISDIR(st.st_mode) : false;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFilesystemNode* FilesystemNode::getRoot()
{
return new POSIXFilesystemNode();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFilesystemNode* FilesystemNode::getNodeForPath(const string& path)
{
return new POSIXFilesystemNode(validatePath(path));
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
POSIXFilesystemNode::POSIXFilesystemNode() POSIXFilesystemNode::POSIXFilesystemNode()
{ {
char buf[MAXPATHLEN]; // The root dir.
_path = getcwd(buf, MAXPATHLEN) == buf ? buf : ""; _path = "/";
_displayName = _path;
_displayName = lastPathComponent(_path);
_path += '/';
_isValid = true; _isValid = true;
_isDirectory = true; _isDirectory = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
POSIXFilesystemNode::POSIXFilesystemNode(const string& p) POSIXFilesystemNode::POSIXFilesystemNode(const string& p, bool verify)
{ {
string path = validatePath(p); // Expand "~/" to the value of the HOME env variable
if ( p.length() >= 2 && p[0] == '~' && p[1] == '/')
Int32 len = 0, offset = path.size();
struct stat st;
_path = path;
// Extract last component from path
const char *str = path.c_str();
while (offset > 0 && str[offset-1] == '/')
offset--;
while (offset > 0 && str[offset-1] != '/')
{ {
len++; const char *home = getenv("HOME");
offset--; if (home != NULL && strlen(home) < MAXPATHLEN)
{
_path = home;
// Skip over the tilda. We know that p contains at least
// two chars, so this is safe:
_path += p.c_str() + 1;
} }
_displayName = string(str + offset, len); }
else
_path = p;
// Check whether it is a directory, and whether the file actually exists _displayName = lastPathComponent(_path);
_isValid = (0 == stat(_path.c_str(), &st));
_isDirectory = S_ISDIR(st.st_mode); if (verify)
setFlags();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
POSIXFilesystemNode::POSIXFilesystemNode(const POSIXFilesystemNode* node) bool POSIXFilesystemNode::getChildren(AbstractFSList& myList, ListMode mode,
bool hidden) const
{ {
_displayName = node->_displayName; assert(_isDirectory);
_isValid = node->_isValid;
_isDirectory = node->_isDirectory;
_path = node->_path;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FSList POSIXFilesystemNode::listDir(ListMode mode) const
{
DIR *dirp = opendir(_path.c_str()); DIR *dirp = opendir(_path.c_str());
struct stat st;
struct dirent *dp; struct dirent *dp;
FSList myList;
if (dirp == NULL) if (dirp == NULL)
return myList; return false;
// ... loop over dir entries using readdir // loop over dir entries using readdir
while ((dp = readdir(dirp)) != NULL) while ((dp = readdir(dirp)) != NULL)
{ {
// Skip 'invisible' files // Skip 'invisible' files if necessary
if (dp->d_name[0] == '.') if (dp->d_name[0] == '.' && !hidden)
continue; continue;
POSIXFilesystemNode entry; // Skip '.' and '..' to avoid cycles
entry._displayName = dp->d_name; if ((dp->d_name[0] == '.' && dp->d_name[1] == 0) || (dp->d_name[0] == '.' && dp->d_name[1] == '.'))
entry._path = _path;
if (entry._path.length() > 0 && entry._path[entry._path.length()-1] != '/')
entry._path += '/';
entry._path += dp->d_name;
if (stat(entry._path.c_str(), &st))
continue; continue;
string newPath(_path);
if (newPath.length() > 0 && newPath[newPath.length()-1] != '/')
newPath += '/';
newPath += dp->d_name;
POSIXFilesystemNode entry(newPath, false);
#if defined(SYSTEM_NOT_SUPPORTING_D_TYPE)
/* TODO: d_type is not part of POSIX, so it might not be supported
* on some of our targets. For those systems where it isn't supported,
* add this #elif case, which tries to use stat() instead.
*
* The d_type method is used to avoid costly recurrent stat() calls in big
* directories.
*/
entry.setFlags();
#else
if (dp->d_type == DT_UNKNOWN)
{
// Fall back to stat()
entry.setFlags();
}
else
{
entry._isValid = (dp->d_type == DT_DIR) || (dp->d_type == DT_REG) || (dp->d_type == DT_LNK);
if (dp->d_type == DT_LNK)
{
struct stat st;
if (stat(entry._path.c_str(), &st) == 0)
entry._isDirectory = S_ISDIR(st.st_mode); entry._isDirectory = S_ISDIR(st.st_mode);
else
entry._isDirectory = false;
}
else
entry._isDirectory = (dp->d_type == DT_DIR);
}
#endif
// Skip files that are invalid for some reason (e.g. because we couldn't
// properly stat them).
if (!entry._isValid)
continue;
// Honor the chosen mode // Honor the chosen mode
if ((mode == kListFilesOnly && entry._isDirectory) || if ((mode == FilesystemNode::kListFilesOnly && entry._isDirectory) ||
(mode == kListDirectoriesOnly && !entry._isDirectory)) (mode == FilesystemNode::kListDirectoriesOnly && !entry._isDirectory))
continue; continue;
if (entry._isDirectory) if (entry._isDirectory)
entry._path += "/"; entry._path += "/";
myList.push_back(wrap(new POSIXFilesystemNode(&entry))); myList.push_back(new POSIXFilesystemNode(entry));
} }
closedir(dirp); closedir(dirp);
return myList; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFilesystemNode *POSIXFilesystemNode::parent() const AbstractFilesystemNode* POSIXFilesystemNode::getParent() const
{ {
if (_path == "/") if (_path == "/")
return 0; return 0;
POSIXFilesystemNode* p = new POSIXFilesystemNode();
const char *start = _path.c_str(); const char *start = _path.c_str();
const char *end = lastPathComponent(_path); const char *end = lastPathComponent(_path);
p->_path = string(start, end - start); return new POSIXFilesystemNode(string(start, end - start), true);
p->_displayName = lastPathComponent(p->_path);
p->_isValid = true;
p->_isDirectory = true;
return p;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AbstractFilesystemNode::fileExists(const string& path) AbstractFilesystemNode* AbstractFilesystemNode::makeRootFileNode()
{ {
struct stat st; return new POSIXFilesystemNode();
if(stat(path.c_str(), &st) != 0)
return false;
return S_ISREG(st.st_mode);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AbstractFilesystemNode::dirExists(const string& path) AbstractFilesystemNode* AbstractFilesystemNode::makeCurrentDirectoryFileNode()
{ {
struct stat st; char buf[MAXPATHLEN];
if(stat(path.c_str(), &st) != 0) getcwd(buf, MAXPATHLEN);
return false; return new POSIXFilesystemNode(buf, true);
}
return S_ISDIR(st.st_mode); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFilesystemNode* AbstractFilesystemNode::makeFileNodePath(const string& path)
{
return new POSIXFilesystemNode(path, true);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -