2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-06-21 12:25:28 +00:00
|
|
|
// FileData.cpp
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-06-21 12:25:28 +00:00
|
|
|
// Provides game file data structures and functions to access and sort this information.
|
|
|
|
// Also provides functions to look up paths to media files and for launching games
|
|
|
|
// (launching initiated by the ViewController).
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "FileData.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2018-01-09 22:55:09 +00:00
|
|
|
#include "utils/FileSystemUtil.h"
|
2017-11-29 19:57:43 +00:00
|
|
|
#include "utils/StringUtil.h"
|
2017-11-22 21:01:12 +00:00
|
|
|
#include "utils/TimeUtil.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "AudioManager.h"
|
|
|
|
#include "CollectionSystemManager.h"
|
|
|
|
#include "FileFilterIndex.h"
|
2017-06-12 16:38:59 +00:00
|
|
|
#include "FileSorts.h"
|
|
|
|
#include "Log.h"
|
2018-02-09 17:23:58 +00:00
|
|
|
#include "MameNames.h"
|
2020-06-21 10:26:21 +00:00
|
|
|
#include "Platform.h"
|
2018-01-30 00:49:08 +00:00
|
|
|
#include "Scripting.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "SystemData.h"
|
|
|
|
#include "VolumeControl.h"
|
|
|
|
#include "Window.h"
|
2018-01-29 22:50:10 +00:00
|
|
|
#include <assert.h>
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
FileData::FileData(
|
2020-06-21 12:25:28 +00:00
|
|
|
FileType type,
|
|
|
|
const std::string& path,
|
|
|
|
SystemEnvironmentData* envData,
|
|
|
|
SystemData* system)
|
|
|
|
: mType(type),
|
|
|
|
mPath(path),
|
|
|
|
mSystem(system),
|
|
|
|
mEnvData(envData),
|
|
|
|
mSourceFileData(nullptr),
|
|
|
|
mParent(nullptr),
|
|
|
|
// Metadata is REALLY set in the constructor!
|
|
|
|
metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA)
|
|
|
|
{
|
|
|
|
// Metadata needs at least a name field (since that's what getName() will return).
|
|
|
|
if (metadata.get("name").empty()) {
|
|
|
|
if ((system->hasPlatformId(PlatformIds::ARCADE) ||
|
|
|
|
system->hasPlatformId(PlatformIds::NEOGEO)) &&
|
|
|
|
metadata.getType() != FOLDER_METADATA) {
|
|
|
|
// If it's a MAME or Neo Geo game, expand the game name accordingly.
|
|
|
|
metadata.set("name",
|
|
|
|
MameNames::getInstance()->getCleanName(getCleanName()));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
metadata.set("name", getDisplayName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mSystemName = system->getName();
|
|
|
|
metadata.resetChangedFlag();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FileData::~FileData()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mParent)
|
|
|
|
mParent->removeChild(this);
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mType == GAME)
|
|
|
|
mSystem->getIndex()->removeFromIndex(this);
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
mChildren.clear();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 15:33:19 +00:00
|
|
|
std::string FileData::getDisplayName() const
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
std::string stem = Utils::FileSystem::getStem(mPath);
|
|
|
|
return stem;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 15:33:19 +00:00
|
|
|
std::string FileData::getCleanName() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return Utils::String::removeParenthesis(this->getDisplayName());
|
2016-03-29 15:33:19 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 16:38:59 +00:00
|
|
|
const std::string& FileData::getName()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return metadata.get("name");
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
2018-04-25 05:07:25 +00:00
|
|
|
const std::string& FileData::getSortName()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (metadata.get("sortname").empty())
|
|
|
|
return metadata.get("name");
|
|
|
|
else
|
|
|
|
return metadata.get("sortname");
|
2018-04-25 05:07:25 +00:00
|
|
|
}
|
|
|
|
|
2020-05-15 15:42:36 +00:00
|
|
|
const bool FileData::getFavorite()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (metadata.get("favorite") == "true")
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
2020-05-15 15:42:36 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 15:09:32 +00:00
|
|
|
const std::string FileData::getROMDirectory()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
std::string romDirSetting = Settings::getInstance()->getString("ROMDirectory");
|
|
|
|
std::string romDirPath = "";
|
2020-06-18 15:09:32 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (romDirSetting == "") {
|
|
|
|
romDirPath = Utils::FileSystem::getHomePath() + "/ROMs/";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
romDirPath = romDirSetting;
|
2020-06-18 15:09:32 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Expand home symbol if the path starts with ~
|
|
|
|
if (romDirPath[0] == '~') {
|
|
|
|
romDirPath.erase(0, 1);
|
|
|
|
romDirPath.insert(0, Utils::FileSystem::getHomePath());
|
|
|
|
}
|
|
|
|
if (romDirPath.back() != '/')
|
|
|
|
romDirPath = romDirPath + "/";
|
|
|
|
}
|
2020-06-18 15:09:32 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return romDirPath;
|
2020-06-18 15:09:32 +00:00
|
|
|
}
|
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::getMediaDirectory()
|
2020-05-18 17:00:43 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
std::string mediaDirSetting = Settings::getInstance()->getString("MediaDirectory");
|
|
|
|
std::string mediaDirPath = "";
|
2017-05-18 10:16:57 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mediaDirSetting == "") {
|
|
|
|
mediaDirPath = Utils::FileSystem::getHomePath() + "/.emulationstation/downloaded_media/";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mediaDirPath = mediaDirSetting;
|
2020-05-18 17:00:43 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Expand home symbol if the path starts with ~
|
|
|
|
if (mediaDirPath[0] == '~') {
|
|
|
|
mediaDirPath.erase(0, 1);
|
|
|
|
mediaDirPath.insert(0, Utils::FileSystem::getHomePath());
|
|
|
|
}
|
|
|
|
if (mediaDirPath.back() != '/')
|
|
|
|
mediaDirPath = mediaDirPath + "/";
|
|
|
|
}
|
2020-05-18 17:00:43 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return mediaDirPath;
|
2020-05-18 17:00:43 +00:00
|
|
|
}
|
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::getMediafilePath(std::string subdirectory, std::string mediatype) const
|
2020-05-18 17:00:43 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
const char* extList[2] = { ".png", ".jpg" };
|
2020-05-18 17:00:43 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Look for an image file in the media directory.
|
|
|
|
std::string tempPath = getMediaDirectory() + mSystemName + "/" +
|
|
|
|
subdirectory + "/" + getDisplayName();
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
std::string mediaPath = tempPath + extList[i];
|
|
|
|
if (Utils::FileSystem::exists(mediaPath))
|
|
|
|
return mediaPath;
|
|
|
|
}
|
2020-05-18 17:00:43 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// No media found in the media directory, so look
|
|
|
|
// for local art as well (if configured to do so).
|
|
|
|
if (Settings::getInstance()->getBool("LocalArt")) {
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
std::string localMediaPath = mEnvData->mStartPath + "/images/" +
|
|
|
|
getDisplayName() + "-" + mediatype + extList[i];
|
|
|
|
if (Utils::FileSystem::exists(localMediaPath))
|
|
|
|
return localMediaPath;
|
|
|
|
}
|
|
|
|
}
|
2020-05-18 17:00:43 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return "";
|
2017-03-18 17:54:39 +00:00
|
|
|
}
|
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::getImagePath() const
|
2017-10-22 23:04:17 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Look for a mix image (a combination of screenshot, 2D/3D box and marquee).
|
|
|
|
std::string image = getMediafilePath("miximages", "miximage");
|
|
|
|
if (image != "")
|
|
|
|
return image;
|
2020-06-06 11:10:33 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// If no mix image was found, try screenshot instead.
|
|
|
|
image = getMediafilePath("screenshots", "screenshot");
|
|
|
|
if (image != "")
|
|
|
|
return image;
|
2020-06-06 11:10:33 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// If no screenshot was found either, try cover.
|
|
|
|
return getMediafilePath("covers", "cover");
|
2020-06-06 11:10:33 +00:00
|
|
|
}
|
2017-10-22 23:04:17 +00:00
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::get3DBoxPath() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return getMediafilePath("3dboxes", "3dbox");
|
2020-06-06 11:10:33 +00:00
|
|
|
}
|
2017-10-22 23:04:17 +00:00
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::getCoverPath() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return getMediafilePath("covers", "cover");
|
2017-10-22 23:04:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const std::string FileData::getMarqueePath() const
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return getMediafilePath("marquees", "marquee");
|
2020-06-06 11:10:33 +00:00
|
|
|
}
|
2017-10-22 23:04:17 +00:00
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::getMiximagePath() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return getMediafilePath("miximages", "miximage");
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::getScreenshotPath() const
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return getMediafilePath("screenshots", "screenshot");
|
2020-06-06 11:10:33 +00:00
|
|
|
}
|
2020-05-18 17:00:43 +00:00
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::getThumbnailPath() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return getMediafilePath("thumbnails", "thumbnail");
|
2020-06-06 11:10:33 +00:00
|
|
|
}
|
2017-10-22 23:04:17 +00:00
|
|
|
|
2020-06-06 11:10:33 +00:00
|
|
|
const std::string FileData::getVideoPath() const
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
const char* extList[5] = { ".avi", ".mkv", ".mov", ".mp4", ".wmv" };
|
|
|
|
std::string tempPath = getMediaDirectory() + mSystemName + "/videos/" + getDisplayName();
|
2020-05-18 17:00:43 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Look for media in the media directory.
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
std::string mediaPath = tempPath + extList[i];
|
|
|
|
if (Utils::FileSystem::exists(mediaPath))
|
|
|
|
return mediaPath;
|
|
|
|
}
|
2020-05-18 17:00:43 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// No media found in the media directory, so look
|
|
|
|
// for local art as well (if configured to do so).
|
|
|
|
if (Settings::getInstance()->getBool("LocalArt"))
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
std::string localMediaPath = mEnvData->mStartPath + "/videos/" + getDisplayName() +
|
|
|
|
"-video" + extList[i];
|
|
|
|
if (Utils::FileSystem::exists(localMediaPath))
|
|
|
|
return localMediaPath;
|
|
|
|
}
|
|
|
|
}
|
2017-10-22 23:04:17 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return "";
|
2020-05-18 17:00:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<FileData*>& FileData::getChildrenListToDisplay()
|
|
|
|
{
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
FileFilterIndex* idx = CollectionSystemManager::get()->getSystemToView(mSystem)->getIndex();
|
|
|
|
if (idx->isFiltered()) {
|
|
|
|
mFilteredChildren.clear();
|
|
|
|
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
|
|
|
if (idx->showFile((*it))) {
|
|
|
|
mFilteredChildren.push_back(*it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return mFilteredChildren;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return mChildren;
|
|
|
|
}
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 17:54:39 +00:00
|
|
|
std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask, bool displayedOnly) const
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
std::vector<FileData*> out;
|
|
|
|
FileFilterIndex* idx = mSystem->getIndex();
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
|
|
|
if ((*it)->getType() & typeMask) {
|
|
|
|
if (!displayedOnly || !idx->isFiltered() || idx->showFile(*it))
|
|
|
|
out.push_back(*it);
|
|
|
|
}
|
|
|
|
if ((*it)->getChildren().size() > 0) {
|
|
|
|
std::vector<FileData*> subchildren = (*it)->getFilesRecursive(typeMask, displayedOnly);
|
|
|
|
out.insert(out.cend(), subchildren.cbegin(), subchildren.cend());
|
|
|
|
}
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return out;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 16:38:59 +00:00
|
|
|
std::string FileData::getKey() {
|
2020-06-21 12:25:28 +00:00
|
|
|
return getFileName();
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 01:29:46 +00:00
|
|
|
const bool FileData::isArcadeAsset()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
const std::string stem = Utils::FileSystem::getStem(mPath);
|
|
|
|
return (
|
|
|
|
(mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
|
|
|
|
mSystem->hasPlatformId(PlatformIds::NEOGEO))) &&
|
|
|
|
(MameNames::getInstance()->isBios(stem) ||
|
|
|
|
MameNames::getInstance()->isDevice(stem))
|
|
|
|
);
|
2018-05-10 01:29:46 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 16:38:59 +00:00
|
|
|
FileData* FileData::getSourceFileData()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return this;
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
void FileData::addChild(FileData* file)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
assert(mType == FOLDER);
|
2020-06-23 18:07:00 +00:00
|
|
|
assert(file->getParent() == nullptr);
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
const std::string key = file->getKey();
|
|
|
|
if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) {
|
|
|
|
mChildrenByFilename[key] = file;
|
|
|
|
mChildren.push_back(file);
|
|
|
|
file->mParent = this;
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FileData::removeChild(FileData* file)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
assert(mType == FOLDER);
|
|
|
|
assert(file->getParent() == this);
|
|
|
|
mChildrenByFilename.erase(file->getKey());
|
|
|
|
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
|
|
|
if (*it == file) {
|
2020-06-23 18:07:00 +00:00
|
|
|
file->mParent = nullptr;
|
2020-06-21 12:25:28 +00:00
|
|
|
mChildren.erase(it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// File somehow wasn't in our children.
|
|
|
|
assert(false);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FileData::sort(ComparisonFunction& comparator, bool ascending)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
mFirstLetterIndex.clear();
|
|
|
|
std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
|
|
|
// Build mFirstLetterIndex.
|
|
|
|
const char firstChar = toupper((*it)->getSortName().front());
|
|
|
|
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
|
|
|
// Iterate through any child folders.
|
|
|
|
if ((*it)->getChildren().size() > 0)
|
|
|
|
(*it)->sort(comparator, ascending);
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Sort and make each entry unique in mFirstLetterIndex.
|
|
|
|
std::sort(mFirstLetterIndex.begin(), mFirstLetterIndex.end());
|
|
|
|
auto last = std::unique(mFirstLetterIndex.begin(), mFirstLetterIndex.end());
|
|
|
|
mFirstLetterIndex.erase(last, mFirstLetterIndex.end());
|
2020-06-11 19:08:48 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (!ascending)
|
|
|
|
std::reverse(mChildren.begin(), mChildren.end());
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
mFirstLetterIndex.clear();
|
|
|
|
std::vector<FileData*> mChildrenFavorites;
|
|
|
|
std::vector<FileData*> mChildrenOthers;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
|
|
|
if (mChildren[i]->getFavorite()) {
|
|
|
|
mChildrenFavorites.push_back(mChildren[i]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mChildrenOthers.push_back(mChildren[i]);
|
|
|
|
// Build mFirstLetterIndex.
|
|
|
|
const char firstChar = toupper(mChildren[i]->getSortName().front());
|
|
|
|
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are only favorites in the gamelist, it makes sense to still generate
|
|
|
|
// a letter index. For instance to be able to quick jump in the 'favorites'
|
|
|
|
// collection. Doing this additional work here only for the applicable gamelists is
|
|
|
|
// probably faster than building a redundant index for all gamelists during sorting.
|
|
|
|
if (mChildrenOthers.size() == 0 && mChildrenFavorites.size() > 0) {
|
|
|
|
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
|
|
|
const char firstChar = toupper(mChildren[i]->getSortName().front());
|
|
|
|
mFirstLetterIndex.push_back(std::string(1, firstChar));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort and make each entry unique in mFirstLetterIndex.
|
|
|
|
std::sort(mFirstLetterIndex.begin(), mFirstLetterIndex.end());
|
|
|
|
auto last = std::unique(mFirstLetterIndex.begin(), mFirstLetterIndex.end());
|
|
|
|
mFirstLetterIndex.erase(last, mFirstLetterIndex.end());
|
|
|
|
|
|
|
|
// If there were at least one favorite in the gamelist, insert the favorite
|
|
|
|
// unicode character in the first position.
|
|
|
|
if (mChildrenOthers.size() > 0 && mChildrenFavorites.size() > 0)
|
|
|
|
mFirstLetterIndex.insert(mFirstLetterIndex.begin(), FAVORITE_CHAR);
|
|
|
|
|
|
|
|
// Sort favorite games and the other games separately.
|
|
|
|
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator);
|
|
|
|
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
|
|
|
|
|
|
|
|
// Iterate through any child folders.
|
|
|
|
for (auto it = mChildrenFavorites.cbegin(); it != mChildrenFavorites.cend(); it++) {
|
|
|
|
if ((*it)->getChildren().size() > 0)
|
|
|
|
(*it)->sortFavoritesOnTop(comparator, ascending);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterate through any child folders.
|
|
|
|
for (auto it = mChildrenOthers.cbegin(); it != mChildrenOthers.cend(); it++) {
|
|
|
|
if ((*it)->getChildren().size() > 0)
|
|
|
|
(*it)->sortFavoritesOnTop(comparator, ascending);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ascending) {
|
|
|
|
std::reverse(mChildrenFavorites.begin(), mChildrenFavorites.end());
|
|
|
|
std::reverse(mChildrenOthers.begin(), mChildrenOthers.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Combine the individually sorted favorite games and other games vectors.
|
|
|
|
mChildren.erase(mChildren.begin(), mChildren.end());
|
|
|
|
mChildren.reserve(mChildrenFavorites.size() + mChildrenOthers.size());
|
|
|
|
mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end());
|
|
|
|
mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end());
|
2020-05-24 08:29:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FileData::sort(const SortType& type, bool mFavoritesOnTop)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mFavoritesOnTop)
|
|
|
|
sortFavoritesOnTop(*type.comparisonFunction, type.ascending);
|
|
|
|
else
|
|
|
|
sort(*type.comparisonFunction, type.ascending);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
2017-06-12 16:38:59 +00:00
|
|
|
|
|
|
|
void FileData::launchGame(Window* window)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
LOG(LogInfo) << "Attempting to launch game...";
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
AudioManager::getInstance()->deinit();
|
|
|
|
VolumeControl::getInstance()->deinit();
|
2020-05-15 15:51:32 +00:00
|
|
|
|
|
|
|
// window->deinit();
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
std::string command = "";
|
2020-05-19 15:52:11 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Check if there is a launch string override for the game
|
|
|
|
// and the corresponding option to use it has been set.
|
|
|
|
if (Settings::getInstance()->getBool("LaunchstringOverride") &&
|
|
|
|
!metadata.get("launchstring").empty())
|
|
|
|
command = metadata.get("launchstring");
|
|
|
|
else
|
|
|
|
command = mEnvData->mLaunchCommand;
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
const std::string rom = Utils::FileSystem::getEscapedPath(getPath());
|
|
|
|
const std::string basename = Utils::FileSystem::getStem(getPath());
|
|
|
|
const std::string rom_raw = Utils::FileSystem::getPreferredPath(getPath());
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
command = Utils::String::replace(command, "%ROM%", rom);
|
|
|
|
command = Utils::String::replace(command, "%BASENAME%", basename);
|
|
|
|
command = Utils::String::replace(command, "%ROM_RAW%", rom_raw);
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
Scripting::fireEvent("game-start", rom, basename);
|
2018-01-30 00:49:08 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
LOG(LogInfo) << " " << command;
|
|
|
|
int exitCode = runSystemCommand(command);
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (exitCode != 0)
|
|
|
|
LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!";
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
Scripting::fireEvent("game-end");
|
2018-01-30 00:49:08 +00:00
|
|
|
|
2020-05-15 15:51:32 +00:00
|
|
|
// window->init();
|
2020-05-24 08:29:29 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
VolumeControl::getInstance()->init();
|
|
|
|
window->normalizeNextUpdate();
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Update number of times the game has been launched.
|
|
|
|
FileData* gameToUpdate = getSourceFileData();
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
int timesPlayed = gameToUpdate->metadata.getInt("playcount") + 1;
|
|
|
|
gameToUpdate->metadata.set("playcount", std::to_string(static_cast<long long>(timesPlayed)));
|
2017-06-12 16:38:59 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Update last played time.
|
|
|
|
gameToUpdate->metadata.set("lastplayed", Utils::Time::DateTime(Utils::Time::now()));
|
|
|
|
CollectionSystemManager::get()->refreshCollectionSystems(gameToUpdate);
|
2019-08-24 14:22:02 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
gameToUpdate->mSystem->onMetaDataSavePoint();
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CollectionFileData::CollectionFileData(FileData* file, SystemData* system)
|
2020-06-21 12:25:28 +00:00
|
|
|
: FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(),
|
|
|
|
file->getSourceFileData()->getSystemEnvData(), system)
|
2017-06-12 16:38:59 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// We use this constructor to create a clone of the filedata, and change its system.
|
|
|
|
mSourceFileData = file->getSourceFileData();
|
|
|
|
refreshMetadata();
|
2020-06-23 18:07:00 +00:00
|
|
|
mParent = nullptr;
|
2020-06-21 12:25:28 +00:00
|
|
|
metadata = mSourceFileData->metadata;
|
|
|
|
mSystemName = mSourceFileData->getSystem()->getName();
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CollectionFileData::~CollectionFileData()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Need to remove collection file data at the collection object destructor.
|
|
|
|
if (mParent)
|
|
|
|
mParent->removeChild(this);
|
2020-06-23 18:07:00 +00:00
|
|
|
mParent = nullptr;
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string CollectionFileData::getKey() {
|
2020-06-21 12:25:28 +00:00
|
|
|
return getFullPath();
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FileData* CollectionFileData::getSourceFileData()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
return mSourceFileData;
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CollectionFileData::refreshMetadata()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
metadata = mSourceFileData->metadata;
|
|
|
|
mDirty = true;
|
2017-06-12 16:38:59 +00:00
|
|
|
}
|
|
|
|
|
2019-08-25 15:23:02 +00:00
|
|
|
const std::string& CollectionFileData::getName()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mDirty) {
|
|
|
|
mCollectionFileName =
|
|
|
|
Utils::String::removeParenthesis(mSourceFileData->metadata.get("name"));
|
|
|
|
mCollectionFileName +=
|
|
|
|
" [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]";
|
|
|
|
mDirty = false;
|
|
|
|
}
|
2019-08-25 15:23:02 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (Settings::getInstance()->getBool("CollectionShowSystemInfo"))
|
|
|
|
return mCollectionFileName;
|
2020-05-24 08:29:29 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return mSourceFileData->metadata.get("name");
|
2017-07-18 09:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// Return sort type based on a string description.
|
2017-07-18 09:45:50 +00:00
|
|
|
FileData::SortType getSortTypeFromString(std::string desc) {
|
2020-06-21 12:25:28 +00:00
|
|
|
std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;
|
|
|
|
// Find it
|
|
|
|
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
|
|
|
|
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
|
|
|
if (sort.description == desc)
|
|
|
|
return sort;
|
|
|
|
}
|
|
|
|
// If no type found then default to "filename, ascending".
|
|
|
|
return FileSorts::SortTypes.at(0);
|
2017-09-14 01:26:33 +00:00
|
|
|
}
|