From 77e14423b9210287298a49af0653010027931777 Mon Sep 17 00:00:00 2001
From: Aloshi <aloshi@aloshi>
Date: Thu, 25 Oct 2012 12:36:30 -0500
Subject: [PATCH] Added the gameImageNotFound tag. See THEMES.md for details.

---
 THEMES.md                      | 15 ++++++++++++---
 changelog.txt                  |  3 +++
 src/XMLReader.cpp              |  4 ++++
 src/components/GuiGameList.cpp |  9 +++++++--
 src/components/GuiTheme.cpp    | 21 ++++++++++++++++++++-
 src/components/GuiTheme.h      |  9 ++++++---
 6 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/THEMES.md b/THEMES.md
index f312e3cbb..4d79aa620 100644
--- a/THEMES.md
+++ b/THEMES.md
@@ -3,7 +3,7 @@ Themes
 
 EmulationStation allows each system to have its own "theme." A theme is a collection of display settings and images defined in an XML document.
 
-ES will check two places for a theme: first, the system's search directory for theme.xml. Then, if that's not found, $HOME/.emulationstation/es_theme.xml.
+ES will check two places for a theme: first, the system's search directory for theme.xml. Then, if that's not found, $HOME/.emulationstation/es_theme.xml will be used.
 
 Almost all positions, dimensions, origins, etc. work in percentages - that is, they are a decimal between 0 and 1, representing the percentage of the screen on that axis to use. This ensures that themes look similar at every resolution.
 
@@ -56,6 +56,9 @@ Display tags
 ============
 Display tags define some "meta" display attributes about your theme. Display tags must be at the root of the `<theme>` tree - for example, they can't be inside a component tag. They are not required.
 
+
+**Game list attributes:**
+
 `<listPrimaryColor>` - the hex font color to use for games on the GuiGameList.
 
 `<listSecondaryColor>` - the hex font color to use for folders on the GuiGameList.
@@ -78,15 +81,21 @@ Display tags define some "meta" display attributes about your theme. Display tag
 
 ~~`<gameImageOffsetY>` - the percentage to offset the displayed game image by. Default is the height of the header font.~~
 
+
+
+**Game image attributes:**
+
 `<gameImagePos>` - two values for the position of the game art, in the form of `[x] [y]`, as a percentage. Default is `$infoWidth/2 $headerHeight`. 
 
 `<gameImageDim>` - two values for the dimensions of the game art, in the form of `[width] [height]`, as a percentage of the screen. Default is `$infoWidth/2 0` (width fits within the info column). The image will only be resized if at least one axis is nonzero *and* exceeded by the image's size. You should always leave at least one axis as zero to preserve the aspect ratio.
 
-`<gameImageOrigin>` two values for the origin of the game art, in the form of `[x] [y]`, as a percentage. Default is `0.5 0` (top-center of the image).
+`<gameImageOrigin>` - two values for the origin of the game art, in the form of `[x] [y]`, as a percentage. Default is `0.5 0` (top-center of the image).
+
+`<gameImageNotFound>` - path to the image to display if a game's image is missing. '.' and '~' are expanded.
 
 
 
-**The Fast Select box can be themed with these tags:**
+**Fast Select box attributes:**
 
 `<fastSelectColor>` - the hex color to use for the letter display on the Fast Select box.
 
diff --git a/changelog.txt b/changelog.txt
index 53dd6ac62..c0f1af6a3 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,6 @@
+October 25
+-Added gameImageNotFound tag for an image to display if a game image is not found/defined.
+
 October 17
 -Added GuiAnimation class which animates its children.
 -The game image on the game list now slides/fades in and out.
diff --git a/src/XMLReader.cpp b/src/XMLReader.cpp
index 3132a7319..67ba20c11 100644
--- a/src/XMLReader.cpp
+++ b/src/XMLReader.cpp
@@ -168,6 +168,10 @@ void parseGamelist(SystemData* system)
 					newImage.erase(0, 1);
 					newImage.insert(0, system->getRootFolder()->getPath());
 				}
+
+				//if the image doesn't exist, forget it
+				if(!boost::filesystem::exists(newImage))
+					newImage = "";
 			}
 
 			game->set(newName, newDesc, newImage);
diff --git a/src/components/GuiGameList.cpp b/src/components/GuiGameList.cpp
index c011e80cd..f47f5d684 100644
--- a/src/components/GuiGameList.cpp
+++ b/src/components/GuiGameList.cpp
@@ -23,7 +23,7 @@ GuiGameList::GuiGameList(bool useDetail)
 	{
 		mList = new GuiList<FileData*>(Renderer::getScreenWidth() * sInfoWidth, Renderer::getDefaultFont(Renderer::LARGE)->getHeight() + 2);
 
-		mScreenshot = new GuiImage(Renderer::getScreenWidth() * sInfoWidth * 0.5, Renderer::getScreenHeight() * mTheme->getGameImageOffsetY(), "", Renderer::getScreenWidth() * sInfoWidth * 0.7, 0, false);
+		mScreenshot = new GuiImage(Renderer::getScreenWidth() * mTheme->getGameImageOffsetX(), Renderer::getScreenHeight() * mTheme->getGameImageOffsetY(), "", Renderer::getScreenWidth() * sInfoWidth * 0.7, 0, false);
 		mScreenshot->setOrigin(mTheme->getGameImageOriginX(), mTheme->getGameImageOriginY());
 		//addChild(mScreenshot);
 
@@ -248,7 +248,12 @@ void GuiGameList::updateDetailData()
 	if(mList->getSelectedObject() && !mList->getSelectedObject()->isFolder())
 	{
 		mScreenshot->setOffset((mTheme->getGameImageOffsetX() - 0.05) * Renderer::getScreenWidth(), mTheme->getGameImageOffsetY() * Renderer::getScreenHeight());
-		mScreenshot->setImage(((GameData*)mList->getSelectedObject())->getImagePath());
+
+		if(((GameData*)mList->getSelectedObject())->getImagePath().empty())
+			mScreenshot->setImage(mTheme->getImageNotFoundPath());
+		else
+			mScreenshot->setImage(((GameData*)mList->getSelectedObject())->getImagePath());
+
 		mImageAnimation->fadeIn(15);
 		mImageAnimation->move((int)(0.05 * Renderer::getScreenWidth()), 0, 5);
 	}else{
diff --git a/src/components/GuiTheme.cpp b/src/components/GuiTheme.cpp
index f51709c5d..aa7d9fd6e 100644
--- a/src/components/GuiTheme.cpp
+++ b/src/components/GuiTheme.cpp
@@ -34,6 +34,8 @@ float GuiTheme::getGameImageHeight() { return mGameImageHeight; }
 float GuiTheme::getGameImageOriginX() { return mGameImageOriginX; }
 float GuiTheme::getGameImageOriginY() { return mGameImageOriginY; }
 
+std::string GuiTheme::getImageNotFoundPath() { return mImageNotFoundPath; }
+
 GuiTheme::GuiTheme(std::string path)
 {
 	setDefaults();
@@ -81,6 +83,8 @@ void GuiTheme::setDefaults()
 	mMenuSelectSound.loadFile("");
 	mMenuBackSound.loadFile("");
 	mMenuOpenSound.loadFile("");
+
+	mImageNotFoundPath = "";
 }
 
 void GuiTheme::deleteComponents()
@@ -148,7 +152,6 @@ void GuiTheme::readXML(std::string path)
 	mListOffsetX = strToFloat(root.child("listOffsetX").text().get(), mListOffsetX);
 	mListTextOffsetX = strToFloat(root.child("listTextOffsetX").text().get(), mListTextOffsetX);
 
-
 	//game image stuff
 	std::string artPos = root.child("gameImagePos").text().get();
 	std::string artDim = root.child("gameImageDim").text().get();
@@ -166,12 +169,17 @@ void GuiTheme::readXML(std::string path)
 	mGameImageOriginX = resolveExp(artOriginX, mGameImageOriginX);
 	mGameImageOriginY = resolveExp(artOriginY, mGameImageOriginY);
 
+	mImageNotFoundPath = expandPath(root.child("gameImageNotFound").text().get());
+
 	//sounds
 	mMenuScrollSound.loadFile(expandPath(root.child("menuScrollSound").text().get()));
 	mMenuSelectSound.loadFile(expandPath(root.child("menuSelectSound").text().get()));
 	mMenuBackSound.loadFile(expandPath(root.child("menuBackSound").text().get()));
 	mMenuOpenSound.loadFile(expandPath(root.child("menuOpenSound").text().get()));
 
+	//fonts
+	//mListFont = resolveFont(root.child("listFont"), NULL);
+
 	//actually read the components
 	createComponentChildren(root, this);
 }
@@ -322,3 +330,14 @@ float GuiTheme::strToFloat(std::string str, float defaultVal)
 	ss >> ret;
 	return ret;
 }
+
+Font* GuiTheme::resolveFont(pugi::xml_node node, Font* def)
+{
+	if(!node)
+		return def;
+
+	std::string path = expandPath(node.child("path").text().get());
+	int size = (int)(strToFloat(node.child("size").value()) * Renderer::getScreenHeight());
+
+	return new Font(path, size);
+}
diff --git a/src/components/GuiTheme.h b/src/components/GuiTheme.h
index 9d2036bf2..fd7538875 100644
--- a/src/components/GuiTheme.h
+++ b/src/components/GuiTheme.h
@@ -5,6 +5,7 @@
 #include "../pugiXML/pugixml.hpp"
 #include "GuiBox.h"
 #include "../Sound.h"
+#include "../Font.h"
 
 //This class loads an XML-defined list of GuiComponents.
 class GuiTheme : public GuiComponent
@@ -41,6 +42,8 @@ public:
 	Sound* getMenuSelectSound();
 	Sound* getMenuBackSound();
 	Sound* getMenuOpenSound();
+
+	std::string getImageNotFoundPath();
 private:
 	void setDefaults();
 	void deleteComponents();
@@ -53,17 +56,17 @@ private:
 	unsigned int resolveColor(std::string str, unsigned int defaultColor = 0x000000FF);
 	void splitString(std::string str, char delim, std::string* before, std::string* after);
 	float strToFloat(std::string str, float defaultVal = 0.0f);
+	Font* resolveFont(pugi::xml_node node, Font* def);
 
 	std::vector<GuiComponent*> mComponentVector;
 	std::string mPath;
+
 	unsigned int mListPrimaryColor, mListSecondaryColor, mListSelectorColor, mListSelectedColor, mDescColor, mFastSelectColor;
 	bool mHideHeader, mHideDividers, mListCentered;
-
 	float mListOffsetX, mListTextOffsetX, mGameImageOffsetX, mGameImageOffsetY, mGameImageWidth, mGameImageHeight, mGameImageOriginX, mGameImageOriginY;
-
 	GuiBoxData mBoxData;
-
 	Sound mMenuScrollSound, mMenuSelectSound, mMenuBackSound, mMenuOpenSound;
+	std::string mImageNotFoundPath;
 };
 
 #endif