mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-18 07:05:39 +00:00
Font size is now dependent on resolution width.
Scrolling will now occur if the input is held (not just keyboards anymore!). Initial XML gamelist support. If a file named gamelist.xml is present in the directory ES is run from, it will be parsed and the detailed GuiGameList will be used. Games are matched by absolute path, and a name, description, and image path can be read. PugiXML is used for parsing XML files - its license can be found in src/pugiXML/pugiXML_license.txt. SDL_image is used for loading screenshots with the detailed GuiGameList. Almost all invalid bash characters should be escaped in ROM paths now - including !$^&*()[]<>?;'"\.
This commit is contained in:
parent
fbfa0d436c
commit
4f99dec7c2
4
Makefile
4
Makefile
|
@ -1,7 +1,7 @@
|
|||
CC=g++
|
||||
CFLAGS=-c -Wall
|
||||
LDFLAGS=-lSDL -lSDL_ttf -lboost_system -lboost_filesystem
|
||||
SRCSOURCES=main.cpp Renderer.cpp Renderer_draw.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp components/GuiTitleScreen.cpp components/GuiList.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp
|
||||
LDFLAGS=-lSDL -lSDL_ttf -lSDL_image -lboost_system -lboost_filesystem
|
||||
SRCSOURCES=main.cpp Renderer.cpp Renderer_draw.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp XMLReader.cpp components/GuiList.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp components/GuiImage.cpp pugiXML/pugixml.cpp
|
||||
SOURCES=$(addprefix src/,$(SRCSOURCES))
|
||||
OBJECTS=$(SOURCES:.cpp=.o)
|
||||
EXECUTABLE=emulationstation
|
||||
|
|
|
@ -1,28 +1,53 @@
|
|||
#include "GameData.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <iostream>
|
||||
|
||||
bool GameData::isFolder() { return false; }
|
||||
std::string GameData::getName() { return mName; }
|
||||
std::string GameData::getPath() { return mPath; }
|
||||
std::string GameData::getDescription() { return mDescription; }
|
||||
std::string GameData::getImagePath() { return mImagePath; }
|
||||
|
||||
GameData::GameData(SystemData* system, std::string path, std::string name)
|
||||
{
|
||||
mSystem = system;
|
||||
mPath = path;
|
||||
mName = name;
|
||||
|
||||
mDescription = "";
|
||||
mImagePath = "";
|
||||
}
|
||||
|
||||
std::string GameData::getPath()
|
||||
std::string GameData::getBashPath()
|
||||
{
|
||||
//a quick and dirty way to insert a backslash before most characters that would mess up a bash path
|
||||
std::string path = mPath;
|
||||
const char* invalidChars = " '\"\\!$^&*(){}[]?;<>";
|
||||
for(unsigned int i = 0; i < path.length(); i++)
|
||||
{
|
||||
if(path[i] == *" " || path[i] == *"'" || path[i] == *"\"" || path[i] == *"\\")
|
||||
{
|
||||
path.insert(i, "\\");
|
||||
i++;
|
||||
}
|
||||
char c;
|
||||
unsigned int charNum = 0;
|
||||
do {
|
||||
c = invalidChars[charNum];
|
||||
if(path[i] == c)
|
||||
{
|
||||
path.insert(i, "\\");
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
charNum++;
|
||||
} while(c != '\0');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void GameData::set(std::string name, std::string description, std::string imagePath)
|
||||
{
|
||||
if(!name.empty())
|
||||
mName = name;
|
||||
if(!description.empty())
|
||||
mDescription = description;
|
||||
if(!imagePath.empty() && boost::filesystem::exists(imagePath))
|
||||
mImagePath = imagePath;
|
||||
}
|
||||
|
|
|
@ -10,13 +10,24 @@ class GameData : public FileData
|
|||
public:
|
||||
GameData(SystemData* system, std::string path, std::string name);
|
||||
|
||||
void set(std::string name = "", std::string description = "", std::string imagePath = "");
|
||||
|
||||
std::string getName();
|
||||
std::string getPath();
|
||||
std::string getBashPath();
|
||||
|
||||
std::string getDescription();
|
||||
std::string getImagePath();
|
||||
|
||||
bool isFolder();
|
||||
private:
|
||||
SystemData* mSystem;
|
||||
std::string mPath;
|
||||
std::string mName;
|
||||
|
||||
//extra data
|
||||
std::string mDescription;
|
||||
std::string mImagePath;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,6 +2,24 @@
|
|||
#include "Renderer.h"
|
||||
#include <iostream>
|
||||
|
||||
std::vector<GuiComponent*> GuiComponent::sComponentVector;
|
||||
|
||||
GuiComponent::GuiComponent()
|
||||
{
|
||||
sComponentVector.push_back(this);
|
||||
}
|
||||
|
||||
GuiComponent::~GuiComponent()
|
||||
{
|
||||
for(unsigned int i = 0; i < sComponentVector.size(); i++)
|
||||
{
|
||||
if(sComponentVector.at(i) == this)
|
||||
{
|
||||
sComponentVector.erase(sComponentVector.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuiComponent::addChild(GuiComponent* comp)
|
||||
{
|
||||
mChildren.push_back(comp);
|
||||
|
@ -21,6 +39,14 @@ void GuiComponent::removeChild(GuiComponent* comp)
|
|||
std::cerr << "Error - tried to remove GuiComponent child, but couldn't find it!\n";
|
||||
}
|
||||
|
||||
void GuiComponent::processTicks(int deltaTime)
|
||||
{
|
||||
for(unsigned int i = 0; i < sComponentVector.size(); i++)
|
||||
{
|
||||
sComponentVector.at(i)->onTick(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void GuiComponent::render()
|
||||
{
|
||||
onRender();
|
||||
|
@ -30,3 +56,4 @@ void GuiComponent::render()
|
|||
mChildren.at(i)->render();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,16 +8,23 @@
|
|||
class GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiComponent();
|
||||
virtual ~GuiComponent();
|
||||
|
||||
void render();
|
||||
virtual void onRender() { };
|
||||
virtual void onTick(int deltaTime) { };
|
||||
|
||||
virtual void onInput(InputManager::InputButton button, bool keyDown) { };
|
||||
virtual unsigned int getLayer() { return BIT(0); };
|
||||
|
||||
void addChild(GuiComponent* comp);
|
||||
void removeChild(GuiComponent* comp);
|
||||
unsigned int getChildCount() { return mChildren.size(); }
|
||||
GuiComponent* getChild(unsigned int i) { return mChildren.at(i); }
|
||||
|
||||
static void processTicks(int deltaTime);
|
||||
private:
|
||||
static std::vector<GuiComponent*> sComponentVector;
|
||||
std::vector<GuiComponent*> mChildren;
|
||||
};
|
||||
|
||||
|
|
|
@ -34,14 +34,9 @@ void Renderer::deleteAll()
|
|||
|
||||
void Renderer::render()
|
||||
{
|
||||
for(unsigned int layer = 0; layer < LAYER_COUNT; layer++)
|
||||
for(unsigned int i = 0; i < renderVector.size(); i++)
|
||||
{
|
||||
unsigned int layerBit = BIT(layer);
|
||||
for(unsigned int i = 0; i < renderVector.size(); i++)
|
||||
{
|
||||
if(renderVector.at(i)->getLayer() & layerBit)
|
||||
renderVector.at(i)->render();
|
||||
}
|
||||
renderVector.at(i)->render();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,18 @@ namespace Renderer
|
|||
unsigned int getScreenWidth();
|
||||
unsigned int getScreenHeight();
|
||||
|
||||
enum FontSize { SMALL, MEDIUM, LARGE };
|
||||
bool loadFonts();
|
||||
extern bool loadedFonts;
|
||||
extern TTF_Font* fonts[3]; //should be FontSize size but I don't remember the syntax
|
||||
extern int fontHeight[3]; //same
|
||||
int getFontHeight(FontSize size); //sometimes font size is needed before fonts have been loaded; this takes care of that
|
||||
|
||||
//drawing commands
|
||||
void drawRect(int x, int y, int w, int h, int color);
|
||||
void drawText(std::string text, int x, int y, int color);
|
||||
void drawCenteredText(std::string text, int y, int color);
|
||||
|
||||
void loadFonts();
|
||||
void drawText(std::string text, int x, int y, int color, FontSize fontsize = MEDIUM);
|
||||
void drawCenteredText(std::string text, int xOffset, int y, int color, FontSize fontsize = MEDIUM);
|
||||
void drawWrappedText(std::string text, int xStart, int yStart, int xLen, int color, FontSize fontsize = MEDIUM);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
#include <SDL/SDL.h>
|
||||
#include <SDL/SDL_ttf.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
SDL_Surface* Renderer::screen;
|
||||
|
||||
TTF_Font* Renderer::font;
|
||||
bool Renderer::loadedFonts = false;
|
||||
TTF_Font* Renderer::fonts[3];
|
||||
int Renderer::fontHeight[3];
|
||||
|
||||
void Renderer::drawRect(int x, int y, int h, int w, int color)
|
||||
{
|
||||
|
@ -13,22 +15,47 @@ void Renderer::drawRect(int x, int y, int h, int w, int color)
|
|||
SDL_FillRect(Renderer::screen, &rect, color);
|
||||
}
|
||||
|
||||
void Renderer::loadFonts()
|
||||
bool Renderer::loadFonts()
|
||||
{
|
||||
font = TTF_OpenFont("LinLibertine_R.ttf", 36);
|
||||
if(!font)
|
||||
const char* fontPath = "LinLibertine_R.ttf";
|
||||
|
||||
//int sizeArray[] = {Renderer::getScreenHeight() * 0.025, Renderer::getScreenHeight() * 0.05, Renderer::getScreenHeight() * 0.075};
|
||||
int sizeArray[] = {Renderer::getScreenWidth() * 0.015, Renderer::getScreenWidth() * 0.03, Renderer::getScreenWidth() * 0.05};
|
||||
|
||||
|
||||
//the three here should be the font count but, again, I don't remember the syntax
|
||||
for(unsigned int i = 0; i < 3; i++)
|
||||
{
|
||||
std::cerr << "Error - could not load font!\n";
|
||||
std::cerr << TTF_GetError() << "\n";
|
||||
return;
|
||||
TTF_Font* font = TTF_OpenFont(fontPath, sizeArray[i]);
|
||||
if(!font)
|
||||
{
|
||||
std::cerr << "Error - could not load font!\n";
|
||||
std::cerr << TTF_GetError() << "\n";
|
||||
return false;
|
||||
}
|
||||
fonts[i] = font;
|
||||
TTF_SizeText(font, "HEIGHT", NULL, &fontHeight[i]); //gets the height of the string "HEIGHT" in this font (in pixels)
|
||||
}
|
||||
|
||||
loadedFonts = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Renderer::drawText(std::string text, int x, int y, int color)
|
||||
int Renderer::getFontHeight(FontSize size)
|
||||
{
|
||||
if(!font)
|
||||
if(!loadedFonts)
|
||||
loadFonts();
|
||||
|
||||
return fontHeight[size];
|
||||
}
|
||||
|
||||
void Renderer::drawText(std::string text, int x, int y, int color, FontSize fontsize)
|
||||
{
|
||||
if(!loadedFonts)
|
||||
loadFonts();
|
||||
|
||||
TTF_Font* font = fonts[fontsize];
|
||||
|
||||
//SDL_Color is a struct of four bytes, with the first three being colors. An int is four bytes.
|
||||
//So, we can just pretend the int is an SDL_Color.
|
||||
SDL_Color* sdlcolor = (SDL_Color*)&color;
|
||||
|
@ -46,16 +73,72 @@ void Renderer::drawText(std::string text, int x, int y, int color)
|
|||
SDL_FreeSurface(textSurf);
|
||||
}
|
||||
|
||||
void Renderer::drawCenteredText(std::string text, int y, int color)
|
||||
void Renderer::drawCenteredText(std::string text, int xOffset, int y, int color, FontSize fontsize)
|
||||
{
|
||||
if(!font)
|
||||
if(!loadedFonts)
|
||||
loadFonts();
|
||||
|
||||
TTF_Font* font = fonts[fontsize];
|
||||
|
||||
int w, h;
|
||||
TTF_SizeText(font, text.c_str(), &w, &h);
|
||||
|
||||
int x = (int)getScreenWidth() - w;
|
||||
x *= 0.5;
|
||||
|
||||
drawText(text, x, y, color);
|
||||
x += xOffset * 0.5;
|
||||
|
||||
drawText(text, x, y, color, fontsize);
|
||||
}
|
||||
|
||||
//this could probably be optimized
|
||||
void Renderer::drawWrappedText(std::string text, int xStart, int yStart, int xLen, int color, FontSize fontsize)
|
||||
{
|
||||
if(!loadedFonts)
|
||||
loadFonts();
|
||||
|
||||
TTF_Font* font = fonts[fontsize];
|
||||
|
||||
int y = yStart;
|
||||
|
||||
std::string line, word, temp;
|
||||
int w, h;
|
||||
size_t space;
|
||||
|
||||
while(text.length() > 0 || !line.empty())
|
||||
{
|
||||
space = text.find(' ', 0);
|
||||
if(space == std::string::npos)
|
||||
space = text.length() - 1;
|
||||
|
||||
word = text.substr(0, space + 1);
|
||||
text.erase(0, space + 1);
|
||||
|
||||
temp = line + word;
|
||||
|
||||
TTF_SizeText(font, temp.c_str(), &w, &h);
|
||||
|
||||
|
||||
if(w <= xLen && text.length() == 0)
|
||||
{
|
||||
line = temp;
|
||||
word = "";
|
||||
}
|
||||
|
||||
if(w > xLen || text.length() == 0)
|
||||
{
|
||||
//render line now
|
||||
drawText(line, xStart, y, color, fontsize);
|
||||
|
||||
//increment y by height and some extra padding for the next line
|
||||
y += h + 4;
|
||||
|
||||
//draw the word we skipped on the next line
|
||||
line = word;
|
||||
}else{
|
||||
//there's still space, continue
|
||||
line = temp;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ void SystemData::launchGame(GameData* game)
|
|||
|
||||
std::string command = mLaunchCommand;
|
||||
|
||||
command = strreplace(command, "%ROM%", game->getPath());
|
||||
command = strreplace(command, "%ROM%", game->getBashPath());
|
||||
|
||||
std::cout << " " << command << "\n";
|
||||
std::cout << "=====================================================\n";
|
||||
|
|
89
src/XMLReader.cpp
Normal file
89
src/XMLReader.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include "XMLReader.h"
|
||||
#include "SystemData.h"
|
||||
#include "GameData.h"
|
||||
#include "pugiXML/pugixml.hpp"
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
//this is obviously an incredibly inefficient way to go about searching
|
||||
//some day I may change this to use hash tables or something
|
||||
//but I don't think it'll matter too much with the size of most collections
|
||||
GameData* searchFolderByPath(FolderData* folder, std::string const& path)
|
||||
{
|
||||
for(unsigned int i = 0; i < folder->getFileCount(); i++)
|
||||
{
|
||||
FileData* file = folder->getFile(i);
|
||||
|
||||
if(file->isFolder())
|
||||
{
|
||||
GameData* result = searchFolderByPath((FolderData*)file, path);
|
||||
if(result)
|
||||
return (GameData*)result;
|
||||
}else{
|
||||
if(file->getPath() == path)
|
||||
return (GameData*)file;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void parseXMLFile(std::string xmlpath)
|
||||
{
|
||||
std::cout << "Parsing XML file \"" << xmlpath << "\"...\n";
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
|
||||
|
||||
if(!result)
|
||||
{
|
||||
std::cerr << "Error parsing XML file \"" << xmlpath << "\"!\n";
|
||||
std::cerr << " " << result.description() << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.child("gameList");
|
||||
if(!root)
|
||||
{
|
||||
std::cerr << "Error - could not find <gameList> node in XML document!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
for(pugi::xml_node gameNode = root.child("game"); gameNode; gameNode = gameNode.next_sibling("game"))
|
||||
{
|
||||
pugi::xml_node pathNode = gameNode.child("path");
|
||||
if(!pathNode)
|
||||
{
|
||||
std::cerr << "Error - <game> node contains no <path> child!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::string path = pathNode.text().get();
|
||||
|
||||
GameData* game = NULL;
|
||||
for(unsigned int i = 0; i < SystemData::sSystemVector.size(); i++)
|
||||
{
|
||||
game = searchFolderByPath(SystemData::sSystemVector.at(i)->getRootFolder(), path);
|
||||
if(game != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if(game == NULL)
|
||||
{
|
||||
std::cerr << "Error - game of path \"" << path << "\" was not found by the system's search. Ignoring.\n";
|
||||
}else{
|
||||
//actually gather the information in the XML doc, then pass it to the game's set method
|
||||
std::string newName, newDesc, newImage;
|
||||
|
||||
if(gameNode.child("name"))
|
||||
newName = gameNode.child("name").text().get();
|
||||
if(gameNode.child("desc"))
|
||||
newDesc = gameNode.child("desc").text().get();
|
||||
if(gameNode.child("image"))
|
||||
newImage = gameNode.child("image").text().get();
|
||||
|
||||
game->set(newName, newDesc, newImage);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "XML parsing complete.\n";
|
||||
}
|
9
src/XMLReader.h
Normal file
9
src/XMLReader.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef _XMLREADER_H_
|
||||
#define _XMLREADER_H_
|
||||
|
||||
#include <string>
|
||||
class SystemData;
|
||||
|
||||
void parseXMLFile(std::string xmlpath);
|
||||
|
||||
#endif
|
|
@ -2,11 +2,28 @@
|
|||
#include "../InputManager.h"
|
||||
#include <iostream>
|
||||
|
||||
GuiGameList::GuiGameList()
|
||||
#define SCREENSHOTWIDTH 256
|
||||
#define SCREENSHOTHEIGHT 256
|
||||
|
||||
GuiGameList::GuiGameList(bool useDetail)
|
||||
{
|
||||
std::cout << "Creating GuiGameList\n";
|
||||
mDetailed = useDetail;
|
||||
|
||||
//The GuiGameList can use the older, simple game list if so desired.
|
||||
//The old view only shows a list in the center of the screen; the new view can display a screenshto and description.
|
||||
//Those with smaller displays may prefer the older view.
|
||||
if(mDetailed)
|
||||
{
|
||||
mList = new GuiList(Renderer::getScreenWidth() * 0.4, Renderer::getFontHeight(Renderer::LARGE) + 2);
|
||||
|
||||
mScreenshot = new GuiImage(Renderer::getScreenWidth() * 0.2 - (SCREENSHOTWIDTH / 2), Renderer::getFontHeight(Renderer::LARGE) + 2);
|
||||
addChild(mScreenshot);
|
||||
}else{
|
||||
mList = new GuiList(0, Renderer::getFontHeight(Renderer::LARGE) + 2);
|
||||
mScreenshot = NULL;
|
||||
}
|
||||
|
||||
mList = new GuiList();
|
||||
addChild(mList);
|
||||
|
||||
setSystemId(0);
|
||||
|
@ -20,6 +37,11 @@ GuiGameList::~GuiGameList()
|
|||
Renderer::unregisterComponent(this);
|
||||
delete mList;
|
||||
|
||||
if(mDetailed)
|
||||
{
|
||||
delete mScreenshot;
|
||||
}
|
||||
|
||||
InputManager::unregisterComponent(this);
|
||||
}
|
||||
|
||||
|
@ -52,7 +74,23 @@ void GuiGameList::onRender()
|
|||
{
|
||||
Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0xFFFFFF);
|
||||
|
||||
Renderer::drawCenteredText(mSystem->getName(), 2, 0x0000FF);
|
||||
Renderer::drawCenteredText(mSystem->getName(), 0, 1, 0x0000FF, Renderer::LARGE);
|
||||
|
||||
|
||||
if(mDetailed)
|
||||
{
|
||||
Renderer::drawRect(Renderer::getScreenWidth() * 0.4, Renderer::getFontHeight(Renderer::LARGE) + 2, 8, Renderer::getScreenHeight(), 0x0000FF);
|
||||
|
||||
//if we have selected a non-folder
|
||||
if(mList->getSelectedObject() && !((FileData*)mList->getSelectedObject())->isFolder())
|
||||
{
|
||||
GameData* game = (GameData*)mList->getSelectedObject();
|
||||
|
||||
std::string desc = game->getDescription();
|
||||
if(!desc.empty())
|
||||
Renderer::drawWrappedText(desc, 2, Renderer::getFontHeight(Renderer::LARGE) + SCREENSHOTHEIGHT + 12, Renderer::getScreenWidth() * 0.4, 0xFF0000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuiGameList::onInput(InputManager::InputButton button, bool keyDown)
|
||||
|
@ -74,7 +112,6 @@ void GuiGameList::onInput(InputManager::InputButton button, bool keyDown)
|
|||
}
|
||||
}
|
||||
|
||||
//std::cout << "mFolderStack.size(): " << mFolderStack.size() << "\n";
|
||||
if(button == InputManager::BUTTON2 && keyDown && mFolderStack.size())
|
||||
{
|
||||
mFolder = mFolderStack.top();
|
||||
|
@ -90,10 +127,26 @@ void GuiGameList::onInput(InputManager::InputButton button, bool keyDown)
|
|||
{
|
||||
setSystemId(mSystemId - 1);
|
||||
}
|
||||
|
||||
if(mDetailed)
|
||||
{
|
||||
if(!keyDown && (button == InputManager::UP || button == InputManager::DOWN))
|
||||
{
|
||||
if(mList->getSelectedObject() && !((FileData*)mList->getSelectedObject())->isFolder())
|
||||
{
|
||||
mScreenshot->setImage(((GameData*)mList->getSelectedObject())->getImagePath());
|
||||
}else{
|
||||
mScreenshot->setImage("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuiGameList::updateList()
|
||||
{
|
||||
if(mDetailed)
|
||||
mScreenshot->setImage("");
|
||||
|
||||
mList->clear();
|
||||
|
||||
for(unsigned int i = 0; i < mFolder->getFileCount(); i++)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "../GuiComponent.h"
|
||||
#include "GuiList.h"
|
||||
#include "GuiImage.h"
|
||||
#include <string>
|
||||
#include <stack>
|
||||
#include "../SystemData.h"
|
||||
|
@ -12,7 +13,7 @@
|
|||
class GuiGameList : GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiGameList();
|
||||
GuiGameList(bool useDetail = false);
|
||||
~GuiGameList();
|
||||
|
||||
void updateList();
|
||||
|
@ -26,7 +27,10 @@ private:
|
|||
FolderData* mFolder;
|
||||
std::stack<FolderData*> mFolderStack;
|
||||
int mSystemId;
|
||||
bool mDetailed;
|
||||
|
||||
GuiList* mList;
|
||||
GuiImage* mScreenshot;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
44
src/components/GuiImage.cpp
Normal file
44
src/components/GuiImage.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "GuiImage.h"
|
||||
#include <SDL/SDL_image.h>
|
||||
#include <iostream>
|
||||
|
||||
GuiImage::GuiImage(int offsetX, int offsetY, std::string path)
|
||||
{
|
||||
mSurface = NULL;
|
||||
SDL_Rect newRect = {offsetX, offsetY, 0, 0};
|
||||
mRect = newRect;
|
||||
|
||||
if(!path.empty())
|
||||
setImage(path);
|
||||
}
|
||||
|
||||
GuiImage::~GuiImage()
|
||||
{
|
||||
if(mSurface)
|
||||
SDL_FreeSurface(mSurface);
|
||||
}
|
||||
|
||||
void GuiImage::setImage(std::string path)
|
||||
{
|
||||
//if we already have an image, clear it
|
||||
if(mSurface)
|
||||
{
|
||||
SDL_FreeSurface(mSurface);
|
||||
mSurface = NULL;
|
||||
}
|
||||
|
||||
if(!path.empty())
|
||||
{
|
||||
mSurface = IMG_Load(path.c_str());
|
||||
|
||||
if(mSurface == NULL)
|
||||
std::cerr << "Error loading image \"" << path.c_str() << "\"\n";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void GuiImage::onRender()
|
||||
{
|
||||
if(mSurface)
|
||||
SDL_BlitSurface(mSurface, NULL, Renderer::screen, &mRect);
|
||||
}
|
22
src/components/GuiImage.h
Normal file
22
src/components/GuiImage.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef _GUIIMAGE_H_
|
||||
#define _GUIIMAGE_H_
|
||||
|
||||
#include "../GuiComponent.h"
|
||||
#include <SDL/SDL.h>
|
||||
#include <string>
|
||||
|
||||
class GuiImage : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiImage(int offsetX = 0, int offsetY = 0, std::string path = "");
|
||||
~GuiImage();
|
||||
|
||||
void setImage(std::string path);
|
||||
|
||||
void onRender();
|
||||
private:
|
||||
SDL_Surface* mSurface;
|
||||
SDL_Rect mRect;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -38,15 +38,18 @@ void GuiInputConfig::onRender()
|
|||
{
|
||||
Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0xFFFFFF);
|
||||
|
||||
Renderer::drawCenteredText("It looks like you have a joystick plugged in!", 2, 0x000000);
|
||||
Renderer::drawCenteredText("POV hats (some D-Pads) are automatically mapped to directions.", 90, 0x000000);
|
||||
Renderer::drawCenteredText("You can press a keyboard key to skip any input.", 130, 0x000000);
|
||||
Renderer::drawCenteredText("If you want to remap later, just delete ~/.es_input.cfg.", 170, 0x000000);
|
||||
int height = Renderer::getFontHeight(Renderer::MEDIUM) + 6;
|
||||
|
||||
Renderer::drawCenteredText("It looks like you have a joystick plugged in!", 0, 2, 0x000000);
|
||||
Renderer::drawCenteredText("POV hats (some D-Pads) are automatically mapped to directions.", 0, height, 0x000000);
|
||||
Renderer::drawCenteredText("You can press a keyboard key to skip any input.", 0, height * 2, 0x000000);
|
||||
Renderer::drawCenteredText("If you want to remap later, just delete ~/.es_input.cfg.", 0, height * 3, 0x000000);
|
||||
Renderer::drawCenteredText("Remember - you'll need to set up your emulator separately!", 0, Renderer::getScreenHeight() - height, 0x000000);
|
||||
|
||||
if(mDone)
|
||||
Renderer::drawCenteredText("All done! Press a keyboard key to continue.", 250, 0x00BB00);
|
||||
Renderer::drawCenteredText("All done! Press a keyboard key to continue.", 0, height * 5, 0x00BB00);
|
||||
else
|
||||
Renderer::drawCenteredText("Please press the axis/button for " + sInputs[mInputNum], 250, 0x00C000);
|
||||
Renderer::drawCenteredText("Please press the axis/button for " + sInputs[mInputNum], 0, height * 5, 0x00C000);
|
||||
}
|
||||
|
||||
void GuiInputConfig::onInput(InputManager::InputButton button, bool keyDown)
|
||||
|
@ -71,7 +74,7 @@ void GuiInputConfig::onInput(InputManager::InputButton button, bool keyDown)
|
|||
if(event->type == SDL_JOYBUTTONDOWN)
|
||||
{
|
||||
mButtonMap[event->jbutton.button] = (InputManager::InputButton)mInputNum;
|
||||
std::cout << " Mapping " << sInputs[mInputNum] << " to button " << event->jbutton.button << "\n";
|
||||
std::cout << " Mapping " << sInputs[mInputNum] << " to button " << (int)event->jbutton.button << "\n";
|
||||
mInputNum++;
|
||||
}
|
||||
|
||||
|
@ -90,13 +93,13 @@ void GuiInputConfig::onInput(InputManager::InputButton button, bool keyDown)
|
|||
mAxisPosMap[event->jaxis.axis] = (InputManager::InputButton)mInputNum;
|
||||
mInputNum++;
|
||||
mLastAxis = event->jaxis.axis;
|
||||
std::cout << " Mapping " << sInputs[mInputNum] << " to axis+ " << mLastAxis << "\n";
|
||||
std::cout << " Mapping " << sInputs[mInputNum - 1] << " to axis+ " << mLastAxis << "\n";
|
||||
}else if(event->jaxis.value < -InputManager::deadzone)
|
||||
{
|
||||
mAxisNegMap[event->jaxis.axis] = (InputManager::InputButton)mInputNum;
|
||||
mInputNum++;
|
||||
mLastAxis = event->jaxis.axis;
|
||||
std::cout << " Mapping " << sInputs[mInputNum] << " to axis- " << mLastAxis << "\n";
|
||||
std::cout << " Mapping " << sInputs[mInputNum - 1] << " to axis- " << mLastAxis << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
#include "GuiList.h"
|
||||
#include <iostream>
|
||||
|
||||
GuiList::GuiList()
|
||||
GuiList::GuiList(int offsetX, int offsetY)
|
||||
{
|
||||
mSelection = 0;
|
||||
mScrollDir = 0;
|
||||
mScrolling = 0;
|
||||
mScrollAccumulator = 0;
|
||||
|
||||
mOffsetX = offsetX;
|
||||
mOffsetY = offsetY;
|
||||
|
||||
InputManager::registerComponent(this);
|
||||
}
|
||||
|
||||
|
@ -14,8 +21,10 @@ GuiList::~GuiList()
|
|||
|
||||
void GuiList::onRender()
|
||||
{
|
||||
const int cutoff = 40;
|
||||
const int entrySize = 40;
|
||||
Renderer::FontSize fontsize = Renderer::MEDIUM;
|
||||
|
||||
const int cutoff = mOffsetY;
|
||||
const int entrySize = Renderer::getFontHeight(fontsize) + 5;
|
||||
|
||||
int startEntry = 0;
|
||||
|
||||
|
@ -33,11 +42,10 @@ void GuiList::onRender()
|
|||
}
|
||||
|
||||
int y = cutoff;
|
||||
int color = 0xFF0000;
|
||||
|
||||
if(mRowVector.size() == 0)
|
||||
{
|
||||
Renderer::drawCenteredText("The list is empty.", y, color);
|
||||
Renderer::drawCenteredText("The list is empty.", 0, y, 0xFF0000);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -49,24 +57,40 @@ void GuiList::onRender()
|
|||
{
|
||||
if(mSelection == i)
|
||||
{
|
||||
Renderer::drawRect(0, y, Renderer::getScreenWidth(), 52, 0x000000);
|
||||
Renderer::drawRect(mOffsetX, y, Renderer::getScreenWidth(), Renderer::getFontHeight(fontsize), 0x000000);
|
||||
}
|
||||
|
||||
ListRow row = mRowVector.at((unsigned int)i);
|
||||
Renderer::drawCenteredText(row.name, y, row.color);
|
||||
y += 40;
|
||||
Renderer::drawCenteredText(row.name, mOffsetX, y, row.color);
|
||||
y += entrySize;
|
||||
}
|
||||
}
|
||||
|
||||
void GuiList::onInput(InputManager::InputButton button, bool keyDown)
|
||||
{
|
||||
if(mRowVector.size() > 0 && keyDown)
|
||||
if(mRowVector.size() > 0)
|
||||
{
|
||||
if(button == InputManager::DOWN)
|
||||
mSelection++;
|
||||
if(keyDown)
|
||||
{
|
||||
if(button == InputManager::DOWN)
|
||||
{
|
||||
mScrollDir = 1;
|
||||
mSelection++;
|
||||
}
|
||||
|
||||
if(button == InputManager::UP)
|
||||
mSelection--;
|
||||
if(button == InputManager::UP)
|
||||
{
|
||||
mScrollDir = -1;
|
||||
mSelection--;
|
||||
}
|
||||
}else{
|
||||
if((button == InputManager::DOWN && mScrollDir > 0) || (button == InputManager::UP && mScrollDir < 0))
|
||||
{
|
||||
mScrollAccumulator = 0;
|
||||
mScrolling = false;
|
||||
mScrollDir = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(mSelection < 0)
|
||||
mSelection += mRowVector.size();
|
||||
|
@ -76,6 +100,40 @@ void GuiList::onInput(InputManager::InputButton button, bool keyDown)
|
|||
}
|
||||
}
|
||||
|
||||
void GuiList::onTick(int deltaTime)
|
||||
{
|
||||
if(mScrollDir != 0)
|
||||
{
|
||||
mScrollAccumulator += deltaTime;
|
||||
|
||||
if(!mScrolling)
|
||||
{
|
||||
if(mScrollAccumulator >= SCROLLDELAY)
|
||||
{
|
||||
mScrollAccumulator = SCROLLTIME;
|
||||
mScrolling = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(mScrolling)
|
||||
{
|
||||
mScrollAccumulator += deltaTime;
|
||||
|
||||
while(mScrollAccumulator >= SCROLLTIME)
|
||||
{
|
||||
mScrollAccumulator -= SCROLLTIME;
|
||||
|
||||
mSelection += mScrollDir;
|
||||
if(mSelection < 0)
|
||||
mSelection += mRowVector.size();
|
||||
if(mSelection >= (int)mRowVector.size())
|
||||
mSelection -= mRowVector.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//list management stuff
|
||||
void GuiList::addObject(std::string name, void* obj, int color)
|
||||
{
|
||||
ListRow row = {name, obj, color};
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#define SCROLLDELAY 507
|
||||
#define SCROLLTIME (57*6)
|
||||
|
||||
struct ListRow
|
||||
{
|
||||
std::string name;
|
||||
|
@ -18,10 +21,11 @@ struct ListRow
|
|||
class GuiList : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiList();
|
||||
GuiList(int offsetX = 0, int offsetY = 0);
|
||||
~GuiList();
|
||||
|
||||
void onRender();
|
||||
void onTick(int deltaTime);
|
||||
void onInput(InputManager::InputButton button, bool keyDown);
|
||||
|
||||
void addObject(std::string name, void* obj, int color = 0xFF0000);
|
||||
|
@ -31,6 +35,10 @@ public:
|
|||
void* getSelectedObject();
|
||||
int getSelection();
|
||||
private:
|
||||
int mScrollDir, mScrollAccumulator;
|
||||
bool mScrolling;
|
||||
|
||||
int mOffsetX, mOffsetY;
|
||||
std::vector<ListRow> mRowVector;
|
||||
int mSelection;
|
||||
};
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#include "GuiTitleScreen.h"
|
||||
#include "../Renderer.h"
|
||||
#include <iostream>
|
||||
|
||||
GuiTitleScreen::GuiTitleScreen()
|
||||
{
|
||||
Renderer::registerComponent(this);
|
||||
|
||||
//add children here
|
||||
}
|
||||
|
||||
void GuiTitleScreen::onRender()
|
||||
{
|
||||
Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0xFFFFFF);
|
||||
Renderer::drawCenteredText("EmulationStation", 5, 0x00FF00);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef _GUITITLESCREEN_H_
|
||||
#define _GUITITLESCREEN_H_
|
||||
|
||||
#include "../GuiComponent.h"
|
||||
|
||||
class GuiTitleScreen : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiTitleScreen();
|
||||
void onRender();
|
||||
};
|
||||
|
||||
#endif
|
28
src/main.cpp
28
src/main.cpp
|
@ -6,6 +6,7 @@
|
|||
#include "SystemData.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "components/GuiInputConfig.h"
|
||||
#include "XMLReader.h"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
@ -44,7 +45,7 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
|
||||
Renderer::screen = SDL_SetVideoMode(width, height, 16, SDL_SWSURFACE | SDL_FULLSCREEN);
|
||||
Renderer::screen = SDL_SetVideoMode(width, height, 16, SDL_SWSURFACE); //| SDL_FULLSCREEN);
|
||||
|
||||
if(Renderer::screen == NULL)
|
||||
{
|
||||
|
@ -57,7 +58,6 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
SDL_ShowCursor(false);
|
||||
SDL_EnableKeyRepeat(500, 100);
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
|
||||
if(!boost::filesystem::exists(SystemData::getConfigPath()))
|
||||
|
@ -75,23 +75,33 @@ int main(int argc, char* argv[])
|
|||
std::cerr << "You should probably go read that, or delete it.\n";
|
||||
running = false;
|
||||
}else{
|
||||
|
||||
bool useDetail = false;
|
||||
|
||||
//loaded system config, so try parsing the test XML doc
|
||||
if(boost::filesystem::exists("gamelist.xml"))
|
||||
{
|
||||
parseXMLFile("gamelist.xml");
|
||||
useDetail = true;
|
||||
}
|
||||
|
||||
if(boost::filesystem::exists(InputManager::getConfigPath()))
|
||||
{
|
||||
InputManager::loadConfig();
|
||||
new GuiGameList();
|
||||
new GuiGameList(useDetail);
|
||||
}else{
|
||||
if(SDL_NumJoysticks() > 0)
|
||||
{
|
||||
new GuiInputConfig();
|
||||
}else{
|
||||
std::cout << "Note - it looks like you have no joysticks connected.\n";
|
||||
new GuiGameList();
|
||||
new GuiGameList(useDetail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int lastTime = 0;
|
||||
while(running)
|
||||
{
|
||||
SDL_Event event;
|
||||
|
@ -104,8 +114,6 @@ int main(int argc, char* argv[])
|
|||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
case SDL_KEYDOWN:
|
||||
InputManager::processEvent(&event);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
InputManager::processEvent(&event);
|
||||
break;
|
||||
|
@ -116,6 +124,12 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
|
||||
int curTime = SDL_GetTicks();
|
||||
int deltaTime = curTime - lastTime;
|
||||
lastTime = curTime;
|
||||
|
||||
GuiComponent::processTicks(deltaTime);
|
||||
|
||||
Renderer::render();
|
||||
SDL_Flip(Renderer::screen);
|
||||
}
|
||||
|
|
27
src/pugiXML/pugiXML_license.txt
Normal file
27
src/pugiXML/pugiXML_license.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
pugixml 1.2 - an XML processing library
|
||||
|
||||
|
||||
This library is distributed under the MIT License:
|
||||
|
||||
Copyright (c) 2006-2012 Arseny Kapoulkine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
69
src/pugiXML/pugiconfig.hpp
Normal file
69
src/pugiXML/pugiconfig.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* pugixml parser - version 1.2
|
||||
* --------------------------------------------------------
|
||||
* Copyright (C) 2006-2012, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
||||
* Report bugs and download new versions at http://pugixml.org/
|
||||
*
|
||||
* This library is distributed under the MIT License. See notice at the end
|
||||
* of this file.
|
||||
*
|
||||
* This work is based on the pugxml parser, which is:
|
||||
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
|
||||
*/
|
||||
|
||||
#ifndef HEADER_PUGICONFIG_HPP
|
||||
#define HEADER_PUGICONFIG_HPP
|
||||
|
||||
// Uncomment this to enable wchar_t mode
|
||||
// #define PUGIXML_WCHAR_MODE
|
||||
|
||||
// Uncomment this to disable XPath
|
||||
// #define PUGIXML_NO_XPATH
|
||||
|
||||
// Uncomment this to disable STL
|
||||
// #define PUGIXML_NO_STL
|
||||
|
||||
// Uncomment this to disable exceptions
|
||||
// #define PUGIXML_NO_EXCEPTIONS
|
||||
|
||||
// Set this to control attributes for public classes/functions, i.e.:
|
||||
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
|
||||
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
|
||||
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
|
||||
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
|
||||
|
||||
// Uncomment this to switch to header-only version
|
||||
// #define PUGIXML_HEADER_ONLY
|
||||
// #include "pugixml.cpp"
|
||||
|
||||
// Tune these constants to adjust memory-related behavior
|
||||
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
|
||||
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
|
||||
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Copyright (c) 2006-2012 Arseny Kapoulkine
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
10250
src/pugiXML/pugixml.cpp
Normal file
10250
src/pugiXML/pugixml.cpp
Normal file
File diff suppressed because it is too large
Load diff
1265
src/pugiXML/pugixml.hpp
Normal file
1265
src/pugiXML/pugixml.hpp
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue