#include "FileFilterIndex.h" #include "utils/StringUtil.h" #include "views/UIModeController.h" #include "FileData.h" #include "Log.h" #include "Settings.h" #define UNKNOWN_LABEL "UNKNOWN" #define INCLUDE_UNKNOWN false; FileFilterIndex::FileFilterIndex() : filterByFavorites(false), filterByGenre(false), filterByHidden(false), filterByKidGame(false), filterByPlayers(false), filterByPubDev(false), filterByRatings(false) { clearAllFilters(); FilterDataDecl filterDecls[] = { //type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel { FAVORITES_FILTER, &favoritesIndexAllKeys, &filterByFavorites, &favoritesIndexFilteredKeys,"favorite", false, "", "FAVORITES" }, { GENRE_FILTER, &genreIndexAllKeys, &filterByGenre, &genreIndexFilteredKeys, "genre", true, "genre", "GENRE" }, { PLAYER_FILTER, &playersIndexAllKeys, &filterByPlayers, &playersIndexFilteredKeys, "players", false, "", "PLAYERS" }, { PUBDEV_FILTER, &pubDevIndexAllKeys, &filterByPubDev, &pubDevIndexFilteredKeys, "developer", true, "publisher", "PUBLISHER / DEVELOPER" }, { RATINGS_FILTER, &ratingsIndexAllKeys, &filterByRatings, &ratingsIndexFilteredKeys, "rating", false, "", "RATING" }, { KIDGAME_FILTER, &kidGameIndexAllKeys, &filterByKidGame, &kidGameIndexFilteredKeys, "kidgame", false, "", "KIDGAME" }, { HIDDEN_FILTER, &hiddenIndexAllKeys, &filterByHidden, &hiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" } }; filterDataDecl = std::vector(filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0])); } FileFilterIndex::~FileFilterIndex() { resetIndex(); } std::vector& FileFilterIndex::getFilterDataDecls() { return filterDataDecl; } void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) { struct IndexImportStructure { std::map* destinationIndex; std::map* sourceIndex; }; IndexImportStructure indexStructDecls[] = { { &genreIndexAllKeys, &(indexToImport->genreIndexAllKeys) }, { &playersIndexAllKeys, &(indexToImport->playersIndexAllKeys) }, { &pubDevIndexAllKeys, &(indexToImport->pubDevIndexAllKeys) }, { &ratingsIndexAllKeys, &(indexToImport->ratingsIndexAllKeys) }, { &favoritesIndexAllKeys, &(indexToImport->favoritesIndexAllKeys) }, { &hiddenIndexAllKeys, &(indexToImport->hiddenIndexAllKeys) }, { &kidGameIndexAllKeys, &(indexToImport->kidGameIndexAllKeys) }, }; std::vector indexImportDecl = std::vector(indexStructDecls, indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0])); for (std::vector::const_iterator indexesIt = indexImportDecl.cbegin(); indexesIt != indexImportDecl.cend(); ++indexesIt ) { for (std::map::const_iterator sourceIt = (*indexesIt).sourceIndex->cbegin(); sourceIt != (*indexesIt).sourceIndex->cend(); ++sourceIt ) { if ((*indexesIt).destinationIndex->find((*sourceIt).first) == (*indexesIt).destinationIndex->cend()) { // entry doesn't exist (*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second; } else { (*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second; } } } } void FileFilterIndex::resetIndex() { clearAllFilters(); clearIndex(genreIndexAllKeys); clearIndex(playersIndexAllKeys); clearIndex(pubDevIndexAllKeys); clearIndex(ratingsIndexAllKeys); clearIndex(favoritesIndexAllKeys); clearIndex(hiddenIndexAllKeys); clearIndex(kidGameIndexAllKeys); } std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary) { std::string key = ""; switch(type) { case GENRE_FILTER: { key = Utils::String::toUpper(game->metadata.get("genre")); key = Utils::String::trim(key); if (getSecondary && !key.empty()) { std::istringstream f(key); std::string newKey; getline(f, newKey, '/'); if (!newKey.empty() && newKey != key) { key = newKey; } else { key = std::string(); } } break; } case PLAYER_FILTER: { if (getSecondary) break; key = game->metadata.get("players"); break; } case PUBDEV_FILTER: { key = Utils::String::toUpper(game->metadata.get("publisher")); key = Utils::String::trim(key); if ((getSecondary && !key.empty()) || (!getSecondary && key.empty())) key = Utils::String::toUpper(game->metadata.get("developer")); else key = Utils::String::toUpper(game->metadata.get("publisher")); break; } case RATINGS_FILTER: { int ratingNumber = 0; if (!getSecondary) { std::string ratingString = game->metadata.get("rating"); if (!ratingString.empty()) { try { ratingNumber = (int)((std::stod(ratingString)*5)+0.5); if (ratingNumber < 0) ratingNumber = 0; key = std::to_string(ratingNumber) + " STARS"; } catch (int e) { LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " << ratingString << ", " << e; } } } break; } case FAVORITES_FILTER: { if (game->getType() != GAME) return "FALSE"; key = Utils::String::toUpper(game->metadata.get("favorite")); break; } case HIDDEN_FILTER: { if (game->getType() != GAME) return "FALSE"; key = Utils::String::toUpper(game->metadata.get("hidden")); break; } case KIDGAME_FILTER: { if (game->getType() != GAME) return "FALSE"; key = Utils::String::toUpper(game->metadata.get("kidgame")); break; } } key = Utils::String::trim(key); if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) { key = UNKNOWN_LABEL; } return key; } void FileFilterIndex::addToIndex(FileData* game) { manageGenreEntryInIndex(game); managePlayerEntryInIndex(game); managePubDevEntryInIndex(game); manageRatingsEntryInIndex(game); manageFavoritesEntryInIndex(game); manageHiddenEntryInIndex(game); manageKidGameEntryInIndex(game); } void FileFilterIndex::removeFromIndex(FileData* game) { manageGenreEntryInIndex(game, true); managePlayerEntryInIndex(game, true); managePubDevEntryInIndex(game, true); manageRatingsEntryInIndex(game, true); manageFavoritesEntryInIndex(game, true); manageHiddenEntryInIndex(game, true); manageKidGameEntryInIndex(game, true); } void FileFilterIndex::setFilter(FilterIndexType type, std::vector* values) { // test if it exists before setting if(type == NONE) { clearAllFilters(); } else { for (std::vector::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) { if ((*it).type == type) { FilterDataDecl filterData = (*it); *(filterData.filteredByRef) = values->size() > 0; filterData.currentFilteredKeys->clear(); for (std::vector::const_iterator vit = values->cbegin(); vit != values->cend(); ++vit ) { // check if exists if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) { filterData.currentFilteredKeys->push_back(std::string(*vit)); } } } } } return; } void FileFilterIndex::clearAllFilters() { for (std::vector::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) { FilterDataDecl filterData = (*it); *(filterData.filteredByRef) = false; filterData.currentFilteredKeys->clear(); } return; } void FileFilterIndex::resetFilters() { clearAllFilters(); setUIModeFilters(); } void FileFilterIndex::setUIModeFilters() { if(!Settings::getInstance()->getBool("ForceDisableFilters")){ if (UIModeController::getInstance()->isUIModeKiosk()) { filterByHidden = true; std::vector val = { "FALSE" }; setFilter(HIDDEN_FILTER, &val); } if (UIModeController::getInstance()->isUIModeKid()) { filterByKidGame = true; std::vector val = { "TRUE" }; setFilter(KIDGAME_FILTER, &val); } } } void FileFilterIndex::debugPrintIndexes() { LOG(LogInfo) << "Printing Indexes..."; for (auto x: playersIndexAllKeys) { LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second; } for (auto x: genreIndexAllKeys) { LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second; } for (auto x: ratingsIndexAllKeys) { LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second; } for (auto x: pubDevIndexAllKeys) { LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second; } for (auto x: favoritesIndexAllKeys) { LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second; } for (auto x : hiddenIndexAllKeys) { LOG(LogInfo) << "Hidden Index: " << x.first << ": " << x.second; } for (auto x : kidGameIndexAllKeys) { LOG(LogInfo) << "KidGames Index: " << x.first << ": " << x.second; } } bool FileFilterIndex::showFile(FileData* game) { // this shouldn't happen, but just in case let's get it out of the way if (!isFiltered()) return true; // if folder, needs further inspection - i.e. see if folder contains at least one element // that should be shown if (game->getType() == FOLDER) { std::vector children = game->getChildren(); // iterate through all of the children, until there's a match for (std::vector::const_iterator it = children.cbegin(); it != children.cend(); ++it ) { if (showFile(*it)) { return true; } } return false; } bool keepGoing = false; for (std::vector::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) { FilterDataDecl filterData = (*it); if(*(filterData.filteredByRef)) { // try to find a match std::string key = getIndexableKey(game, filterData.type, false); keepGoing = isKeyBeingFilteredBy(key, filterData.type); // if we didn't find a match, try for secondary keys - i.e. publisher and dev, or first genre if (!keepGoing) { if (!filterData.hasSecondaryKey) { return false; } std::string secKey = getIndexableKey(game, filterData.type, true); if (secKey != UNKNOWN_LABEL) { keepGoing = isKeyBeingFilteredBy(secKey, filterData.type); } } // if still nothing, then it's not a match if (!keepGoing) return false; } } return keepGoing; } bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) { const FilterIndexType filterTypes[7] = { FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER,HIDDEN_FILTER, KIDGAME_FILTER }; std::vector filterKeysList[7] = { favoritesIndexFilteredKeys, genreIndexFilteredKeys, playersIndexFilteredKeys, pubDevIndexFilteredKeys, ratingsIndexFilteredKeys, hiddenIndexFilteredKeys, kidGameIndexFilteredKeys }; for (int i = 0; i < 7; i++) { if (filterTypes[i] == type) { for (std::vector::const_iterator it = filterKeysList[i].cbegin(); it != filterKeysList[i].cend(); ++it ) { if (key == (*it)) { return true; } } return false; } } return false; } void FileFilterIndex::manageGenreEntryInIndex(FileData* game, bool remove) { std::string key = getIndexableKey(game, GENRE_FILTER, false); // flag for including unknowns bool includeUnknown = INCLUDE_UNKNOWN; // only add unknown in pubdev IF both dev and pub are empty if (!includeUnknown && (key == UNKNOWN_LABEL || key == "BIOS")) { // no valid genre info found return; } manageIndexEntry(&genreIndexAllKeys, key, remove); key = getIndexableKey(game, GENRE_FILTER, true); if (!includeUnknown && key == UNKNOWN_LABEL) { manageIndexEntry(&genreIndexAllKeys, key, remove); } } void FileFilterIndex::managePlayerEntryInIndex(FileData* game, bool remove) { // flag for including unknowns bool includeUnknown = INCLUDE_UNKNOWN; std::string key = getIndexableKey(game, PLAYER_FILTER, false); // only add unknown in pubdev IF both dev and pub are empty if (!includeUnknown && key == UNKNOWN_LABEL) { // no valid player info found return; } manageIndexEntry(&playersIndexAllKeys, key, remove); } void FileFilterIndex::managePubDevEntryInIndex(FileData* game, bool remove) { std::string pub = getIndexableKey(game, PUBDEV_FILTER, false); std::string dev = getIndexableKey(game, PUBDEV_FILTER, true); // flag for including unknowns bool includeUnknown = INCLUDE_UNKNOWN; bool unknownPub = false; bool unknownDev = false; if (pub == UNKNOWN_LABEL) { unknownPub = true; } if (dev == UNKNOWN_LABEL) { unknownDev = true; } if (!includeUnknown && unknownDev && unknownPub) { // no valid rating info found return; } if (unknownDev && unknownPub) { // if no info at all manageIndexEntry(&pubDevIndexAllKeys, pub, remove); } else { if (!unknownDev) { // if no info at all manageIndexEntry(&pubDevIndexAllKeys, dev, remove); } if (!unknownPub) { // if no info at all manageIndexEntry(&pubDevIndexAllKeys, pub, remove); } } } void FileFilterIndex::manageRatingsEntryInIndex(FileData* game, bool remove) { std::string key = getIndexableKey(game, RATINGS_FILTER, false); // flag for including unknowns bool includeUnknown = INCLUDE_UNKNOWN; if (!includeUnknown && key == UNKNOWN_LABEL) { // no valid rating info found return; } manageIndexEntry(&ratingsIndexAllKeys, key, remove); } void FileFilterIndex::manageFavoritesEntryInIndex(FileData* game, bool remove) { // flag for including unknowns bool includeUnknown = INCLUDE_UNKNOWN; std::string key = getIndexableKey(game, FAVORITES_FILTER, false); if (!includeUnknown && key == UNKNOWN_LABEL) { // no valid favorites info found return; } manageIndexEntry(&favoritesIndexAllKeys, key, remove); } void FileFilterIndex::manageHiddenEntryInIndex(FileData* game, bool remove) { // flag for including unknowns bool includeUnknown = INCLUDE_UNKNOWN; std::string key = getIndexableKey(game, HIDDEN_FILTER, false); if (!includeUnknown && key == UNKNOWN_LABEL) { // no valid hidden info found return; } manageIndexEntry(&hiddenIndexAllKeys, key, remove); } void FileFilterIndex::manageKidGameEntryInIndex(FileData* game, bool remove) { // flag for including unknowns bool includeUnknown = INCLUDE_UNKNOWN; std::string key = getIndexableKey(game, KIDGAME_FILTER, false); if (!includeUnknown && key == UNKNOWN_LABEL) { // no valid kidgame info found return; } manageIndexEntry(&kidGameIndexAllKeys, key, remove); } void FileFilterIndex::manageIndexEntry(std::map* index, std::string key, bool remove) { bool includeUnknown = INCLUDE_UNKNOWN; if (!includeUnknown && key == UNKNOWN_LABEL) return; if (remove) { // removing entry if (index->find(key) == index->cend()) { // this shouldn't happen LOG(LogInfo) << "Couldn't find entry in index! " << key; } else { (index->at(key))--; if(index->at(key) <= 0) { index->erase(key); } } } else { // adding entry if (index->find(key) == index->cend()) { (*index)[key] = 1; } else { (index->at(key))++; } } } void FileFilterIndex::clearIndex(std::map indexMap) { indexMap.clear(); }