More reworking of the file selection mechanism.

- removed GameList, and integrated functionality directly into 'FSList' (which was mostly doing the same thing)
- have FSNode::getChildren() relabel directories, instead of Browser and Launcher dialogs
- MD5 calculations in ROM launcher are now cached, instead of being recalculated each time a directory is left and re-entered

Windows and macOS likely broken for a moment; this will be fixed next.
This commit is contained in:
Stephen Anthony 2019-06-22 21:09:42 -02:30
parent 786f3b2a49
commit bcca945951
15 changed files with 145 additions and 266 deletions

View File

@ -134,8 +134,8 @@ void FilesystemNodeZIP::setFlags(const string& zipfile,
{ {
_path += ("/" + _virtualPath); _path += ("/" + _virtualPath);
_shortPath += ("/" + _virtualPath); _shortPath += ("/" + _virtualPath);
_name = lastPathComponent(_path);
} }
_name = lastPathComponent(_path);
_error = zip_error::NONE; _error = zip_error::NONE;
if(!_realNode->isFile()) if(!_realNode->isFile())
@ -145,8 +145,7 @@ void FilesystemNodeZIP::setFlags(const string& zipfile,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNodeZIP::getChildren(AbstractFSList& myList, ListMode mode, bool FilesystemNodeZIP::getChildren(AbstractFSList& myList, ListMode mode) const
bool hidden) const
{ {
// Files within ZIP archives don't contain children // Files within ZIP archives don't contain children
if(!isDirectory() || _error != zip_error::NONE) if(!isDirectory() || _error != zip_error::NONE)

View File

@ -48,9 +48,11 @@ class FilesystemNodeZIP : public AbstractFSNode
explicit FilesystemNodeZIP(const string& path); explicit FilesystemNodeZIP(const string& path);
bool exists() const override { return _realNode && _realNode->exists(); } bool exists() const override { return _realNode && _realNode->exists(); }
const string& getName() const override { return _name; } const string& getName() const override { return _name; }
void setName(const string& name) override { _name = name; }
const string& getPath() const override { return _path; } const string& getPath() const override { return _path; }
string getShortPath() const override { return _shortPath; } string getShortPath() const override { return _shortPath; }
bool hasParent() const override { return true; }
bool isDirectory() const override { return _isDirectory; } bool isDirectory() const override { return _isDirectory; }
bool isFile() const override { return _isFile; } bool isFile() const override { return _isFile; }
bool isReadable() const override { return _realNode && _realNode->isReadable(); } bool isReadable() const override { return _realNode && _realNode->isReadable(); }
@ -62,7 +64,7 @@ class FilesystemNodeZIP : public AbstractFSNode
bool rename(const string& newfile) override { return false; } bool rename(const string& newfile) override { return false; }
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const override; bool getChildren(AbstractFSList& list, ListMode mode) const override;
AbstractFSNodePtr getParent() const override; AbstractFSNodePtr getParent() const override;
uInt32 read(ByteBuffer& image) const override; uInt32 read(ByteBuffer& image) const override;

View File

@ -37,7 +37,7 @@ FilesystemNode::FilesystemNode(const string& p)
{ {
// Is this potentially a ZIP archive? // Is this potentially a ZIP archive?
#if defined(ZIP_SUPPORT) #if defined(ZIP_SUPPORT)
if(BSPF::containsIgnoreCase(p, ".zip")) if (BSPF::containsIgnoreCase(p, ".zip"))
_realNode = FilesystemNodeFactory::create(p, FilesystemNodeFactory::Type::ZIP); _realNode = FilesystemNodeFactory::create(p, FilesystemNodeFactory::Type::ZIP);
else else
#endif #endif
@ -51,7 +51,7 @@ bool FilesystemNode::exists() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::getChildren(FSList& fslist, ListMode mode, bool hidden) const bool FilesystemNode::getChildren(FSList& fslist, ListMode mode) const
{ {
if (!_realNode || !_realNode->isDirectory()) if (!_realNode || !_realNode->isDirectory())
return false; return false;
@ -59,11 +59,48 @@ bool FilesystemNode::getChildren(FSList& fslist, ListMode mode, bool hidden) con
AbstractFSList tmp; AbstractFSList tmp;
tmp.reserve(fslist.capacity()); tmp.reserve(fslist.capacity());
if (!_realNode->getChildren(tmp, mode, hidden)) if (!_realNode->getChildren(tmp, mode))
return false; return false;
std::sort(tmp.begin(), tmp.end(),
[](const AbstractFSNodePtr& node1, const AbstractFSNodePtr& node2)
{
if (node1->isDirectory() != node2->isDirectory())
return node1->isDirectory();
else
return BSPF::compareIgnoreCase(node1->getName(), node2->getName()) < 0;
}
);
// Add parent node, if it is valid to do so
if (hasParent())
{
FilesystemNode parent = getParent();
parent.setName(" [..]");
fslist.emplace_back(parent);
}
// And now add the rest of the entries
for (const auto& i: tmp) for (const auto& i: tmp)
fslist.emplace_back(FilesystemNode(i)); {
#if defined(ZIP_SUPPORT)
// Force ZIP c'tor to be called
if (BSPF::endsWithIgnoreCase(i->getPath(), ".zip"))
{
AbstractFSNodePtr ptr = FilesystemNodeFactory::create(i->getPath(),
FilesystemNodeFactory::Type::ZIP);
fslist.emplace_back(FilesystemNode(ptr));
}
else
#endif
{
// Make directories stand out
if (i->isDirectory())
i->setName(" [" + i->getName() + "]");
fslist.emplace_back(FilesystemNode(i));
}
}
return true; return true;
} }
@ -74,6 +111,13 @@ const string& FilesystemNode::getName() const
return _realNode->getName(); return _realNode->getName();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FilesystemNode::setName(const string& name)
{
_realNode->setName(name);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& FilesystemNode::getPath() const const string& FilesystemNode::getPath() const
{ {
@ -109,7 +153,7 @@ string FilesystemNode::getPathWithExt(const string& ext) const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNode::hasParent() const bool FilesystemNode::hasParent() const
{ {
return _realNode ? (_realNode->getParent() != nullptr) : false; return _realNode ? _realNode->hasParent() : false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -164,23 +208,23 @@ uInt32 FilesystemNode::read(ByteBuffer& image) const
uInt32 size = 0; uInt32 size = 0;
// File must actually exist // File must actually exist
if(!(exists() && isReadable())) if (!(exists() && isReadable()))
throw runtime_error("File not found/readable"); throw runtime_error("File not found/readable");
// First let the private subclass attempt to open the file // First let the private subclass attempt to open the file
if((size = _realNode->read(image)) > 0) if ((size = _realNode->read(image)) > 0)
return size; return size;
// Otherwise, the default behaviour is to read from a normal C++ ifstream // Otherwise, the default behaviour is to read from a normal C++ ifstream
image = make_unique<uInt8[]>(512 * 1024); image = make_unique<uInt8[]>(512 * 1024);
ifstream in(getPath(), std::ios::binary); ifstream in(getPath(), std::ios::binary);
if(in) if (in)
{ {
in.seekg(0, std::ios::end); in.seekg(0, std::ios::end);
std::streampos length = in.tellg(); std::streampos length = in.tellg();
in.seekg(0, std::ios::beg); in.seekg(0, std::ios::beg);
if(length == 0) if (length == 0)
throw runtime_error("Zero-byte file"); throw runtime_error("Zero-byte file");
size = std::min(uInt32(length), 512u * 1024u); size = std::min(uInt32(length), 512u * 1024u);

View File

@ -94,18 +94,6 @@ class FilesystemNode
FilesystemNode(const FilesystemNode&) = default; FilesystemNode(const FilesystemNode&) = default;
FilesystemNode& operator=(const FilesystemNode&) = default; FilesystemNode& operator=(const FilesystemNode&) = default;
/**
* Compare the name of this node to the name of another. Directories
* go before normal files.
*/
inline bool operator<(const FilesystemNode& node) const
{
if (isDirectory() != node.isDirectory())
return isDirectory();
return BSPF::compareIgnoreCase(getName(), node.getName()) < 0;
}
/** /**
* Compare the name of this node to the name of another, testing for * Compare the name of this node to the name of another, testing for
* equality, * equality,
@ -139,11 +127,10 @@ class FilesystemNode
* @return true if successful, false otherwise (e.g. when the directory * @return true if successful, false otherwise (e.g. when the directory
* does not exist). * does not exist).
*/ */
bool getChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly, bool getChildren(FSList& fslist, ListMode mode = ListMode::DirectoriesOnly) const;
bool hidden = false) const;
/** /**
* Return a string representation of the name of the file. This is can be * Set/get 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 * 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 * file. But it is *not* suitable for use with fopen / File::open, nor
* should it be archived. * should it be archived.
@ -151,6 +138,7 @@ class FilesystemNode
* @return the file name * @return the file name
*/ */
const string& getName() const; const string& getName() const;
void setName(const string& name);
/** /**
* Return a string representation of the file which can be passed to fopen(). * Return a string representation of the file which can be passed to fopen().
@ -300,12 +288,11 @@ class AbstractFSNode
* *
* @param list List to put the contents of the directory in. * @param list List to put the contents of the directory in.
* @param mode Mode to use while listing the directory. * @param mode Mode to use while listing the directory.
* @param hidden Whether to include hidden files or not in the results.
* *
* @return true if successful, false otherwise (e.g. when the directory * @return true if successful, false otherwise (e.g. when the directory
* does not exist). * does not exist).
*/ */
virtual bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const = 0; virtual bool getChildren(AbstractFSList& list, ListMode mode) const = 0;
/** /**
* Returns the last component of the path pointed by this FilesystemNode. * Returns the last component of the path pointed by this FilesystemNode.
@ -318,6 +305,7 @@ class AbstractFSNode
* implementation for more information. * implementation for more information.
*/ */
virtual const string& getName() const = 0; virtual const string& getName() const = 0;
virtual void setName(const string& name) = 0;
/** /**
* Returns the 'path' of the current node, usable in fopen(). * Returns the 'path' of the current node, usable in fopen().
@ -330,6 +318,17 @@ class AbstractFSNode
virtual string getShortPath() const = 0; virtual string getShortPath() const = 0;
/**
* Determine whether this node has a parent.
*/
virtual bool hasParent() const = 0;
/**
* The parent node of this directory.
* The parent of the root is 'nullptr'.
*/
virtual AbstractFSNodePtr getParent() const = 0;
/** /**
* Indicates whether this path refers to a directory or not. * Indicates whether this path refers to a directory or not.
*/ */
@ -392,12 +391,6 @@ class AbstractFSNode
* a try-catch block. * a try-catch block.
*/ */
virtual uInt32 read(ByteBuffer& buffer) const { return 0; } virtual uInt32 read(ByteBuffer& buffer) const { return 0; }
/**
* The parent node of this directory.
* The parent of the root is the root itself.
*/
virtual AbstractFSNodePtr getParent() const = 0;
}; };
#endif #endif

View File

@ -18,16 +18,21 @@
#include "ScrollBarWidget.hxx" #include "ScrollBarWidget.hxx"
#include "FileListWidget.hxx" #include "FileListWidget.hxx"
#include "Bankswitch.hxx"
#include "MD5.hxx"
#include "bspf.hxx" #include "bspf.hxx"
/**
TODO:
- extension handling not handled
- add lambda filter to selectively choose files based on pattern
- history of selected folders/files
*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileListWidget::FileListWidget(GuiObject* boss, const GUI::Font& font, FileListWidget::FileListWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h) int x, int y, int w, int h)
: StringListWidget(boss, font, x, y, w, h), : StringListWidget(boss, font, x, y, w, h),
_fsmode(FilesystemNode::ListMode::All), _fsmode(FilesystemNode::ListMode::All),
_selectedPos(0) _selected(0)
{ {
// This widget is special, in that it catches signals and redirects them // This widget is special, in that it catches signals and redirects them
setTarget(this); setTarget(this);
@ -45,36 +50,15 @@ void FileListWidget::setLocation(const FilesystemNode& node, string select)
_node = _node.getParent(); _node = _node.getParent();
} }
// Start with empty list // Read in the data from the file system (start with an empty list)
_gameList.clear(); _fileList.clear();
_fileList.reserve(512);
// Read in the data from the file system _node.getChildren(_fileList, _fsmode);
FSList content;
content.reserve(512);
_node.getChildren(content, _fsmode);
// Add '[..]' to indicate previous folder
if(_node.hasParent())
_gameList.appendGame(" [..]", _node.getParent().getPath(), "", true);
// Now add the directory entries
for(const auto& file: content)
{
string name = file.getName();
bool isDir = file.isDirectory();
if(isDir)
name = " [" + name + "]";
else if(!BSPF::endsWithIgnoreCase(name, _extension))
continue;
_gameList.appendGame(name, file.getPath(), "", isDir);
}
_gameList.sortByName();
// 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;
for(uInt32 i = 0; i < _gameList.size(); ++i) for(const auto& file: _fileList)
l.push_back(_gameList.name(i)); l.push_back(file.getName());
setList(l); setList(l);
setSelected(select); setSelected(select);
@ -96,20 +80,7 @@ void FileListWidget::selectParent()
void FileListWidget::reload() void FileListWidget::reload()
{ {
if(_node.exists()) if(_node.exists())
setLocation(_node, _gameList.name(_selectedPos)); setLocation(_node, selected().getName());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& FileListWidget::selectedMD5()
{
if(_selected.isDirectory() || !Bankswitch::isValidRomName(_selected))
return EmptyString;
// Make sure we have a valid md5 for this ROM
if(_gameList.md5(_selectedPos) == "")
_gameList.setMd5(_selectedPos, MD5::hash(_selected));
return _gameList.md5(_selectedPos);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -123,19 +94,16 @@ void FileListWidget::handleCommand(CommandSender* sender, int cmd, int data, int
case ListWidget::kSelectionChangedCmd: case ListWidget::kSelectionChangedCmd:
cmd = ItemChanged; cmd = ItemChanged;
_selected = FilesystemNode(_gameList.path(data)); _selected = data;
_selectedPos = data;
break; break;
case ListWidget::kActivatedCmd: case ListWidget::kActivatedCmd:
case ListWidget::kDoubleClickedCmd: case ListWidget::kDoubleClickedCmd:
if(_gameList.isDir(data)) _selected = data;
if(selected().isDirectory())
{ {
cmd = ItemChanged; cmd = ItemChanged;
if(_gameList.name(data) == " [..]") setLocation(selected());
selectParent();
else
setLocation(FilesystemNode(_gameList.path(data)));
} }
else else
cmd = ItemActivated; cmd = ItemActivated;

View File

@ -21,7 +21,6 @@
class CommandSender; class CommandSender;
#include "FSNode.hxx" #include "FSNode.hxx"
#include "GameList.hxx"
#include "StringListWidget.hxx" #include "StringListWidget.hxx"
/** /**
@ -64,26 +63,19 @@ class FileListWidget : public StringListWidget
void reload(); void reload();
/** Gets current node(s) */ /** Gets current node(s) */
const FilesystemNode& selected() const { return _selected; } const FilesystemNode& selected() const { return _fileList[_selected]; }
const FilesystemNode& currentDir() const { return _node; } const FilesystemNode& currentDir() const { return _node; }
/** Gets MD5sum of the current node, if it is a file, and caches the result.
Otherwise, does nothing.
@return MD5sum of selected file, else EmptyString
*/
const string& selectedMD5();
private: private:
void handleCommand(CommandSender* sender, int cmd, int data, int id) override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
private: private:
FilesystemNode::ListMode _fsmode; FilesystemNode::ListMode _fsmode;
FilesystemNode _node, _selected; FilesystemNode _node;
GameList _gameList; FSList _fileList;
string _extension; string _extension;
uInt32 _selectedPos; uInt32 _selected;
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -1,58 +0,0 @@
//============================================================================
//
// 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-2019 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// Based on code from KStella - Stella frontend
// Copyright (C) 2003-2005 Stephen Anthony
//============================================================================
#include <cctype>
#include <algorithm>
#include "GameList.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void GameList::sortByName()
{
if(myArray.size() < 2)
return;
auto cmp = [](const Entry& a, const Entry& b)
{
// directories always first
if(a._isdir != b._isdir)
return a._isdir;
auto it1 = a._name.cbegin(), it2 = b._name.cbegin();
// Account for ending ']' character in directory entries
auto end1 = a._isdir ? a._name.cend() - 1 : a._name.cend();
auto end2 = b._isdir ? b._name.cend() - 1 : b._name.cend();
// Stop when either string's end has been reached
while((it1 != end1) && (it2 != end2))
{
if(toupper(*it1) != toupper(*it2)) // letters differ?
return toupper(*it1) < toupper(*it2);
// proceed to the next character in each string
++it1;
++it2;
}
return a._name.size() < b._name.size();
};
sort(myArray.begin(), myArray.end(), cmp);
}

View File

@ -1,75 +0,0 @@
//============================================================================
//
// 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-2019 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// Based on code from KStella - Stella frontend
// Copyright (C) 2003-2005 Stephen Anthony
//============================================================================
#ifndef GAME_LIST_HXX
#define GAME_LIST_HXX
#include "bspf.hxx"
/**
Holds the list of game info for the ROM launcher.
*/
class GameList
{
public:
GameList() = default;
const string& name(uInt32 i) const
{ return i < myArray.size() ? myArray[i]._name : EmptyString; }
const string& path(uInt32 i) const
{ return i < myArray.size() ? myArray[i]._path : EmptyString; }
const string& md5(uInt32 i) const
{ return i < myArray.size() ? myArray[i]._md5 : EmptyString; }
bool isDir(uInt32 i) const
{ return i < myArray.size() ? myArray[i]._isdir: false; }
void setMd5(uInt32 i, const string& md5)
{ myArray[i]._md5 = md5; }
uInt32 size() const { return uInt32(myArray.size()); }
void clear() { myArray.clear(); }
void appendGame(const string& name, const string& path, const string& md5,
bool isDir = false) {
myArray.emplace_back(name, path, md5, isDir);
}
void sortByName();
private:
struct Entry {
string _name;
string _path;
string _md5;
bool _isdir;
Entry(string name, string path, string md5, bool isdir)
: _name(name), _path(path), _md5(md5), _isdir(isdir) { }
};
vector<Entry> myArray;
private:
// Following constructors and assignment operators not supported
GameList(const GameList&) = delete;
GameList(GameList&&) = delete;
GameList& operator=(const GameList&) = delete;
GameList& operator=(GameList&&) = delete;
};
#endif

View File

@ -16,6 +16,7 @@
//============================================================================ //============================================================================
#include "bspf.hxx" #include "bspf.hxx"
#include "Bankswitch.hxx"
#include "BrowserDialog.hxx" #include "BrowserDialog.hxx"
#include "ContextMenu.hxx" #include "ContextMenu.hxx"
#include "DialogContainer.hxx" #include "DialogContainer.hxx"
@ -23,7 +24,7 @@
#include "EditTextWidget.hxx" #include "EditTextWidget.hxx"
#include "FileListWidget.hxx" #include "FileListWidget.hxx"
#include "FSNode.hxx" #include "FSNode.hxx"
#include "GameList.hxx" #include "MD5.hxx"
#include "OptionsDialog.hxx" #include "OptionsDialog.hxx"
#include "GlobalPropsDialog.hxx" #include "GlobalPropsDialog.hxx"
#include "StellaSettingsDialog.hxx" #include "StellaSettingsDialog.hxx"
@ -46,7 +47,7 @@
TODO: TODO:
- show all files / only ROMs - show all files / only ROMs
- connect to 'matchPattern' - connect to 'matchPattern'
- history of selected folders/files - create lambda filter to pass these to FileListWidget
*/ */
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -246,7 +247,19 @@ const string& LauncherDialog::selectedRom() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& LauncherDialog::selectedRomMD5() const string& LauncherDialog::selectedRomMD5()
{ {
return myList->selectedMD5(); if(currentNode().isDirectory() || !Bankswitch::isValidRomName(currentNode()))
return EmptyString;
// Attempt to conserve memory
if(myMD5List.size() > 500)
myMD5List.clear();
// Lookup MD5, and if not present, cache it
auto iter = myMD5List.find(currentNode().getPath());
if(iter == myMD5List.end())
myMD5List[currentNode().getPath()] = MD5::hash(currentNode());
return myMD5List[currentNode().getPath()];
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -612,7 +625,7 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::loadRom() void LauncherDialog::loadRom()
{ {
const string& result = instance().createConsole(currentNode(), myList->selectedMD5()); const string& result = instance().createConsole(currentNode(), selectedRomMD5());
if(result == EmptyString) if(result == EmptyString)
{ {
instance().settings().setValue("lastrom", myList->getSelectedString()); instance().settings().setValue("lastrom", myList->getSelectedString());

View File

@ -36,6 +36,8 @@ namespace GUI {
class MessageBox; class MessageBox;
} }
#include <unordered_map>
#include "bspf.hxx" #include "bspf.hxx"
#include "Dialog.hxx" #include "Dialog.hxx"
#include "FSNode.hxx" #include "FSNode.hxx"
@ -122,6 +124,7 @@ class LauncherDialog : public Dialog
CheckboxWidget* myAllFiles; CheckboxWidget* myAllFiles;
RomInfoWidget* myRomInfoWidget; RomInfoWidget* myRomInfoWidget;
std::unordered_map<string,string> myMD5List;
int mySelectedItem; int mySelectedItem;

View File

@ -19,7 +19,6 @@ MODULE_OBJS := \
src/gui/FileListWidget.o \ src/gui/FileListWidget.o \
src/gui/Font.o \ src/gui/Font.o \
src/gui/GameInfoDialog.o \ src/gui/GameInfoDialog.o \
src/gui/GameList.o \
src/gui/GlobalPropsDialog.o \ src/gui/GlobalPropsDialog.o \
src/gui/HelpDialog.o \ src/gui/HelpDialog.o \
src/gui/InputDialog.o \ src/gui/InputDialog.o \

View File

@ -19,14 +19,11 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNodeLIBRETRO::FilesystemNodeLIBRETRO() FilesystemNodeLIBRETRO::FilesystemNodeLIBRETRO()
: _name("rom"),
_isDirectory(false),
_isFile(true),
_isValid(true)
{ {
_displayName = "rom";
_path = "";
_isDirectory = false;
_isFile = true;
_isPseudoRoot = false;
_isValid = true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -60,7 +57,7 @@ string FilesystemNodeLIBRETRO::getShortPath() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNodeLIBRETRO:: bool FilesystemNodeLIBRETRO::
getChildren(AbstractFSList& myList, ListMode mode, bool hidden) const getChildren(AbstractFSList& myList, ListMode mode) const
{ {
return false; return false;
} }

View File

@ -20,9 +20,6 @@
#include "FSNode.hxx" #include "FSNode.hxx"
// TODO - fix isFile() functionality so that it actually determines if something
// is a file; for now, it assumes a file if it isn't a directory
/* /*
* Implementation of the Stella file system API based on LIBRETRO API. * Implementation of the Stella file system API based on LIBRETRO API.
* *
@ -37,9 +34,11 @@ class FilesystemNodeLIBRETRO : public AbstractFSNode
explicit FilesystemNodeLIBRETRO(const string& path); explicit FilesystemNodeLIBRETRO(const string& path);
bool exists() const override; bool exists() const override;
const string& getName() const override { return _displayName; } const string& getName() const override { return _name; }
const string& getPath() const override { return _path; } void setName(const string& name) override { _name = name; }
const string& getPath() const override { return _path; }
string getShortPath() const override; string getShortPath() const override;
bool hasParent() const override { return false; }
bool isDirectory() const override { return _isDirectory; } bool isDirectory() const override { return _isDirectory; }
bool isFile() const override { return _isFile; } bool isFile() const override { return _isFile; }
bool isReadable() const override; bool isReadable() const override;
@ -47,13 +46,13 @@ class FilesystemNodeLIBRETRO : public AbstractFSNode
bool makeDir() override; bool makeDir() override;
bool rename(const string& newfile) override; bool rename(const string& newfile) override;
bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const override; bool getChildren(AbstractFSList& list, ListMode mode) const override;
AbstractFSNodePtr getParent() const override; AbstractFSNodePtr getParent() const override;
uInt32 read(ByteBuffer& image) const override; uInt32 read(ByteBuffer& image) const override;
protected: protected:
string _displayName; string _name;
string _path; string _path;
bool _isDirectory; bool _isDirectory;
bool _isFile; bool _isFile;

View File

@ -53,13 +53,13 @@ FilesystemNodePOSIX::FilesystemNodePOSIX()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesystemNodePOSIX::FilesystemNodePOSIX(const string& p, bool verify) FilesystemNodePOSIX::FilesystemNodePOSIX(const string& path, bool verify)
: _isValid(true), : _isValid(true),
_isFile(false), _isFile(false),
_isDirectory(true) _isDirectory(true)
{ {
// Default to home directory // Default to home directory
_path = p.length() > 0 ? p : "~"; _path = path.length() > 0 ? path : "~";
// Expand '~' to the HOME environment variable // Expand '~' to the HOME environment variable
if(_path[0] == '~') if(_path[0] == '~')
@ -99,8 +99,13 @@ string FilesystemNodePOSIX::getShortPath() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNodePOSIX::getChildren(AbstractFSList& myList, ListMode mode, bool FilesystemNodePOSIX::hasParent() const
bool hidden) const {
return _path != "" && _path != ROOT_DIR;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FilesystemNodePOSIX::getChildren(AbstractFSList& myList, ListMode mode) const
{ {
assert(_isDirectory); assert(_isDirectory);
@ -112,12 +117,8 @@ bool FilesystemNodePOSIX::getChildren(AbstractFSList& myList, ListMode mode,
struct dirent* dp; struct dirent* dp;
while ((dp = readdir(dirp)) != nullptr) while ((dp = readdir(dirp)) != nullptr)
{ {
// Skip 'invisible' files if necessary // Ignore all hidden files
if (dp->d_name[0] == '.' && !hidden) if (dp->d_name[0] == '.')
continue;
// Skip '.' and '..' to avoid cycles
if ((dp->d_name[0] == '.' && dp->d_name[1] == 0) || (dp->d_name[0] == '.' && dp->d_name[1] == '.'))
continue; continue;
string newPath(_path); string newPath(_path);

View File

@ -61,9 +61,11 @@ class FilesystemNodePOSIX : public AbstractFSNode
FilesystemNodePOSIX(const string& path, bool verify = true); FilesystemNodePOSIX(const string& path, bool verify = true);
bool exists() const override { return access(_path.c_str(), F_OK) == 0; } bool exists() const override { return access(_path.c_str(), F_OK) == 0; }
const string& getName() const override { return _displayName; } const string& getName() const override { return _displayName; }
void setName(const string& name) override { _displayName = name; }
const string& getPath() const override { return _path; } const string& getPath() const override { return _path; }
string getShortPath() const override; string getShortPath() const override;
bool hasParent() const override;
bool isDirectory() const override { return _isDirectory; } bool isDirectory() const override { return _isDirectory; }
bool isFile() const override { return _isFile; } bool isFile() const override { return _isFile; }
bool isReadable() const override { return access(_path.c_str(), R_OK) == 0; } bool isReadable() const override { return access(_path.c_str(), R_OK) == 0; }
@ -71,7 +73,7 @@ class FilesystemNodePOSIX : public AbstractFSNode
bool makeDir() override; bool makeDir() override;
bool rename(const string& newfile) override; bool rename(const string& newfile) override;
bool getChildren(AbstractFSList& list, ListMode mode, bool hidden) const override; bool getChildren(AbstractFSList& list, ListMode mode) const override;
AbstractFSNodePtr getParent() const override; AbstractFSNodePtr getParent() const override;
protected: protected: