Merge branch 'master' into feature/filesystem

This commit is contained in:
Stephen Anthony 2022-08-14 11:40:30 -02:30
commit d6b88336cf
7 changed files with 144 additions and 133 deletions

View File

@ -314,7 +314,7 @@ bool FileListWidget::handleKeyDown(StellaKey key, StellaMod mod)
{ {
handled = true; handled = true;
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
cerr << " " << mod << ", " << key << endl; cerr << " FileListWidget::handleKeyDown " << mod << ", " << key << endl;
#endif #endif
switch(key) switch(key)
{ {

View File

@ -476,7 +476,7 @@ void LauncherDialog::tick()
reload(); reload();
if(myPendingRomInfo && myRomInfoTime < TimerManager::getTicks() / 1000) if(myPendingRomInfo && myRomInfoTime < TimerManager::getTicks() / 1000)
loadRomInfo(true); loadPendingRomInfo();
Dialog::tick(); Dialog::tick();
} }
@ -515,7 +515,7 @@ void LauncherDialog::loadConfig()
} }
Dialog::setFocus(getFocusList()[mySelectedItem]); Dialog::setFocus(getFocusList()[mySelectedItem]);
if(myRomInfoWidget) if(myRomImageWidget && myRomInfoWidget)
{ {
myRomImageWidget->reloadProperties(currentNode()); myRomImageWidget->reloadProperties(currentNode());
myRomInfoWidget->reloadProperties(currentNode()); myRomInfoWidget->reloadProperties(currentNode());
@ -551,10 +551,7 @@ void LauncherDialog::updateUI()
<< (myShortCount ? " items" : " items found"); << (myShortCount ? " items" : " items found");
myRomCount->setLabel(buf.str()); myRomCount->setLabel(buf.str());
// Update ROM info UI item, delayed loadRomInfo();
myRomInfoTime = TimerManager::getTicks() / 1000 + 250; // TODO: define delay
myPendingRomInfo = true;
loadRomInfo(false);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -673,23 +670,41 @@ void LauncherDialog::setRomInfoFont(const Common::Size& area)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void LauncherDialog::loadRomInfo(bool complete) void LauncherDialog::loadRomInfo()
{ {
myPendingRomInfo = !complete; if(!myRomImageWidget || !myRomInfoWidget)
return;
if(!myRomImageWidget || !myROMInfoFont) // Update ROM info UI item, delayed
myRomInfoTime = TimerManager::getTicks() / 1000 + 250; // TODO: define pending load delay
myPendingRomInfo = true;
const string& md5 = selectedRomMD5();
if(md5 != EmptyString)
{
myRomImageWidget->setProperties(currentNode(), md5, false);
myRomInfoWidget->setProperties(currentNode(), md5, false);
}
else
{
myRomImageWidget->clearProperties();
myRomInfoWidget->clearProperties();
}
}
// --------------------------------------
void LauncherDialog::loadPendingRomInfo()
{
myPendingRomInfo = false;
if(!myRomImageWidget || !myRomInfoWidget)
return; return;
const string& md5 = selectedRomMD5(); const string& md5 = selectedRomMD5();
if(md5 != EmptyString) if(md5 != EmptyString)
{ {
myRomImageWidget->setProperties(currentNode(), md5, complete); myRomImageWidget->setProperties(currentNode(), md5);
myRomInfoWidget->setProperties(currentNode(), md5); // TODO: skip controller detector? myRomInfoWidget->setProperties(currentNode(), md5);
}
else if(!complete)
{
myRomImageWidget->clearProperties();
myRomInfoWidget->clearProperties();
} }
} }

View File

@ -146,7 +146,8 @@ class LauncherDialog : public Dialog, CommandSender
void setRomInfoFont(const Common::Size& area); void setRomInfoFont(const Common::Size& area);
void loadRom(); void loadRom();
void loadRomInfo(bool complete); void loadRomInfo();
void loadPendingRomInfo();
void openSettings(); void openSettings();
void openGameProperties(); void openGameProperties();
void openContextMenu(int x = -1, int y = -1); void openContextMenu(int x = -1, int y = -1);

View File

@ -15,8 +15,8 @@
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================ //============================================================================
#include <regex>
#include "EventHandler.hxx" #include "EventHandler.hxx"
#include "FrameBuffer.hxx"
#include "Dialog.hxx" #include "Dialog.hxx"
#include "FBSurface.hxx" #include "FBSurface.hxx"
#include "Font.hxx" #include "Font.hxx"
@ -24,7 +24,6 @@
#include "PNGLibrary.hxx" #include "PNGLibrary.hxx"
#include "Props.hxx" #include "Props.hxx"
#include "PropsSet.hxx" #include "PropsSet.hxx"
#include "bspf.hxx"
#include "RomImageWidget.hxx" #include "RomImageWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -40,7 +39,7 @@ RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomImageWidget::setProperties(const FSNode& node, const string& md5, bool complete) void RomImageWidget::setProperties(const FSNode& node, const string& md5, bool full)
{ {
myHaveProperties = true; myHaveProperties = true;
@ -52,7 +51,7 @@ void RomImageWidget::setProperties(const FSNode& node, const string& md5, bool c
// Decide whether the information should be shown immediately // Decide whether the information should be shown immediately
if(instance().eventHandler().state() == EventHandlerState::LAUNCHER) if(instance().eventHandler().state() == EventHandlerState::LAUNCHER)
parseProperties(node, complete); parseProperties(node, full);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -74,11 +73,11 @@ void RomImageWidget::reloadProperties(const FSNode& node)
// by saving a different image or through a change in video renderer, // by saving a different image or through a change in video renderer,
// so we reload the properties // so we reload the properties
if(myHaveProperties) if(myHaveProperties)
parseProperties(node, true); parseProperties(node);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomImageWidget::parseProperties(const FSNode& node, bool complete) void RomImageWidget::parseProperties(const FSNode& node, bool full)
{ {
if(myNavSurface == nullptr) if(myNavSurface == nullptr)
{ {
@ -112,47 +111,49 @@ void RomImageWidget::parseProperties(const FSNode& node, bool complete)
}); });
} }
// Initialize to empty properties entry #ifdef PNG_SUPPORT
mySurfaceErrorMsg = ""; if(!full)
mySurfaceIsValid = false;
#ifdef PNG_SUPPORT
// TODO: RETRON_77
// Get a valid filename representing a snapshot file for this rom and load the snapshot
myImageList.clear();
myImageIdx = 0;
if(complete)
{ {
// Try to load snapshots by property name and ROM file name myImageIdx = 0;
getImageList(myProperties.get(PropType::Cart_Name), node.getNameWithExt()); myImageList.clear();
mySurfaceErrorMsg = "";
if(myImageList.size()) // Get a valid filename representing a snapshot file for this rom and load the snapshot
mySurfaceIsValid = loadPng(myImageList[0].getPath()); const string& path = instance().snapshotLoadDir().getPath();
// 1. Try to load first snapshot by property name
string fileName = path + myProperties.get(PropType::Cart_Name) + ".png";
mySurfaceIsValid = loadPng(fileName);
if(!mySurfaceIsValid)
{
// 2. If none exists, try to load first snapshot by ROM file name
fileName = path + node.getNameWithExt("png");
mySurfaceIsValid = loadPng(fileName);
}
if(mySurfaceIsValid)
myImageList.emplace_back(fileName);
else
// 3. If no ROM snapshots exist, try to load a default snapshot
mySurfaceIsValid = loadPng(path + "default_snapshot.png");
} }
else else
{ {
const string& path = instance().snapshotLoadDir().getPath(); const string& oldFileName = myImageList.size() ? myImageList[0].getPath() : "";
string filename = path + myProperties.get(PropType::Cart_Name) + ".png";
mySurfaceIsValid = loadPng(filename); // Try to find all snapshots by property and ROM file name
if(!mySurfaceIsValid) myImageList.clear();
getImageList(myProperties.get(PropType::Cart_Name), node.getNameWithExt());
// The first file found before must not be the first file now, if files by
// property *and* ROM name are found
if(myImageList.size() && myImageList[0].getPath() != oldFileName)
{ {
filename = path + node.getNameWithExt("png"); mySurfaceErrorMsg = "";
mySurfaceIsValid = loadPng(filename); mySurfaceIsValid = loadPng(myImageList[0].getPath());
} }
if(mySurfaceIsValid)
myImageList.emplace_back(filename);
} }
if(!mySurfaceIsValid)
{
// If no ROM snapshots exist, try to load a default snapshot
mySurfaceIsValid = loadPng(instance().snapshotLoadDir().getPath() + "default_snapshot.png");
}
#else #else
mySurfaceIsValid = false;
mySurfaceErrorMsg = "PNG image loading not supported"; mySurfaceErrorMsg = "PNG image loading not supported";
#endif #endif
if(mySurface) if(mySurface)
@ -174,29 +175,21 @@ bool RomImageWidget::changeImage(int direction)
#ifdef PNG_SUPPORT #ifdef PNG_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RomImageWidget::getImageList(const string& propname, const string& filename) bool RomImageWidget::getImageList(const string& propName, const string& romName)
{ {
// TODO: search for consecutive digits and letters instead of getChildren const std::regex symbols{R"([-[\]{}()*+?.,\^$|#])"}; // \s
//std::ifstream in(filename, std::ios_base::binary); const string rgxPropName = std::regex_replace(propName, symbols, R"(\$&)");
//if(!in.is_open()) const string rgxRomName = std::regex_replace(romName, symbols, R"(\$&)");
// loadImageERROR("No snapshot found"); // Look for <name.png> or <name_#.png> (# is a number)
// or use a timer before updating the images const std::regex rgx("^(" + rgxPropName + "|" + rgxRomName + ")(_\\d+){0,1}\\.png$");
const string pngPropName = propname + ".png";
const string pngFileName = filename + ".png";
FSNode::NameFilter filter = ([&](const FSNode& node) FSNode::NameFilter filter = ([&](const FSNode& node)
{ {
const string& nodeName = node.getName(); return std::regex_match(node.getName(), rgx);
return
(nodeName == pngPropName || nodeName == pngFileName ||
(nodeName.find(propname + " #") == 0 &&
nodeName.find(".png") == nodeName.length() - 4) ||
(nodeName.find(filename + " #") == 0 &&
nodeName.find(".png") == nodeName.length() - 4));
} }
); );
// Find all images matching the filename and the extension // Find all images matching the given names and the extension
FSNode node(instance().snapshotLoadDir().getPath()); FSNode node(instance().snapshotLoadDir().getPath());
node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false); node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false);
@ -212,12 +205,12 @@ bool RomImageWidget::getImageList(const string& propname, const string& filename
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RomImageWidget::loadPng(const string& filename) bool RomImageWidget::loadPng(const string& fileName)
{ {
try try
{ {
VariantList comments; VariantList comments;
instance().png().loadImage(filename, *mySurface, comments); instance().png().loadImage(fileName, *mySurface, comments);
// Scale surface to available image area // Scale surface to available image area
const Common::Rect& src = mySurface->srcRect(); const Common::Rect& src = mySurface->srcRect();
@ -271,7 +264,7 @@ void RomImageWidget::drawWidget(bool hilite)
FBSurface& s = dialog().surface(); FBSurface& s = dialog().surface();
const int yoff = myImageHeight; const int yoff = myImageHeight;
s.fillRect(_x+1, _y+1, _w-2, _h-1, _bgcolor); s.fillRect(_x+1, _y+1, _w-2, _h-2, _bgcolor);
s.frameRect(_x, _y, _w, myImageHeight, kColor); s.frameRect(_x, _y, _w, myImageHeight, kColor);
if(!myHaveProperties) if(!myHaveProperties)

View File

@ -22,7 +22,6 @@ class FBSurface;
class Properties; class Properties;
#include "Widget.hxx" #include "Widget.hxx"
#include "bspf.hxx"
class RomImageWidget : public Widget, public CommandSender class RomImageWidget : public Widget, public CommandSender
{ {
@ -36,7 +35,7 @@ class RomImageWidget : public Widget, public CommandSender
return font.getFontHeight() * 9 / 8; return font.getFontHeight() * 9 / 8;
} }
void setProperties(const FSNode& node, const string& md5, bool complete); void setProperties(const FSNode& node, const string& md5, bool full = true);
void clearProperties(); void clearProperties();
void reloadProperties(const FSNode& node); void reloadProperties(const FSNode& node);
bool changeImage(int direction = 1); bool changeImage(int direction = 1);
@ -49,10 +48,10 @@ class RomImageWidget : public Widget, public CommandSender
#endif #endif
private: private:
void parseProperties(const FSNode& node, bool complete); void parseProperties(const FSNode& node, bool full = true);
#ifdef PNG_SUPPORT #ifdef PNG_SUPPORT
bool getImageList(const string& propname, const string& filename); bool getImageList(const string& propName, const string& romName);
bool loadPng(const string& filename); bool loadPng(const string& fileName);
#endif #endif
private: private:

View File

@ -16,7 +16,6 @@
//============================================================================ //============================================================================
#include "EventHandler.hxx" #include "EventHandler.hxx"
#include "FrameBuffer.hxx"
#include "Dialog.hxx" #include "Dialog.hxx"
#include "FBSurface.hxx" #include "FBSurface.hxx"
#include "Font.hxx" #include "Font.hxx"
@ -40,7 +39,7 @@ RomInfoWidget::RomInfoWidget(GuiObject* boss, const GUI::Font& font,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomInfoWidget::setProperties(const FSNode& node, const string& md5) void RomInfoWidget::setProperties(const FSNode& node, const string& md5, bool full)
{ {
myHaveProperties = true; myHaveProperties = true;
@ -52,7 +51,7 @@ void RomInfoWidget::setProperties(const FSNode& node, const string& md5)
// Decide whether the information should be shown immediately // Decide whether the information should be shown immediately
if(instance().eventHandler().state() == EventHandlerState::LAUNCHER) if(instance().eventHandler().state() == EventHandlerState::LAUNCHER)
parseProperties(node); parseProperties(node, full);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -77,7 +76,7 @@ void RomInfoWidget::reloadProperties(const FSNode& node)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomInfoWidget::parseProperties(const FSNode& node) void RomInfoWidget::parseProperties(const FSNode& node, bool full)
{ {
// Initialize to empty properties entry // Initialize to empty properties entry
myRomInfo.clear(); myRomInfo.clear();
@ -97,63 +96,68 @@ void RomInfoWidget::parseProperties(const FSNode& node)
myRomInfo.push_back("Rarity: " + value); myRomInfo.push_back("Rarity: " + value);
if((value = myProperties.get(PropType::Cart_Note)) != EmptyString) if((value = myProperties.get(PropType::Cart_Note)) != EmptyString)
myRomInfo.push_back("Note: " + value); myRomInfo.push_back("Note: " + value);
const bool swappedPorts = myProperties.get(PropType::Console_SwapPorts) == "YES";
if(full)
// Load the image for controller and bankswitch type auto detection
string left = myProperties.get(PropType::Controller_Left);
string right = myProperties.get(PropType::Controller_Right);
const Controller::Type leftType = Controller::getType(left);
const Controller::Type rightType = Controller::getType(right);
string bsDetected = myProperties.get(PropType::Cart_Type);
bool isPlusCart = false;
size_t size = 0;
try
{ {
ByteBuffer image; const bool swappedPorts = myProperties.get(PropType::Console_SwapPorts) == "YES";
string md5 = "";
if(node.exists() && !node.isDirectory() && // Load the image for controller and bankswitch type auto detection
(image = instance().openROM(node, md5, size)) != nullptr) string left = myProperties.get(PropType::Controller_Left);
string right = myProperties.get(PropType::Controller_Right);
const Controller::Type leftType = Controller::getType(left);
const Controller::Type rightType = Controller::getType(right);
string bsDetected = myProperties.get(PropType::Cart_Type);
bool isPlusCart = false;
size_t size = 0;
try
{ {
Logger::debug(myProperties.get(PropType::Cart_Name) + ":"); ByteBuffer image;
left = ControllerDetector::detectName(image, size, leftType, string md5 = "";
if(node.exists() && !node.isDirectory() &&
(image = instance().openROM(node, md5, size)) != nullptr)
{
Logger::debug(myProperties.get(PropType::Cart_Name) + ":");
left = ControllerDetector::detectName(image, size, leftType,
!swappedPorts ? Controller::Jack::Left : Controller::Jack::Right, !swappedPorts ? Controller::Jack::Left : Controller::Jack::Right,
instance().settings()); instance().settings());
right = ControllerDetector::detectName(image, size, rightType, right = ControllerDetector::detectName(image, size, rightType,
!swappedPorts ? Controller::Jack::Right : Controller::Jack::Left, !swappedPorts ? Controller::Jack::Right : Controller::Jack::Left,
instance().settings()); instance().settings());
if (bsDetected == "AUTO") if(bsDetected == "AUTO")
bsDetected = Bankswitch::typeToName(CartDetector::autodetectType(image, size)); bsDetected = Bankswitch::typeToName(CartDetector::autodetectType(image, size));
isPlusCart = CartDetector::isProbablyPlusROM(image, size); isPlusCart = CartDetector::isProbablyPlusROM(image, size);
}
} }
} catch(const runtime_error&)
catch(const runtime_error&)
{
// Do nothing; we simply don't update the controllers if openROM
// failed for any reason
left = right = "";
}
if(left != "" && right != "")
myRomInfo.push_back("Controllers: " + (left + " (left), " + right + " (right)"));
if(bsDetected != "")
{
ostringstream buf;
// Display actual ROM size in developer mode
if(instance().settings().getBool("dev.settings"))
{ {
buf << " - "; // Do nothing; we simply don't update the controllers if openROM
if(size < 1_KB) // failed for any reason
buf << size << "B"; left = right = "";
else }
buf << (std::round(size / static_cast<float>(1_KB))) << "K"; if(left != "" && right != "")
myRomInfo.push_back("Controllers: " + (left + " (left), " + right + " (right)"));
if(bsDetected != "")
{
ostringstream buf;
// Display actual ROM size in developer mode
if(instance().settings().getBool("dev.settings"))
{
buf << " - ";
if(size < 1_KB)
buf << size << "B";
else
buf << (std::round(size / static_cast<float>(1_KB))) << "K";
}
myRomInfo.push_back("Type: " + Bankswitch::typeToDesc(Bankswitch::nameToType(bsDetected))
+ (isPlusCart ? " - PlusROM" : "")
+ buf.str());
} }
myRomInfo.push_back("Type: " + Bankswitch::typeToDesc(Bankswitch::nameToType(bsDetected))
+ (isPlusCart ? " - PlusROM" : "")
+ buf.str());
} }
setDirty(); setDirty();
} }

View File

@ -22,7 +22,6 @@ class FBSurface;
class Properties; class Properties;
#include "Widget.hxx" #include "Widget.hxx"
#include "bspf.hxx"
class RomInfoWidget : public Widget, public CommandSender class RomInfoWidget : public Widget, public CommandSender
{ {
@ -36,7 +35,7 @@ class RomInfoWidget : public Widget, public CommandSender
int x, int y, int w, int h); int x, int y, int w, int h);
~RomInfoWidget() override = default; ~RomInfoWidget() override = default;
void setProperties(const FSNode& node, const string& md5); void setProperties(const FSNode& node, const string& md5, bool full = true);
void clearProperties(); void clearProperties();
void reloadProperties(const FSNode& node); void reloadProperties(const FSNode& node);
@ -47,7 +46,7 @@ class RomInfoWidget : public Widget, public CommandSender
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override; void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
private: private:
void parseProperties(const FSNode& node); void parseProperties(const FSNode& node, bool full = true);
private: private:
// Some ROM properties info, as well as 'tEXt' chunks from the PNG image // Some ROM properties info, as well as 'tEXt' chunks from the PNG image