diff --git a/stella/src/build/makefile b/stella/src/build/makefile index d82d4777e..4374650ae 100644 --- a/stella/src/build/makefile +++ b/stella/src/build/makefile @@ -13,7 +13,7 @@ ## See the file "license" for information on usage and redistribution of ## this file, and for a DISCLAIMER OF ALL WARRANTIES. ## -## $Id: makefile,v 1.75 2005-05-06 18:38:59 stephena Exp $ +## $Id: makefile,v 1.76 2005-05-09 18:58:17 stephena Exp $ ##============================================================================ ##============================================================================ @@ -126,7 +126,7 @@ linux: $(MAKE) $(SMP) stella \ EXE_NAME="stella" \ OPTIONS="$(OPTIONS) -DBSPF_UNIX -DUNIX -DHAVE_GETTIMEOFDAY" \ - OBJS="$(OBJECTS) SettingsUNIX.o OSystemUNIX.o" + OBJS="$(OBJECTS) SettingsUNIX.o OSystemUNIX.o FSNodePOSIX.o" linux-gl: $(MAKE) $(SMP) stella \ @@ -134,20 +134,20 @@ linux-gl: LDFLAGS="$(LDFLAGS) -L/usr/X11R6/lib" \ LDLIBS="$(LDLIBS) -lGL" \ OPTIONS="$(OPTIONS) -DBSPF_UNIX -DUNIX -DHAVE_GETTIMEOFDAY -DDISPLAY_OPENGL" \ - OBJS="$(OBJECTS) FrameBufferGL.o SettingsUNIX.o OSystemUNIX.o" + OBJS="$(OBJECTS) FrameBufferGL.o SettingsUNIX.o OSystemUNIX.o FSNodePOSIX.o" win32: $(MAKE) $(SMP) stella \ EXE_NAME="stella.exe" \ OPTIONS="$(OPTIONS) -DBSPF_WIN32 -DWIN32" \ - OBJS="$(OBJECTS) SettingsWin32.o OSystemWin32.o" + OBJS="$(OBJECTS) SettingsWin32.o OSystemWin32.o FSNodeWin32.o" win32-gl: $(MAKE) $(SMP) stella \ EXE_NAME="stella.exe" \ LDLIBS="$(LDLIBS) -lopengl32" \ OPTIONS="$(OPTIONS) -DBSPF_WIN32 -DWIN32 -DDISPLAY_OPENGL -DTEXTURES_ARE_LOST" \ - OBJS="$(OBJECTS) FrameBufferGL.o SettingsWin32.o OSystemWin32.o" + OBJS="$(OBJECTS) FrameBufferGL.o SettingsWin32.o FSNodeWin32.o" ############################################################################### @@ -168,7 +168,7 @@ CORE_OBJS = Booster.o Cart.o Cart2K.o Cart3F.o Cart4K.o CartAR.o CartDPC.o \ Event.o Joystick.o Keyboard.o M6532.o MD5.o MediaSrc.o Paddles.o \ Props.o PropsSet.o Random.o SoundNull.o Switches.o Settings.o TIA.o \ Serializer.o Deserializer.o EventHandler.o FrameBuffer.o \ - OSystem.o \ + OSystem.o FSNode.o \ $(M6502_OBJS) $(GUI_OBJS) stella: $(CORE_OBJS) $(OBJS) @@ -316,18 +316,27 @@ Settings.o: $(CORE)/Settings.cxx $(CORE)/Settings.hxx OSystem.o: $(CORE)/OSystem.cxx $(CORE)/OSystem.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(CORE)/OSystem.cxx +FSNode.o: $(CORE)/FSNode.cxx $(CORE)/FSNode.hxx + $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(CORE)/FSNode.cxx + SettingsUNIX.o: $(SRC)/unix/SettingsUNIX.cxx $(SRC)/unix/SettingsUNIX.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(SRC)/unix/SettingsUNIX.cxx OSystemUNIX.o: $(SRC)/unix/OSystemUNIX.cxx $(SRC)/unix/OSystemUNIX.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(SRC)/unix/OSystemUNIX.cxx +FSNodePOSIX.o: $(SRC)/unix/FSNodePOSIX.cxx + $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(SRC)/unix/FSNodePOSIX.cxx + SettingsWin32.o: $(SRC)/win32/SettingsWin32.cxx $(SRC)/win32/SettingsWin32.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(SRC)/win32/SettingsWin32.cxx OSystemWin32.o: $(SRC)/win32/OSystemWin32.cxx $(SRC)/win32/OSystemWin32.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(SRC)/win32/OSystemWin32.cxx +FSNodeWin32.o: $(SRC)/win32/FSNodeWin32.cxx + $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(SRC)/win32/FSNodeWin32.cxx + SoundNull.o: $(COMMON)/SoundNull.cxx $(COMMON)/SoundNull.hxx $(CXX) -c $(FLAGS) $(OPTIONS) $(LDFLAGS) $(COMMON)/SoundNull.cxx diff --git a/stella/src/common/mainSDL.cxx b/stella/src/common/mainSDL.cxx index 1c48a7894..d6fd2f52a 100644 --- a/stella/src/common/mainSDL.cxx +++ b/stella/src/common/mainSDL.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: mainSDL.cxx,v 1.37 2005-05-06 18:38:59 stephena Exp $ +// $Id: mainSDL.cxx,v 1.38 2005-05-09 18:58:18 stephena Exp $ //============================================================================ #include @@ -33,6 +33,7 @@ #include "PropsSet.hxx" #include "Sound.hxx" #include "Settings.hxx" +#include "FSNode.hxx" #include "OSystem.hxx" #if defined(UNIX) diff --git a/stella/src/emucore/FSNode.cxx b/stella/src/emucore/FSNode.cxx new file mode 100644 index 000000000..fa6895fcf --- /dev/null +++ b/stella/src/emucore/FSNode.cxx @@ -0,0 +1,114 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: FSNode.cxx,v 1.1 2005-05-09 18:58:18 stephena Exp $ +// +// Based on code from ScummVM - Scumm Interpreter +// Copyright (C) 2002-2004 The ScummVM project +//============================================================================ + +#include "bspf.hxx" +#include "GuiUtils.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) + SWAP(_data[min], _data[i]); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +FilesystemNode AbstractFilesystemNode::wrap(AbstractFilesystemNode *node) +{ + FilesystemNode wrapper; + wrapper._realNode = node; + + return wrapper; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +FilesystemNode::FilesystemNode() +{ + _realNode = getRoot(); + _refCount = new int(1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +FilesystemNode::FilesystemNode(const FilesystemNode &node) + : AbstractFilesystemNode() +{ + _realNode = node._realNode; + _refCount = node._refCount; + ++(*_refCount); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#ifdef MACOSX +FilesystemNode::FilesystemNode(const string& p) +{ + _realNode = getNodeForPath(p); + _refCount = new int(1); +} +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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 + return AbstractFilesystemNode::wrap(node); +} diff --git a/stella/src/emucore/FSNode.hxx b/stella/src/emucore/FSNode.hxx new file mode 100644 index 000000000..98b43a7b8 --- /dev/null +++ b/stella/src/emucore/FSNode.hxx @@ -0,0 +1,209 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: FSNode.hxx,v 1.1 2005-05-09 18:58:18 stephena Exp $ +// +// Based on code from ScummVM - Scumm Interpreter +// Copyright (C) 2002-2004 The ScummVM project +//============================================================================ + +#ifndef FS_NODE_HXX +#define FS_NODE_HXX + +/* + * The API described in this header is meant to allow for file system browsing in a + * portable fashions. 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. + */ + +/* + * TODO - Instead of starting with getRoot(), we should rather add a getDefaultDir() + * call that on Unix might return the current dir or the users home dir... + * i.e. the root dir is usually not the best starting point for browsing. + */ + +#include "Array.hxx" + +class FilesystemNode; + +/** + * List of multiple file system nodes. E.g. the contents of a given directory. + */ +class FSList : public Array +{ + public: + void sort(); +}; + + +/** + * File system node. + */ +class AbstractFilesystemNode +{ + public: + /** + Flag to tell listDir() which kind of files to list. + */ + typedef enum { + kListFilesOnly = 1, + kListDirectoriesOnly = 2, + kListAll = 3 + } ListMode; + + virtual ~AbstractFilesystemNode() {} + + /** + Return display name, used by e.g. the GUI to present the file in the file browser. + + @return the display name + */ + virtual string displayName() const = 0; + + /** + Is this node valid (i.e. referring to an actual FS object)? + */ + virtual bool isValid() const = 0; + + /** + Is this node a directory or not? + */ + virtual bool isDirectory() const = 0; + + /** + A path representation suitable for use with fopen() + */ + virtual string path() const = 0; + + /** + List the content of this directory node. + If this node is not a directory, throw an exception or call error(). + */ + virtual FSList listDir(ListMode mode = kListDirectoriesOnly) const = 0; + + /** + Compare the name of this node to the name of another. + */ + 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; + } + + /* TODO: + bool exists(); + + bool isDirectory(); + bool isFile(); + + bool isReadable(); + bool isWriteable(); + */ + + protected: + friend class FilesystemNode; + + /** + The parent node of this directory. + The parent of the root is the root itself. + */ + virtual AbstractFilesystemNode *parent() const = 0; + + /** + * 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); +#ifdef MACOSX + FilesystemNode(const string& path); +#endif + ~FilesystemNode(); + + FilesystemNode &operator =(const FilesystemNode &node); + + FilesystemNode getParent() 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(); + +#ifdef MACOSX + /* + * Construct a node based on a path; the path is in the same format as it + * would be for calls to fopen(). + * + * I.e. getNodeForPath(oldNode.path()) should create a new node identical to oldNode. + */ + static AbstractFilesystemNode* getNodeForPath(const string& path); +#endif +}; + +#endif diff --git a/stella/src/gui/Launcher.cxx b/stella/src/gui/Launcher.cxx index b1c240102..b7ded5d40 100644 --- a/stella/src/gui/Launcher.cxx +++ b/stella/src/gui/Launcher.cxx @@ -13,7 +13,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: Launcher.cxx,v 1.2 2005-05-06 22:50:15 stephena Exp $ +// $Id: Launcher.cxx,v 1.3 2005-05-09 18:58:19 stephena Exp $ //============================================================================ #include "OSystem.hxx" @@ -36,8 +36,10 @@ Launcher::~Launcher() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Launcher::initialize() { - delete myBaseDialog; - myBaseDialog = new LauncherDialog(myOSystem, 0, 0, kLauncherWidth, kLauncherHeight); + // We only create one instance of this dialog, since each time we do so, + // the ROM listing is read from disk. This can be very expensive. + if(myBaseDialog == NULL) + myBaseDialog = new LauncherDialog(myOSystem, 0, 0, kLauncherWidth, kLauncherHeight); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/stella/src/gui/LauncherDialog.cxx b/stella/src/gui/LauncherDialog.cxx index e6dd0837e..3f0bbd1a0 100644 --- a/stella/src/gui/LauncherDialog.cxx +++ b/stella/src/gui/LauncherDialog.cxx @@ -13,13 +13,14 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: LauncherDialog.cxx,v 1.3 2005-05-08 17:38:23 stephena Exp $ +// $Id: LauncherDialog.cxx,v 1.4 2005-05-09 18:58:19 stephena Exp $ // // Based on code from ScummVM - Scumm Interpreter // Copyright (C) 2002-2004 The ScummVM project //============================================================================ #include "OSystem.hxx" +#include "FSNode.hxx" #include "Widget.hxx" #include "ListWidget.hxx" #include "Dialog.hxx" @@ -78,9 +79,6 @@ LauncherDialog::LauncherDialog(OSystem* osystem, uInt16 x, uInt16 y, myList->setEditable(false); myList->setNumberingMode(kListNumberingOff); - // Populate the list - updateListing(); - // Restore last selection /* string last = ConfMan.get(String("lastselectedgame"), ConfigManager::kApplicationDomain); @@ -114,6 +112,10 @@ LauncherDialog::~LauncherDialog() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void LauncherDialog::loadConfig() { + // Assume that if the list is empty, this is the first time that loadConfig() + // has been called (and we should reload the list). + if(myList->getList().isEmpty()) + updateListing(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -135,10 +137,35 @@ void LauncherDialog::close() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void LauncherDialog::updateListing() { -// FIXME - add bulk of KStella code here wrt loading from stella.cache cerr << "LauncherDialog::updateListing()\n"; + + // Figure out if the ROM dir has changed since we last accessed it. + // If so, we do a full reload from disk (takes quite some time). + // Otherwise, we can use the cache file (which is much faster). +// FIXME - actually implement the following code /* - Common::StringList l; + if(... ROM_DIR_CHANGED ...) + loadListFromDisk(); + else if( ... CACHE_FILE_EXISTS) + loadListFromCache(); + else // we have no other choice + loadListFromDisk(); +*/ + + StringList l; + + FilesystemNode t; + FilesystemNode dir(t);//"/local/emulators/atari/roms"); // FIXME + FSList files = dir.listDir(FilesystemNode::kListAll); + files.sort(); + + for (int idx = 0; idx < (int)files.size(); idx++) + l.push_back(files[idx].displayName()); + +/* + // ...so let's determine a list of candidates, games that + // could be contained in the specified directory. + DetectedGameList candidates(PluginManager::instance().detectGames(files)); // Retrieve a list of all games defined in the config file _domains.clear(); @@ -166,10 +193,9 @@ cerr << "LauncherDialog::updateListing()\n"; _domains.insert_at(pos, iter->_key); } } - - _list->setList(l); - updateButtons(); */ + myList->setList(l); + updateButtons(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -292,19 +318,21 @@ void LauncherDialog::addGame() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void LauncherDialog::handleCommand(CommandSender* sender, uInt32 cmd, uInt32 data) { - Int32 item = myList->getSelected(); - switch (cmd) { case kStartCmd: case kListItemActivatedCmd: case kListItemDoubleClickedCmd: - cerr << "Game selected: " << item << endl; - // FIXME - start a new console based on the filename selected - // this is only here for testing - instance()->createConsole("frostbite.a26"); - close(); + { + if(myList->getSelected() >= 0) + { + string item = myList->getSelectedString(); + cerr << "Game selected: " << item << endl; + instance()->createConsole(item); + close(); + } break; + } case kLocationCmd: cerr << "kLocationCmd from LauncherDialog\n"; diff --git a/stella/src/unix/FSNodePOSIX.cxx b/stella/src/unix/FSNodePOSIX.cxx new file mode 100644 index 000000000..354d6e193 --- /dev/null +++ b/stella/src/unix/FSNodePOSIX.cxx @@ -0,0 +1,199 @@ +//============================================================================ +// +// 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-2005 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: FSNodePOSIX.cxx,v 1.1 2005-05-09 18:58:19 stephena Exp $ +// +// Based on code from ScummVM - Scumm Interpreter +// Copyright (C) 2002-2004 The ScummVM project +//============================================================================ + +#include "FSNode.hxx" + +#ifdef MACOSX + #include +#endif + +#include +#include +#include + +#include +#include + +/* + * Implementation of the Stella file system API based on POSIX (for Linux and OSX) + */ + +class POSIXFilesystemNode : public AbstractFilesystemNode +{ + public: + POSIXFilesystemNode(); + POSIXFilesystemNode(const string& path); + POSIXFilesystemNode(const POSIXFilesystemNode* node); + + virtual string displayName() const { return _displayName; } + virtual bool isValid() const { return _isValid; } + virtual bool isDirectory() const { return _isDirectory; } + virtual string path() const { return _path; } + + virtual FSList listDir(ListMode mode = kListDirectoriesOnly) const; + virtual AbstractFilesystemNode* parent() const; + + protected: + string _displayName; + bool _isDirectory; + bool _isValid; + string _path; +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +static const char* lastPathComponent(const string& str) +{ + // FIXME - use native C++ string code + const char *start = str.c_str(); + const char *cur = start + str.size() - 2; + + while (cur > start && *cur != '/') + --cur; + + return cur+1; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +AbstractFilesystemNode* FilesystemNode::getRoot() +{ + return new POSIXFilesystemNode(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#ifdef MACOSX +AbstractFilesystemNode* FilesystemNode::getNodeForPath(const string& path) +{ + return new POSIXFilesystemNode(path); +} +#endif + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +POSIXFilesystemNode::POSIXFilesystemNode() +{ + char buf[MAXPATHLEN]; + getcwd(buf, MAXPATHLEN); + + _path = buf; + _displayName = lastPathComponent(_path); + _path += '/'; + _isValid = true; + _isDirectory = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +POSIXFilesystemNode::POSIXFilesystemNode(const string& p) +{ + Int32 len = 0, offset = p.size(); + struct stat st; + + assert(offset > 0); + + _path = p; + + // Extract last component from path + const char *str = p.c_str(); + while (offset > 0 && str[offset-1] == '/') + offset--; + while (offset > 0 && str[offset-1] != '/') + { + len++; + offset--; + } + _displayName = string(str + offset, len); + + // Check whether it is a directory, and whether the file actually exists + _isValid = (0 == stat(_path.c_str(), &st)); + _isDirectory = S_ISDIR(st.st_mode); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +POSIXFilesystemNode::POSIXFilesystemNode(const POSIXFilesystemNode* node) +{ + _displayName = node->_displayName; + _isValid = node->_isValid; + _isDirectory = node->_isDirectory; + _path = node->_path; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +FSList POSIXFilesystemNode::listDir(ListMode mode) const +{ + assert(_isDirectory); + DIR *dirp = opendir(_path.c_str()); + struct stat st; + + struct dirent *dp; + FSList myList; + + if (dirp == NULL) + return myList; + + // ... loop over dir entries using readdir + while ((dp = readdir(dirp)) != NULL) + { + // Skip 'invisible' files + if (dp->d_name[0] == '.') + continue; + + POSIXFilesystemNode entry; + entry._displayName = dp->d_name; + 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; + entry._isDirectory = S_ISDIR(st.st_mode); + + // Honor the chosen mode + if ((mode == kListFilesOnly && entry._isDirectory) || + (mode == kListDirectoriesOnly && !entry._isDirectory)) + continue; + + if (entry._isDirectory) + entry._path += "/"; + + myList.push_back(wrap(new POSIXFilesystemNode(&entry))); + } + closedir(dirp); + + return myList; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +AbstractFilesystemNode *POSIXFilesystemNode::parent() const +{ + if (_path == "/") + return 0; + + POSIXFilesystemNode* p = new POSIXFilesystemNode(); + const char *start = _path.c_str(); + const char *end = lastPathComponent(_path); + + p->_path = string(start, end - start); + p->_displayName = lastPathComponent(p->_path); + + p->_isValid = true; + p->_isDirectory = true; + + return p; +}