Commit ed8db326 authored by Bkg2k's avatar Bkg2k Committed by MikaXII

Resolve "Add demo mode"

parent 3b687934
......@@ -2,6 +2,7 @@ project("emulationstation")
set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/MameNameMap.h
${CMAKE_CURRENT_SOURCE_DIR}/src/DemoMode.h
${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.h
${CMAKE_CURRENT_SOURCE_DIR}/src/ItemType.h
${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.h
......@@ -65,6 +66,7 @@ set(ES_HEADERS
)
set(ES_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/DemoMode.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/FolderData.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RootFolderData.cpp
......
//
// Created by bkg2k on 10/03/19.
//
#include "DemoMode.h"
#include "Log.h"
DemoMode::DemoMode(Window& window)
: mWindow(window),
mSettings(*Settings::getInstance()),
mRecalboxConf(*RecalboxConf::getInstance()),
mRandomGenerator(mRandomRevice())
{
// Default duration
mDefaultDuration = (int)mRecalboxConf.getUInt("global.demo.duration", 90);
// Build system list filtered by user config
const std::vector<SystemData*>& allSystems = SystemData::getAllSystems();
bool systemListExists = !mRecalboxConf.get("global.demo.systemlist").empty();
for(int i=(int)allSystems.size(); --i>= 0;)
{
const std::string& name = allSystems[i]->getName();
bool includeSystem = mRecalboxConf.getBool(name + ".demo.include");
bool systemIsInIncludeList = !systemListExists || mRecalboxConf.isInList("global.demo.systemlist", name);
if ((includeSystem || systemIsInIncludeList) &&
(allSystems[i]->getRootFolder()->countAllDisplayableItemsRecursively(false) > 0))
{
mDemoSystems.push_back(allSystems[i]);
int systemDuration = mRecalboxConf.getUInt(name + ".demo.duration", (unsigned int)mDefaultDuration);
mDurations.push_back(systemDuration);
}
}
// Check if there is at least one system.
// If not, get all system with no filtering
if (mDemoSystems.empty())
for(int i=(int)allSystems.size(); --i>= 0;)
{
mDemoSystems.push_back(allSystems[i]);
mDurations.push_back(mDefaultDuration);
}
}
bool DemoMode::hasDemoMode()
{
return (mSettings.getString("ScreenSaverBehavior") == "demo");
}
bool DemoMode::getRandomSystem(SystemData*& outputSystem, int& outputDuration)
{
if (mDemoSystems.empty()) return false;
std::uniform_int_distribution<int> random(0, (int)mDemoSystems.size() - 1);
for(int i=5; --i>=0; ) // Maximum 5 tries to find a new game to launch
{
// Select random system
int randomPosition = random(mRandomGenerator);
outputSystem = mDemoSystems[randomPosition];
outputDuration = mDurations[randomPosition];
std::string outputName = outputSystem->getName();
if ((outputName != mLastSystem) || (mDemoSystems.size() == 1))
{
// Set new game path
mLastSystem = outputName;
break;
}
// No luck, try again
}
LOG(LogInfo) << "Randomly selected system: " << outputSystem->getName();
//std::cout << "Randomly selected system: " << outputSystem->getName() << "\r\n";
return true;
}
bool DemoMode::getRandomGame(FileData*& outputGame, int& outputDuration)
{
SystemData* system = nullptr;
if (!getRandomSystem(system, outputDuration)) return false;
// Get filelist
FileData::List gamelist = system->getRootFolder()->getAllDisplayableItemsRecursively(false);
if (gamelist.empty()) return false;
std::uniform_int_distribution<int> random(0, (int)gamelist.size() - 1);
for(int i=5; --i>=0; ) // Maximum 5 tries to find a new game to launch
{
// Select game
outputGame = gamelist[random(mRandomGenerator)];
std::string outputPath = outputGame->getPath().generic_string();
if ((outputPath != mLastGamePath) || (gamelist.size() == 1))
{
// Set new game path
mLastGamePath = outputPath;
break;
}
// No luck, try again
}
LOG(LogInfo) << "Randomly selected game: " << outputGame->getPath().generic_string();
//std::cout << "Randomly selected game: " << outputGame->getPath().generic_string() << "\r\n";;
return true;
}
void DemoMode::runDemo()
{
if (!hasDemoMode()) return;
FileData* game = nullptr;
int duration = 0;
bool Initialized = false;
SystemData* system = nullptr;
while(getRandomGame(game, duration))
{
// Get game's parent system
system = game->getSystem();
// Initialize (shutdown ES display)
if (!Initialized)
{
system->demoInitialize(mWindow);
Initialized = true;
}
// Run game
if (system->demoLaunchGame(game, duration))
{
mWindow.doWake();
break;
}
}
// Finalize (remount ES display)
if (Initialized)
system->demoFinalize(mWindow);
}
#pragma once
#include <vector>
#include <random>
#include "Settings.h"
#include "RecalboxConf.h"
#include "SystemData.h"
#include "FileData.h"
class DemoMode
{
private:
//! Window
Window& mWindow;
//! Settings direct access
Settings& mSettings;
//! Recalbox configuration direct access
RecalboxConf& mRecalboxConf;
//! Game session duration
int mDefaultDuration;
//! List of system from which to get random games
std::vector<SystemData*> mDemoSystems;
//! Duration of game session per system
std::vector<int> mDurations;
//! Previously selected game path
std::string mLastGamePath;
//! Previously selected system
std::string mLastSystem;
//! Random device to seed random generator
std::random_device mRandomRevice;
std::mt19937 mRandomGenerator;
/*!
* @brief Get next random game
* @return Game structure or nullptr if no game is available
*/
/*!
* @brief Get next random game
* @param outputGame Choosen game
* @param outputDuration Session Duration
* @return True if a game is available.
*/
bool getRandomGame(FileData*& outputGame, int& outputDuration);
bool getRandomSystem(SystemData*& outputSystem, int& outputDuration);
public:
DemoMode(Window& window);
/*!
* @brief Return true is the uer set the screensaver to "demo"
* @return True if demo screensaver is on
*/
bool hasDemoMode();
/*!
* @brief Run random games until user interaction
*/
void runDemo();
};
......@@ -180,6 +180,12 @@ class FolderData : public FileData
* @return List of filtered items
*/
int countFilteredItems(Filter filters, bool includefolders) const { return countItems(filters, includefolders); }
/*!
* Count displayable items (normal + favorites) recursively starting from the current folder
* @param includefolders True to include subfolders in the resulting list
* @return Number of displayable items
*/
int countAllDisplayableItemsRecursively(bool includefolders) const { return countItems(Filter::Normal | Filter::Favorite, includefolders); }
/*!
* Get filtered items recursively starting from the current folder
......
......@@ -229,6 +229,73 @@ void SystemData::launchGame(Window* window, FileData* game, const std::string& n
game->Metadata().SetLastplayedNow();
}
void SystemData::demoInitialize(Window& window)
{
LOG(LogInfo) << "Entering demo mode...";
AudioManager::getInstance()->deinit();
VolumeControl::getInstance()->deinit();
window.deinit();
}
void SystemData::demoFinalize(Window& window)
{
// Reinit
window.init();
VolumeControl::getInstance()->init();
AudioManager::getInstance()->resumeMusic();
window.normalizeNextUpdate();
}
bool SystemData::demoLaunchGame(FileData* game, int duration)
{
std::string controlersConfig = InputManager::getInstance()->configureEmulators();
LOG(LogInfo) << "Controllers config : " << controlersConfig;
std::string command = mLaunchCommand;
const std::string rom = escapePath(game->getPath());
const std::string basename = game->getPath().stem().string();
const std::string rom_raw = fs::path(game->getPath()).make_preferred().string();
command = strreplace(command, "%ROM%", rom);
command = strreplace(command, "%CONTROLLERSCONFIG%", controlersConfig);
command = strreplace(command, "%SYSTEM%", game->getSystem()->getName());
command = strreplace(command, "%BASENAME%", basename);
command = strreplace(command, "%ROM_RAW%", rom_raw);
command = strreplace(command, "%EMULATOR%", game->Metadata().Emulator());
command = strreplace(command, "%CORE%", game->Metadata().Core());
command = strreplace(command, "%RATIO%", game->Metadata().RatioAsString());
command = strreplace(command, "%NETPLAY%", "");
// Add demo stuff
command += " -demo 1";
command += " -demoduration ";
command += std::to_string(duration);
LOG(LogInfo) << "Demo command: " << command;
//std::cout << " " << command << "\r\n";
int exitCode = runSystemCommand(command);
LOG(LogInfo) << "Demo exit code : " << exitCode;
//std::cout << "ExitCode : " << exitCode << "\r\n";
// Configgen returns an exitcode 0x33 when the user interact with any pad/mouse
// this exitcode returns here byte-swapped or shifted. Need further investigation
if( exitCode == 0x3300)
{
LOG(LogInfo) << "Exiting demo upon user request";
return true;
}
if( exitCode != 0)
{
LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!";
}
return false;
}
void SystemData::populateFolder(FolderData *folder, FileData::StringMap& doppelgangerWatcher)
{
try
......@@ -707,4 +774,4 @@ int SystemData::getSystemIndex(const std::string &name)
if (sSystemVector[i]->mName == name)
return i;
return -1;
}
\ No newline at end of file
}
......@@ -121,9 +121,10 @@ class SystemData
static void writeExampleConfig(const std::string& path);
static std::vector<SystemData*> sSystemVector;
static SystemData *getFavoriteSystem();
static SystemData* getFavoriteSystem();
static SystemData* getSystem(std::string& name);
static int getSystemIndex(const std::string& name);
static const std::vector<SystemData*>& getAllSystems() { return sSystemVector; }
inline std::vector<SystemData*>::const_iterator getIterator() const { return std::find(sSystemVector.begin(), sSystemVector.end(), this); };
inline std::vector<SystemData*>::const_reverse_iterator getRevIterator() const { return std::find(sSystemVector.rbegin(), sSystemVector.rend(), this); };
......@@ -152,4 +153,8 @@ class SystemData
static std::string getUserConfigurationAbsolutePath() { return RootFolders::DataRootFolder + "/system/.emulationstation/es_systems.cfg"; }
static std::string getTemplateConfigurationAbsolutePath() { return RootFolders::TemplateRootFolder + "/system/.emulationstation/es_systems.cfg"; }
void demoInitialize(Window& window);
void demoFinalize(Window& window);
bool demoLaunchGame(FileData* game, int duration);
};
......@@ -741,6 +741,7 @@ void GuiMenu::menuUISettings(){
std::vector<std::string> screensavers;
screensavers.push_back("dim");
screensavers.push_back("black");
screensavers.push_back("demo");
for (auto it = screensavers.begin(); it != screensavers.end(); it++)
screensaver_behavior->add(*it, *it, Settings::getInstance()->getString("ScreenSaverBehavior") == *it);
s->addWithLabel(screensaver_behavior, _("SCREENSAVER BEHAVIOR"), _(MenuMessages::UI_SCREENSAVER_BEHAVIOR_HELP_MSG));
......
......@@ -29,6 +29,7 @@
#include "views/ViewController.h"
#include "VolumeControl.h"
#include "Window.h"
#include "DemoMode.h"
#include "guis/GuiDetectDevice.h"
#include "guis/GuiInfoPopup.h"
#include "guis/GuiMsgBox.h"
......@@ -46,7 +47,16 @@ namespace fs = boost::filesystem;
bool scrape_cmdline = false;
void playSound(std::string name);
void playSound(const std::string& name)
{
std::string selectedTheme = Settings::getInstance()->getString("ThemeSet");
std::string loadingMusic = RootFolders::DataRootFolder + "/system/.emulationstation/themes/" + selectedTheme +
"/fx/" + name + ".ogg";
if (boost::filesystem::exists(loadingMusic))
{
Music::get(loadingMusic)->play(false, nullptr);
}
}
bool parseArgs(int argc, char* argv[], unsigned int* width, unsigned int* height)
{
......@@ -60,8 +70,9 @@ bool parseArgs(int argc, char* argv[], unsigned int* width, unsigned int* height
return false;
}
*width = atoi(argv[i + 1]);
*height = atoi(argv[i + 2]);
char* err;
*width = (unsigned int)strtol(argv[i + 1], &err, 10);
*height = (unsigned int)strtol(argv[i + 2], &err, 10);
i += 2; // skip the argument value
}
else if (strcmp(argv[i], "--ignore-gamelist") == 0)
......@@ -88,7 +99,7 @@ bool parseArgs(int argc, char* argv[], unsigned int* width, unsigned int* height
}
else if (strcmp(argv[i], "--vsync") == 0)
{
bool vsync = (strcmp(argv[i + 1], "on") == 0 || strcmp(argv[i + 1], "1") == 0) ? true : false;
bool vsync = (strcmp(argv[i + 1], "on") == 0 || strcmp(argv[i + 1], "1") == 0);
Settings::getInstance()->setBool("VSync", vsync);
i++; // skip vsync value
}
......@@ -98,7 +109,8 @@ bool parseArgs(int argc, char* argv[], unsigned int* width, unsigned int* height
}
else if (strcmp(argv[i], "--max-vram") == 0)
{
int maxVRAM = atoi(argv[i + 1]);
char* err;
int maxVRAM = (int)strtol(argv[i + 1], &err, 10);
Settings::getInstance()->setInt("MaxVRAM", maxVRAM);
}
else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)
......@@ -157,7 +169,7 @@ bool verifyHomeFolderExists()
// Returns true if everything is OK,
bool loadSystemConfigFile(const char** errorString)
{
*errorString = NULL;
*errorString = nullptr;
if (!SystemData::loadConfig())
{
......@@ -167,7 +179,7 @@ bool loadSystemConfigFile(const char** errorString)
return false;
}
if (SystemData::sSystemVector.size() == 0)
if (SystemData::sSystemVector.empty())
{
LOG(LogError)
<< "No systems found! Does at least one system have a game present? (check that extensions match!)\n(Also, make sure you've updated your es_systems.cfg for XML!)";
......@@ -292,6 +304,9 @@ int main(int argc, char* argv[])
}
#endif
// Randomize
std::srand(std::time(nullptr));
try
{
//if ~/.emulationstation doesn't exist and cannot be created, bail
......@@ -340,11 +355,11 @@ int main(int argc, char* argv[])
playSound("loading");
const char* errorMsg = NULL;
const char* errorMsg = nullptr;
if (!loadSystemConfigFile(&errorMsg))
{
// something went terribly wrong
if (errorMsg == NULL)
if (errorMsg == nullptr)
{
LOG(LogError) << "Unknown error occured while parsing system config file.";
if (!scrape_cmdline)
......@@ -355,7 +370,8 @@ int main(int argc, char* argv[])
// we can't handle es_systems.cfg file problems inside ES itself, so display the error message then quit
window.pushGui(new GuiMsgBox(&window, errorMsg, _("QUIT"), []
{
SDL_Event* quit = new SDL_Event();
SDL_Event* quit = nullptr;
quit = new SDL_Event();
quit->type = SDL_QUIT;
SDL_PushEvent(quit);
}));
......@@ -369,7 +385,7 @@ int main(int argc, char* argv[])
RecalboxSystem::getInstance()->getIpAdress();
// UPDATED VERSION MESSAGE
std::string changelog = RecalboxUpgrade::getInstance()->getChangelog();
if (changelog != "")
if (!changelog.empty())
{
std::string message = "Changes :\n" + changelog;
window.pushGui(new GuiMsgBoxScroll(&window, _("THE SYSTEM IS UP TO DATE"), message, _("OK"), []
......@@ -414,7 +430,7 @@ int main(int argc, char* argv[])
//ViewController::get()->preload();
//choose which GUI to open depending on if an input configuration already exists
if (errorMsg == NULL)
if (errorMsg == nullptr)
{
if (fs::exists(InputManager::getConfigPath()) && InputManager::getInstance()->getNumConfiguredDevices() > 0)
{
......@@ -443,6 +459,8 @@ int main(int argc, char* argv[])
bool doReboot = false;
bool doShutdown = false;
DemoMode demoMode(window);
while (running)
{
SDL_Event event;
......@@ -506,8 +524,14 @@ int main(int argc, char* argv[])
if (window.isSleeping())
{
if (demoMode.hasDemoMode())
{
demoMode.runDemo();
}
lastTime = SDL_GetTicks();
SDL_Delay(1); // this doesn't need to be accurate, we're just giving up our CPU time until something wakes us up
// this doesn't need to be accurate, we're just giving up our CPU time until something wakes us up
SDL_Delay(1);
continue;
}
......@@ -567,13 +591,3 @@ int main(int argc, char* argv[])
return 0;
}
void playSound(std::string name)
{
std::string selectedTheme = Settings::getInstance()->getString("ThemeSet");
std::string loadingMusic = RootFolders::DataRootFolder + "/system/.emulationstation/themes/" + selectedTheme +
"/fx/" + name + ".ogg";
if (boost::filesystem::exists(loadingMusic))
{
Music::get(loadingMusic)->play(false, NULL);
}
}
......@@ -75,7 +75,9 @@ int runSystemCommand(const std::string& cmd_utf8)
std::wstring wchar_str = converter.from_bytes(cmd_utf8);
return _wsystem(wchar_str.c_str());
#else
return system((cmd_utf8 + " 2> /recalbox/share/system/logs/es_launch_stderr.log | head -300 > /recalbox/share/system/logs/es_launch_stdout.log").c_str());
//return system((cmd_utf8 + " 2> /recalbox/share/system/logs/es_launch_stderr.log | head -300 > /recalbox/share/system/logs/es_launch_stdout.log").c_str());
int exitcode = system((cmd_utf8 + " > /recalbox/share/system/logs/es_launch_stdout.log 2> /recalbox/share/system/logs/es_launch_stderr.log").c_str());
return exitcode;
#endif
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment