Made the game listview show the actual game names (from the stella.pro file).

Added ability to cache the reading of ROM info, since it can take up to
30 seconds on slower systems.  If such a cache file exists, the display
of ROM info normally takes a few seconds or less.  I plan to add a progress
dialog box for the first case, since even on an Athlon64 3200+, it can still
take almost 10 seconds.  Fortunately, this won't happen very often (only
when you change ROM directories, or do a reload to detect a new game).

Still TODO is add support to FSNode to determine last access time of a
directory.  Then, if the ROM dir has been changed since we last ran Stella,
the program will 'see' that and automatically do a full reload (just like
KStella).  Neat, eh? :)


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@418 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2005-05-11 01:44:39 +00:00
parent 781f8b8d2e
commit ee742cb0a3
5 changed files with 191 additions and 193 deletions

View File

@ -13,29 +13,48 @@
// 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: GameList.cxx,v 1.1 2005-05-10 19:20:43 stephena Exp $ // $Id: GameList.cxx,v 1.2 2005-05-11 01:44:39 stephena Exp $
// //
// Copyright (C) 2005 by Stephen Anthony // Based on code from KStella - Stella frontend
// @author Stephen Anthony // Copyright (C) 2003-2005 Stephen Anthony
//============================================================================ //============================================================================
#include "GuiUtils.hxx"
#include "GameList.hxx" #include "GameList.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GameList::GameList() GameList::GameList()
{ {
cerr << "GameList::GameList()\n";
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GameList::~GameList() GameList::~GameList()
{ {
cerr << "GameList::~GameList()\n";
myArray.clear(); myArray.clear();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void GameList::appendGame(const string& rom, const string& name, const string& note)
{
Entry g;
g._rom = rom;
g._name = name;
g._note = note;
myArray.push_back(g);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void GameList::sortByName() void GameList::sortByName()
{ {
cerr << "GameList::sortByName()\n"; // Simple selection sort
for(uInt32 i = 0; i < myArray.size()-1; i++)
{
uInt32 min = i;
for (uInt32 j = i+1; j < myArray.size(); j++)
if (myArray[j]._name < myArray[min]._name)
min = j;
if (min != i)
SWAP(myArray[min], myArray[i]);
}
} }

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: GameList.hxx,v 1.1 2005-05-10 19:20:43 stephena Exp $ // $Id: GameList.hxx,v 1.2 2005-05-11 01:44:39 stephena Exp $
// //
// Based on code from KStella - Stella frontend // Based on code from KStella - Stella frontend
// Copyright (C) 2003-2005 Stephen Anthony // Copyright (C) 2003-2005 Stephen Anthony
@ -30,24 +30,29 @@
*/ */
class GameList class GameList
{ {
struct Entry { private:
string _rom; struct Entry {
string _md5; string _rom;
string _name; string _name;
string _note; string _note;
}; };
typedef Array<Entry> GameArray; typedef Array<Entry> EntryList;
EntryList myArray;
public: public:
GameList(); GameList();
~GameList(); ~GameList();
void sortByName(); inline const string& rom(Int32 i) { return myArray[i]._rom; }
GameArray& getList() { return myArray; } inline const string& name(Int32 i) { return myArray[i]._name; }
inline const string& note(Int32 i) { return myArray[i]._note; }
inline Int32 size() { return myArray.size(); }
private: void clear() { myArray.clear(); }
GameArray myArray; void appendGame(const string& rom, const string& name, const string& note);
void sortByName();
}; };
#endif #endif

View File

@ -13,14 +13,20 @@
// 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.6 2005-05-10 19:20:44 stephena Exp $ // $Id: LauncherDialog.cxx,v 1.7 2005-05-11 01:44:39 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 <algorithm>
#include <sstream>
#include "OSystem.hxx" #include "OSystem.hxx"
#include "Settings.hxx" #include "Settings.hxx"
#include "PropsSet.hxx"
#include "Props.hxx"
#include "MD5.hxx"
#include "FSNode.hxx" #include "FSNode.hxx"
#include "Widget.hxx" #include "Widget.hxx"
#include "ListWidget.hxx" #include "ListWidget.hxx"
@ -56,8 +62,11 @@ LauncherDialog::LauncherDialog(OSystem* osystem, DialogContainer* parent,
myGameList(NULL) myGameList(NULL)
{ {
// Show game name // Show game name
new StaticTextWidget(this, 10, 8, _w - 20, kLineHeight, new StaticTextWidget(this, 10, 8, 200, kLineHeight,
"Select a game from the list ...", kTextAlignCenter); "Select a game from the list ...", kTextAlignLeft);
myRomCount = new StaticTextWidget(this, _w - 100, 8, 90, kLineHeight,
"", kTextAlignRight);
// Add three buttons at the bottom // Add three buttons at the bottom
const int border = 10; const int border = 10;
@ -81,8 +90,9 @@ LauncherDialog::LauncherDialog(OSystem* osystem, DialogContainer* parent,
myList->setNumberingMode(kListNumberingOff); myList->setNumberingMode(kListNumberingOff);
// Add note textwidget to show any notes for the currently selected ROM // Add note textwidget to show any notes for the currently selected ROM
myNote = new StaticTextWidget(this, 20, _h - 43, w - 20, 16, new StaticTextWidget(this, 20, _h - 43, 30, 16, "Note:", kTextAlignLeft);
"Note: ", kTextAlignLeft); myNote = new StaticTextWidget(this, 50, _h - 43, w - 70, 16,
"", kTextAlignLeft);
// Restore last selection // Restore last selection
/* /*
@ -148,194 +158,166 @@ void LauncherDialog::close()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::updateListing(bool full_reload) void LauncherDialog::updateListing(bool fullReload)
{ {
cerr << "LauncherDialog::updateListing()\n";
// Figure out if the ROM dir has changed since we last accessed it. // 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). // If so, we do a full reload from disk (takes quite some time).
// Otherwise, we can use the cache file (which is much faster). // Otherwise, we can use the cache file (which is much faster).
// FIXME - actually implement the following code // FIXME - actually implement the following code, where we use
bool ROM_DIR_CHANGED = true; // a function to detect last modification time, and compare
// it to the current modification time
/*
if(ROM_DIR_CHANGED) if(ROM_DIR_CHANGED)
loadListFromDisk(); loadListFromDisk();
// else if( ... CACHE_FILE_EXISTS) else if(CACHE_FILE_EXISTS)
// loadListFromCache(); loadListFromCache();
else // we have no other choice else // we have no other choice
loadListFromDisk(); loadListFromDisk();
StringList l;
FilesystemNode dir(myBrowser->getResult());
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();
const ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
ConfigManager::DomainMap::const_iterator iter = domains.begin();
for (iter = domains.begin(); iter != domains.end(); ++iter) {
String name(iter->_value.get("gameid"));
String description(iter->_value.get("description"));
if (name.isEmpty())
name = iter->_key;
if (description.isEmpty()) {
GameSettings g = GameDetector::findGame(name);
if (g.description)
description = g.description;
}
if (!name.isEmpty() && !description.isEmpty()) {
// Insert the game into the launcher list
int pos = 0, size = l.size();
while (pos < size && (scumm_stricmp(description.c_str(), l[pos].c_str()) > 0))
pos++;
l.insert_at(pos, description);
_domains.insert_at(pos, iter->_key);
}
}
*/ */
// Start with empty lists
myGameList->clear();
if(instance()->fileExists("stella.cache") && !fullReload) // FIXME - get name from Settings
loadListFromCache();
else
loadListFromDisk();
// Now fill the list widget with the contents of the GameList
StringList l;
for (Int32 i = 0; i < (Int32) myGameList->size(); ++i)
l.push_back(myGameList->name(i));
myList->setList(l); myList->setList(l);
// Indicate how many files were found
ostringstream buf;
buf << myGameList->size() << " files found";
myRomCount->setLabel(buf.str());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::addGame() void LauncherDialog::loadListFromDisk()
{ {
/* Properties props;
// Allow user to add a new game to the list.
// 1) show a dir selection dialog which lets the user pick the directory
// the game data resides in.
// 2) try to auto detect which game is in the directory, if we cannot
// determine it uniquely preent a list of candidates to the user
// to pick from
// 3) Display the 'Edit' dialog for that item, letting the user specify
// an alternate description (to distinguish multiple versions of the
// game, e.g. 'Monkey German' and 'Monkey English') and set default
// options for that game.
if (_browser->runModal() > 0) { FilesystemNode dir(myBrowser->getResult());
// User made his choice... FSList files = dir.listDir(FilesystemNode::kListAll);
FilesystemNode dir(_browser->getResult());
FSList files = dir.listDir(FilesystemNode::kListAll);
// ...so let's determine a list of candidates, games that // Create a entry for the GameList for each file
// could be contained in the specified directory. string path = dir.path(), rom, md5, name, note;
DetectedGameList candidates(PluginManager::instance().detectGames(files)); for (int idx = 0; idx < (int)files.size(); idx++)
{
int idx; rom = path + files[idx].displayName();
if (candidates.isEmpty()) {
// No game was found in the specified directory
MessageDialog alert("ScummVM could not find any game in the specified directory!");
alert.runModal();
idx = -1;
} else if (candidates.size() == 1) {
// Exact match
idx = 0;
} else {
// Display the candidates to the user and let her/him pick one
StringList list;
for (idx = 0; idx < (int)candidates.size(); idx++)
list.push_back(candidates[idx].description);
ChooserDialog dialog("Pick the game:");
dialog.setList(list);
idx = dialog.runModal();
}
if (0 <= idx && idx < (int)candidates.size()) {
DetectedGame result = candidates[idx];
// The auto detector or the user made a choice. // Calculate the MD5 so we can get the rest of the info
// Pick a domain name which does not yet exist (after all, we // from the PropertiesSet (stella.pro)
// are *adding* a game to the config, not replacing). md5 = MD5FromFile(rom);
String domain(result.name); instance()->propSet().getMD5(md5, props);
if (ConfMan.hasGameDomain(domain)) { name = props.get("Cartridge.Name");
char suffix = 'a'; note = props.get("Cartridge.Note");
domain += suffix;
while (ConfMan.hasGameDomain(domain)) {
assert(suffix < 'z');
domain.deleteLastChar();
suffix++;
domain += suffix;
}
ConfMan.set("gameid", result.name, domain);
ConfMan.set("description", result.description, domain);
}
ConfMan.set("path", dir.path(), domain);
const bool customLanguage = (result.language != Common::UNK_LANG);
const bool customPlatform = (result.platform != Common::kPlatformUnknown);
// Set language if specified // Some games may not have a name, since there may not
if (customLanguage) // be an entry in stella.pro. In that case, we use the rom name
ConfMan.set("language", Common::getLanguageCode(result.language), domain); if(name == "Untitled")
name = files[idx].displayName();
// Set platform if specified myGameList->appendGame(rom, name, note);
if (customPlatform) }
ConfMan.set("platform", Common::getPlatformCode(result.platform), domain);
// Adapt the description string if custom platform/language is set // Sort the list by rom name (since that's what we see in the listview)
if (customLanguage || customPlatform) { myGameList->sortByName();
String desc = result.description;
desc += " (";
if (customLanguage)
desc += Common::getLanguageDescription(result.language);
if (customLanguage && customPlatform)
desc += "/";
if (customPlatform)
desc += Common::getPlatformDescription(result.platform);
desc += ")";
ConfMan.set("description", desc, domain); // And create a cache file, so that the next time Stella starts,
} // we don't have to do this time-consuming operation again
createListCache();
}
// Display edit dialog for the new entry // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EditGameDialog editDialog(domain, result); void LauncherDialog::loadListFromCache()
if (editDialog.runModal() > 0) { {
// User pressed OK, so make changes permanent ifstream in("stella.cache"); // FIXME - get name from Settings
if(!in)
{
loadListFromDisk();
return;
}
// Write config to disk // It seems terribly ugly that we need to use char arrays
ConfMan.flushToDisk(); // instead of strings. Or maybe I don't know the correct way ??
char buf[2048];
string line, rom, name, note;
uInt32 pos1, pos2; // The locations of the two '|' characters
// Update the ListWidget and force a redraw // Keep reading until all lines have been inspected
updateListing(); while(!in.eof())
draw(); {
} else { in.getline(buf, 2048);
// User aborted, remove the the new domain again line = buf;
ConfMan.removeGameDomain(domain);
} // Now split the line into three parts
} pos1 = line.find("|", 0);
} if(pos1 == string::npos) continue;
*/ pos2 = line.find("|", pos1+1);
if(pos2 == string::npos) continue;
rom = line.substr(0, pos1);
name = line.substr(pos1+1, pos2-pos1-1);
note = line.substr(pos2+1);
// Add this game to the list
// We don't do sorting, since it's assumed to be done by loadListFromDisk()
myGameList->appendGame(rom, name, note);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::createListCache()
{
ofstream out("stella.cache"); // FIXME - get name from Settings
// Write the gamelist to the cachefile (sorting is already done)
for (Int32 i = 0; i < (Int32) myGameList->size(); ++i)
{
out << myGameList->rom(i) << "|"
<< myGameList->name(i) << "|"
<< myGameList->note(i)
<< endl;
}
out.close();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string LauncherDialog::MD5FromFile(const string& path)
{
ifstream in(path.c_str(), ios_base::binary);
if(!in)
return "";
uInt8* image = new uInt8[512 * 1024];
in.read((char*)image, 512 * 1024);
uInt32 size = in.gcount();
in.close();
return MD5(image, size);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::handleCommand(CommandSender* sender, uInt32 cmd, uInt32 data) void LauncherDialog::handleCommand(CommandSender* sender, uInt32 cmd, uInt32 data)
{ {
Int32 item;
switch (cmd) switch (cmd)
{ {
case kStartCmd: case kStartCmd:
case kListItemActivatedCmd: case kListItemActivatedCmd:
case kListItemDoubleClickedCmd: case kListItemDoubleClickedCmd:
{ item = myList->getSelected();
if(myList->getSelected() >= 0) if(item >= 0)
{ {
string item = myList->getSelectedString(); instance()->createConsole(myGameList->rom(item));
instance()->createConsole(item);
close(); close();
} }
break; break;
}
case kLocationCmd: case kLocationCmd:
parent()->addDialog(myBrowser); parent()->addDialog(myBrowser);
@ -346,7 +328,9 @@ void LauncherDialog::handleCommand(CommandSender* sender, uInt32 cmd, uInt32 dat
break; break;
case kListSelectionChangedCmd: case kListSelectionChangedCmd:
// cerr << "change note\n"; item = myList->getSelected();
if(item >= 0)
myNote->setLabel(myGameList->note(item));
break; break;
case kQuitCmd: case kQuitCmd:
@ -358,13 +342,3 @@ void LauncherDialog::handleCommand(CommandSender* sender, uInt32 cmd, uInt32 dat
Dialog::handleCommand(sender, cmd, data); Dialog::handleCommand(sender, cmd, data);
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::loadListFromDisk()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::loadListFromCache()
{
}

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.hxx,v 1.3 2005-05-10 19:20:44 stephena Exp $ // $Id: LauncherDialog.hxx,v 1.4 2005-05-11 01:44:39 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,23 +45,23 @@ class LauncherDialog : public Dialog
virtual void handleCommand(CommandSender* sender, uInt32 cmd, uInt32 data); virtual void handleCommand(CommandSender* sender, uInt32 cmd, uInt32 data);
protected: protected:
void updateListing(bool full_reload = false); void updateListing(bool fullReload = false);
void close(); void close();
virtual void addGame();
void removeGame(int item);
void editGame(int item);
void loadConfig(); void loadConfig();
protected: protected:
ListWidget* myList; ListWidget* myList;
BrowserDialog* myBrowser; BrowserDialog* myBrowser;
StaticTextWidget* myNote; StaticTextWidget* myNote;
StaticTextWidget* myRomCount;
GameList* myGameList; GameList* myGameList;
private: private:
void loadListFromDisk(); void loadListFromDisk();
void loadListFromCache(); void loadListFromCache();
void createListCache();
string MD5FromFile(const string& path);
}; };
#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: PopUpWidget.cxx,v 1.5 2005-05-10 19:20:44 stephena Exp $ // $Id: PopUpWidget.cxx,v 1.6 2005-05-11 01:44:39 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
@ -23,8 +23,8 @@
#include "OSystem.hxx" #include "OSystem.hxx"
#include "FrameBuffer.hxx" #include "FrameBuffer.hxx"
#include "Stack.hxx" #include "Stack.hxx"
#include "Menu.hxx"
#include "Dialog.hxx" #include "Dialog.hxx"
#include "DialogContainer.hxx"
#include "GuiUtils.hxx" #include "GuiUtils.hxx"
#include "PopUpWidget.hxx" #include "PopUpWidget.hxx"
@ -102,7 +102,7 @@ void PopUpDialog::handleMouseDown(Int32 x, Int32 y, Int32 button, Int32 clickCou
_popUpBoss->sendCommand(_popUpBoss->_cmd, _selection); _popUpBoss->sendCommand(_popUpBoss->_cmd, _selection);
// We remove the dialog and delete the dialog when the user has selected an item // We remove the dialog and delete the dialog when the user has selected an item
_popUpBoss->instance()->menu().removeDialog(); parent()->removeDialog();
delete this; delete this;
} }
@ -286,7 +286,7 @@ void PopUpWidget::handleMouseDown(Int32 x, Int32 y, Int32 button, Int32 clickCou
if(isEnabled()) if(isEnabled())
{ {
myPopUpDialog = new PopUpDialog(this, x + getAbsX(), y + getAbsY()); myPopUpDialog = new PopUpDialog(this, x + getAbsX(), y + getAbsY());
instance()->menu().addDialog(myPopUpDialog); parent()->addDialog(myPopUpDialog);
} }
} }