From 5956eec040aa3dc6a6dfbd29f77c5368a91c5a35 Mon Sep 17 00:00:00 2001 From: Pieter Hulshoff Date: Wed, 11 Jan 2017 10:54:02 +0100 Subject: [PATCH] Added support for trurip SuperDAT files (to be placed in the meta/trurip directory). Also added check for meta.db update based on file timestamps. --- RetroFE/Source/Database/MetadataDatabase.cpp | 212 ++++++++++++++++++- RetroFE/Source/Database/MetadataDatabase.h | 2 + 2 files changed, 211 insertions(+), 3 deletions(-) diff --git a/RetroFE/Source/Database/MetadataDatabase.cpp b/RetroFE/Source/Database/MetadataDatabase.cpp index fdfd27a..2eb4256 100644 --- a/RetroFE/Source/Database/MetadataDatabase.cpp +++ b/RetroFE/Source/Database/MetadataDatabase.cpp @@ -33,6 +33,10 @@ #include #include +#if defined(__linux) || defined(__APPLE__) +#include +#endif + MetadataDatabase::MetadataDatabase(DB &db, Configuration &c) : config_(c) , db_(db) @@ -115,8 +119,9 @@ bool MetadataDatabase::importDirectory() { DIR *dp; struct dirent *dirp; - std::string hyperListPath = Utils::combinePath(Configuration::absolutePath, "meta", "hyperlist"); - std::string mameListPath = Utils::combinePath(Configuration::absolutePath, "meta", "mamelist"); + std::string hyperListPath = Utils::combinePath(Configuration::absolutePath, "meta", "hyperlist"); + std::string mameListPath = Utils::combinePath(Configuration::absolutePath, "meta", "mamelist"); + std::string truripListPath = Utils::combinePath(Configuration::absolutePath, "meta", "trurip"); dp = opendir(hyperListPath.c_str()); @@ -183,6 +188,37 @@ bool MetadataDatabase::importDirectory() closedir(dp); } + dp = opendir(truripListPath.c_str()); + + if(dp == NULL) + { + Logger::write(Logger::ZONE_INFO, "MetadataDatabase", "Could not read directory \"" + truripListPath + "\""); + } + else + { + while((dirp = readdir(dp)) != NULL) + { + if (dirp->d_type != DT_DIR && std::string(dirp->d_name) != "." && std::string(dirp->d_name) != "..") + { + + std::string basename = dirp->d_name; + + std::string extension = basename.substr(basename.find_last_of("."), basename.size()-1); + basename = basename.substr(0, basename.find_last_of(".")); + + + if(extension == ".dat") + { + std::string importFile = Utils::combinePath(truripListPath, std::string(dirp->d_name)); + Logger::write(Logger::ZONE_INFO, "Metadata", "Importing truriplist: " + importFile); + importTruriplist(importFile); + } + } + } + + closedir(dp); + } + return true; } @@ -307,8 +343,11 @@ bool MetadataDatabase::needsRefresh() if(rc == SQLITE_ROW) { int count = sqlite3_column_int(stmt, 0); + struct stat metadb; + int metadbErr = stat( Utils::combinePath(Configuration::absolutePath, "meta.db").c_str(), &metadb); + time_t metadirTime = timeDir(Utils::combinePath(Configuration::absolutePath, "meta")); - return (count == 0) ? true : false; + return (count == 0 || metadbErr || metadb.st_mtime < metadirTime) ? true : false; } else { @@ -524,3 +563,170 @@ bool MetadataDatabase::importMamelist(std::string filename, std::string collecti return true; } + + +bool MetadataDatabase::importTruriplist(std::string truriplistFile) +{ + char *error = NULL; + + config_.setProperty("status", "Scraping data from \"" + truriplistFile + "\""); + rapidxml::xml_document<> doc; + std::ifstream file(truriplistFile.c_str()); + std::vector buffer((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + + try + { + buffer.push_back('\0'); + + doc.parse<0>(&buffer[0]); + + rapidxml::xml_node<> *root = doc.first_node("datafile"); + + if(!root) + { + Logger::write(Logger::ZONE_ERROR, "Metadata", "Does not appear to be a TruripList file (missing tag)"); + return false; + } + + rapidxml::xml_node<> *header = root->first_node("header"); + if (!header) + { + Logger::write(Logger::ZONE_ERROR, "Metadata", "Does not appear to be a TruripList file (missing
tag)"); + return false; + } + rapidxml::xml_node<> *name = header->first_node("name"); + if (!name) + { + Logger::write(Logger::ZONE_ERROR, "Metadata", "Does not appear to be a TruripList SuperDat file (missing in
tag)"); + return false; + } + std::string collectionName = name->value(); + std::size_t pos = collectionName.find(" - "); + if(pos != std::string::npos) + { + collectionName = collectionName.substr(0, pos); + } + sqlite3 *handle = db_.handle; + sqlite3_exec(handle, "BEGIN IMMEDIATE TRANSACTION;", NULL, NULL, &error); + + + for(rapidxml::xml_node<> *game = root->first_node("game"); game; game = game->next_sibling("game")) + { + rapidxml::xml_node<> *descriptionXml = game->first_node("description"); + rapidxml::xml_node<> *truripXml = game->first_node("trurip"); + if (!truripXml) + { + Logger::write(Logger::ZONE_ERROR, "Metadata", "Does not appear to be a TruripList SuperDat file (missing tag)"); + return false; + } + rapidxml::xml_node<> *cloneofXml = truripXml->first_node("cloneof"); + rapidxml::xml_node<> *manufacturerXml = truripXml->first_node("publisher"); + rapidxml::xml_node<> *developerXml = truripXml->first_node("developer"); + rapidxml::xml_node<> *yearXml = truripXml->first_node("year"); + rapidxml::xml_node<> *genreXml = truripXml->first_node("genre"); + rapidxml::xml_node<> *ratingXml = truripXml->first_node("ratings"); + rapidxml::xml_node<> *scoreXml = truripXml->first_node("score"); + rapidxml::xml_node<> *numberPlayersXml = truripXml->first_node("players"); + rapidxml::xml_node<> *enabledXml = truripXml->first_node("enabled"); + std::string name = (descriptionXml) ? descriptionXml->value() : ""; + std::string description = (descriptionXml) ? descriptionXml->value() : ""; + std::string crc = ""; + std::string cloneOf = (cloneofXml) ? cloneofXml->value() : ""; + std::string manufacturer = (manufacturerXml) ? manufacturerXml->value() : ""; + std::string developer = (developerXml) ? developerXml->value() : ""; + std::string year = (yearXml) ? yearXml->value() : ""; + std::string genre = (genreXml) ? genreXml->value() : ""; + std::string rating = (ratingXml) ? ratingXml->value() : ""; + std::string score = (scoreXml) ? scoreXml->value() : ""; + std::string numberPlayers = (numberPlayersXml) ? numberPlayersXml->value() : ""; + std::string ctrlType = ""; + std::string numberButtons = ""; + std::string numberJoyWays = ""; + std::string enabled = (enabledXml) ? enabledXml->value() : ""; + + if(name.length() > 0) + { + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(handle, + "INSERT OR REPLACE INTO Meta (name, title, year, manufacturer, developer, genre, players, ctrltype, buttons, joyways, cloneOf, collectionName, rating, score) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", + -1, &stmt, 0); + + sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, description.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 3, year.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 4, manufacturer.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 5, developer.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 6, genre.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 7, numberPlayers.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 8, ctrlType.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 9, numberButtons.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 10, numberJoyWays.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 11, cloneOf.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 12, collectionName.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 13, rating.c_str(), -1, SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 14, score.c_str(), -1, SQLITE_TRANSIENT); + + sqlite3_step(stmt); + sqlite3_finalize(stmt); + } + } + config_.setProperty("status", "Saving data from \"" + truriplistFile + "\" to database"); + sqlite3_exec(handle, "COMMIT TRANSACTION;", NULL, NULL, &error); + + return true; + } + catch(rapidxml::parse_error &e) + { + std::string what = e.what(); + long line = static_cast(std::count(&buffer.front(), e.where(), char('\n')) + 1); + std::stringstream ss; + ss << "Could not parse layout file. [Line: " << line << "] Reason: " << e.what(); + + Logger::write(Logger::ZONE_ERROR, "Metadata", ss.str()); + } + catch(std::exception &e) + { + std::string what = e.what(); + Logger::write(Logger::ZONE_ERROR, "Metadata", "Could not parse truriplist file. Reason: " + what); + } + + + return false; +} + + +time_t MetadataDatabase::timeDir( std::string path ) +{ + time_t lastTime = 0; + DIR *dp; + struct dirent *dirp; + + dp = opendir( path.c_str( ) ); + + while (dp != NULL && (dirp = readdir( dp )) != NULL) + { + std::string file = dirp->d_name; + + // Check if file is a directory + struct stat sb; + if (file != "." && file != ".." && stat( Utils::combinePath( path, file ).c_str( ), &sb ) == 0 && S_ISDIR( sb.st_mode )) + { + time_t tmpTime = timeDir( Utils::combinePath( path, file ) ); + lastTime = (tmpTime > lastTime) ? tmpTime : lastTime; + } + else if (file != "." && file != "..") + { + struct stat filestat; + int err = stat( Utils::combinePath( path, file ).c_str( ), &filestat ); + lastTime = (!err && filestat.st_mtime > lastTime) ? filestat.st_mtime : lastTime; + } + } + + if (dp != NULL) + { + closedir( dp ); + } + + return lastTime; +} diff --git a/RetroFE/Source/Database/MetadataDatabase.h b/RetroFE/Source/Database/MetadataDatabase.h index b6bfc50..a6dfc93 100644 --- a/RetroFE/Source/Database/MetadataDatabase.h +++ b/RetroFE/Source/Database/MetadataDatabase.h @@ -35,10 +35,12 @@ public: void injectMetadata(CollectionInfo *collection); bool importHyperlist(std::string hyperlistFile, std::string collectionName); bool importMamelist(std::string filename, std::string collectionName); + bool importTruriplist(std::string filename); private: bool importDirectory(); bool needsRefresh(); + time_t timeDir(std::string path); Configuration &config_; DB &db_; };