diff --git a/.github/workflows/build-libfp-linux.yml b/.github/workflows/build-libfp-linux.yml index ad53bda..7f56011 100644 --- a/.github/workflows/build-libfp-linux.yml +++ b/.github/workflows/build-libfp-linux.yml @@ -18,14 +18,9 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04, ubuntu-22.04] - compiler: [gcc, clang] + compiler: [clang] lib_linkage: [shared, static] include: - - os: ubuntu-20.04 - compiler: gcc - c_comp: gcc-10 - cxx_comp: g++-10 - qt_comp: clang12 - os: ubuntu-20.04 compiler: clang c_comp: clang-12 @@ -36,6 +31,13 @@ jobs: c_comp: gcc-12 cxx_comp: g++-12 qt_comp: clang14 + lib_linkage: shared + - os: ubuntu-22.04 + compiler: gcc + c_comp: gcc-12 + cxx_comp: g++-12 + qt_comp: clang14 + lib_linkage: static - os: ubuntu-22.04 compiler: clang c_comp: clang-14 @@ -58,7 +60,7 @@ jobs: - name: Install Qt (custom build) uses: oblivioncth/actions/general/install-and-cache-qt-from-ffynnon@dev with: - version: 6.4.2 + version: 6.5.1 os: linux compiler: ${{ matrix.qt_comp }} linkage: ${{ matrix.lib_linkage }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c811ec..526f53c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,14 +6,14 @@ cmake_minimum_required(VERSION 3.23.0...3.25.0) # Project # NOTE: DON'T USE TRAILING ZEROS IN VERSIONS project(libfp - VERSION 0.3.1 + VERSION 0.4 LANGUAGES CXX - DESCRIPTION "C++ support library for Flashpoint" + DESCRIPTION "C++ support library for Flashpoint Archive" ) # Get helper scripts include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FetchOBCMake.cmake) -fetch_ob_cmake("v0.3") +fetch_ob_cmake("v0.3.2") # Initialize project according to standard rules include(OB/Project) @@ -52,7 +52,7 @@ set(LIBFP_QX_COMPONENTS include(OB/FetchQx) ob_fetch_qx( - REF "v0.5.0.1" + REF "v0.5.1" COMPONENTS ${LIBFP_QX_COMPONENTS} ) diff --git a/README.md b/README.md index a07cd02..0571a5c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # libfp -A C++ interface for instances of [Blue Maxima's Flashpoint](https://bluemaxima.org/flashpoint/) +A C++ interface for instances of [Flashpoint Archive](https://flashpointarchive.org/) [![Dev Builds](https://github.com/oblivioncth/libfp/actions/workflows/push-reaction.yml/badge.svg?branch=dev)](https://github.com/oblivioncth/libfp/actions/workflows/push-reaction.yml) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2ba5d8f..ce07077 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -11,14 +11,24 @@ ob_add_standard_library(${LIB_TARGET_NAME} fp-db.h fp-install.h fp-items.h - fp-json.h fp-macro.h + fp-playlistmanager.h + settings/fp-config.h + settings/fp-execs.h + settings/fp-preferences.h + settings/fp-services.h + settings/fp-settings.h IMPLEMENTATION fp-db.cpp fp-install.cpp - fp-items.cpp - fp-json.cpp fp-macro.cpp + fp-items.cpp + fp-playlistmanager.cpp + settings/fp-config.cpp + settings/fp-execs.cpp + settings/fp-preferences.cpp + settings/fp-services.cpp + settings/fp-settings.cpp LINKS PUBLIC Qt6::Core diff --git a/lib/include/fp/fp-db.h b/lib/include/fp/fp-db.h index 5e4e008..bdf3f3c 100644 --- a/lib/include/fp/fp-db.h +++ b/lib/include/fp/fp-db.h @@ -10,158 +10,170 @@ #include // Qx Includes -#include +#include + +// Project Includes +#include "fp/fp-items.h" + +using namespace Qt::Literals::StringLiterals; namespace Fp { -class FP_FP_EXPORT Db : public QObject +class FP_FP_EXPORT QX_ERROR_TYPE(DbError, "Fp::DbError", 1101) { -//-QObject Macro (Required for all QObject Derived Classes)----------------------------------------------------------- - Q_OBJECT - -//-Inner Classes------------------------------------------------------------------------------------------------- + friend class Db; +//-Class Enums------------------------------------------------------------- public: - class Table_Game + enum Type { - public: - static inline const QString NAME = "game"; - - static inline const QString COL_ID = "id"; - static inline const QString COL_PARENT_ID = "parentGameId"; - static inline const QString COL_TITLE = "title"; - static inline const QString COL_SERIES = "series"; - static inline const QString COL_DEVELOPER = "developer"; - static inline const QString COL_PUBLISHER = "publisher"; - static inline const QString COL_DATE_ADDED = "dateAdded"; - static inline const QString COL_DATE_MODIFIED = "dateModified"; - static inline const QString COL_PLATFORM = "platform"; - static inline const QString COL_BROKEN = "broken"; - static inline const QString COL_EXTREME = "extreme"; - static inline const QString COL_PLAY_MODE = "playMode"; - static inline const QString COL_STATUS = "status"; - static inline const QString COL_NOTES = "notes"; - static inline const QString COL_SOURCE = "source"; - static inline const QString COL_APP_PATH = "applicationPath"; - static inline const QString COL_LAUNCH_COMMAND = "launchCommand"; - static inline const QString COL_RELEASE_DATE = "releaseDate"; - static inline const QString COL_VERSION = "version"; - static inline const QString COL_ORIGINAL_DESC = "originalDescription"; - static inline const QString COL_LANGUAGE = "language"; - static inline const QString COL_LIBRARY = "library"; - static inline const QString COL_ORDER_TITLE = "orderTitle"; - - static inline const QStringList COLUMN_LIST = {COL_ID, COL_TITLE, COL_SERIES, COL_DEVELOPER, COL_PUBLISHER, COL_DATE_ADDED, COL_DATE_MODIFIED, COL_PLATFORM, - COL_BROKEN, COL_EXTREME, COL_PLAY_MODE, COL_STATUS, COL_NOTES, COL_SOURCE, COL_APP_PATH, COL_LAUNCH_COMMAND, COL_RELEASE_DATE, - COL_VERSION, COL_ORIGINAL_DESC, COL_LANGUAGE, COL_LIBRARY, COL_ORDER_TITLE}; - - static inline const QString ENTRY_GAME_LIBRARY = "arcade"; - static inline const QString ENTRY_ANIM_LIBRARY = "theatre"; - static inline const QString ENTRY_NOT_WORK = "Not Working"; + NoError = 0, + SqlError = 1, + InvalidSchema = 2, + IdCollision = 3, + IncompleteSearch = 4, + UpdateIneffective = 5, + UpdateTooMany = 6 }; - class Table_Game_Data - { - public: - static inline const QString NAME = "game_data"; - - static inline const QString COL_ID = "id"; - static inline const QString COL_GAME_ID = "gameId"; - static inline const QString COL_TITLE = "title"; - static inline const QString COL_DATE_ADDED = "dateAdded"; - static inline const QString COL_SHA256 = "sha256"; - static inline const QString COL_CRC32 = "crc32"; - static inline const QString COL_PRES_ON_DISK = "presentOnDisk"; - static inline const QString COL_PATH = "path"; - static inline const QString COL_SIZE = "size"; - static inline const QString COL_PARAM = "parameters"; - - static inline const QStringList COLUMN_LIST = {COL_ID, COL_GAME_ID, COL_TITLE, COL_DATE_ADDED, COL_SHA256, COL_CRC32, COL_PRES_ON_DISK, COL_PATH, COL_SIZE, COL_PARAM}; +//-Class Variables------------------------------------------------------------- +private: + static inline const QHash ERR_STRINGS{ + {NoError, u"No error occurred."_s}, + {SqlError, u"An unexpected SQL error occurred."_s}, + {InvalidSchema, u"The schema of the database was different than expected."_s}, + {IdCollision, u"A duplicate of a unique ID was found."_s}, + {IncompleteSearch, u"A data search could not be completed."_s}, + {UpdateIneffective, u"An update statement unexpectedly affected 0 rows."_s}, + {UpdateTooMany, u"An update statement affected more rows than expected."_s} }; - class Table_Add_App - { - public: - static inline const QString NAME = "additional_app"; +//-Instance Variables------------------------------------------------------------- +private: + Type mType; + QString mCause; + QString mDetails; - static inline const QString COL_ID = "id"; - static inline const QString COL_APP_PATH = "applicationPath"; - static inline const QString COL_AUTORUN = "autoRunBefore"; - static inline const QString COL_LAUNCH_COMMAND = "launchCommand"; - static inline const QString COL_NAME = "name"; - static inline const QString COL_WAIT_EXIT = "waitForExit"; - static inline const QString COL_PARENT_ID = "parentGameId"; +//-Class Constructor------------------------------------------------------------- +private: + DbError(Type t, const QString& c, const QString& d = {}); - static inline const QStringList COLUMN_LIST = {COL_ID, COL_APP_PATH, COL_AUTORUN, COL_LAUNCH_COMMAND, COL_NAME, COL_WAIT_EXIT, COL_PARENT_ID}; +public: + DbError(); - static inline const QString ENTRY_EXTRAS = ":extras:"; - static inline const QString ENTRY_MESSAGE = ":message:"; - }; +//-Class Functions--------------------------------------------------------------- +private: + static DbError fromSqlError(const QSqlError& e); - class Table_Playlist - { - public: - static inline const QString NAME = "playlist"; - static inline const QString COL_ID = "id"; - static inline const QString COL_TITLE = "title"; - static inline const QString COL_DESCRIPTION = "description"; - static inline const QString COL_AUTHOR = "author"; - static inline const QString COL_LIBRARY = "library"; +//-Instance Functions------------------------------------------------------------- +private: + Qx::Severity deriveSeverity() const override; + quint32 deriveValue() const override; + QString derivePrimary() const override; + QString deriveSecondary() const override; + QString deriveDetails() const override; - static inline const QString ENTRY_GAME_LIBRARY = "arcade"; +public: + bool isValid() const; + Type type() const; + QString cause() const; + QString details() const; +}; - static inline const QStringList COLUMN_LIST = {COL_ID, COL_TITLE, COL_DESCRIPTION, COL_AUTHOR, COL_LIBRARY}; - }; +class FP_FP_EXPORT Db : public QObject +{ +//-QObject Macro (Required for all QObject Derived Classes)----------------------------------------------------------- + Q_OBJECT - class Table_Playlist_Game +//-Inner Classes------------------------------------------------------------------------------------------------- +public: + class Table_Game { public: - static inline const QString NAME = "playlist_game"; - - static inline const QString COL_ID = "id"; - static inline const QString COL_PLAYLIST_ID = "playlistId"; - static inline const QString COL_ORDER = "order"; - static inline const QString COL_GAME_ID = "gameId"; + static inline const QString NAME = u"game"_s; + + static inline const QString COL_ID = u"id"_s; + static inline const QString COL_PARENT_ID = u"parentGameId"_s; + static inline const QString COL_TITLE = u"title"_s; + static inline const QString COL_SERIES = u"series"_s; + static inline const QString COL_DEVELOPER = u"developer"_s; + static inline const QString COL_PUBLISHER = u"publisher"_s; + static inline const QString COL_DATE_ADDED = u"dateAdded"_s; + static inline const QString COL_DATE_MODIFIED = u"dateModified"_s; + static inline const QString COL_BROKEN = u"broken"_s; + static inline const QString COL_EXTREME = u"extreme"_s; + static inline const QString COL_PLAY_MODE = u"playMode"_s; + static inline const QString COL_STATUS = u"status"_s; + static inline const QString COL_NOTES = u"notes"_s; + static inline const QString COL_SOURCE = u"source"_s; + static inline const QString COL_APP_PATH = u"applicationPath"_s; + static inline const QString COL_LAUNCH_COMMAND = u"launchCommand"_s; + static inline const QString COL_RELEASE_DATE = u"releaseDate"_s; + static inline const QString COL_VERSION = u"version"_s; + static inline const QString COL_ORIGINAL_DESC = u"originalDescription"_s; + static inline const QString COL_LANGUAGE = u"language"_s; + static inline const QString COL_LIBRARY = u"library"_s; + static inline const QString COL_ORDER_TITLE = u"orderTitle"_s; + static inline const QString COL_PLATFORM_NAME = u"platformName"_s; + + static inline const QStringList COLUMN_LIST = {COL_ID, COL_PARENT_ID, COL_TITLE, COL_SERIES, COL_DEVELOPER, COL_PUBLISHER, COL_DATE_ADDED, COL_DATE_MODIFIED, + COL_BROKEN, COL_EXTREME, COL_PLAY_MODE, COL_STATUS, COL_NOTES, COL_SOURCE, COL_APP_PATH, COL_LAUNCH_COMMAND, COL_RELEASE_DATE, + COL_VERSION, COL_ORIGINAL_DESC, COL_LANGUAGE, COL_LIBRARY, COL_ORDER_TITLE, COL_PLATFORM_NAME}; - static inline const QStringList COLUMN_LIST = {COL_ID, COL_PLAYLIST_ID, COL_ORDER, COL_GAME_ID}; + static inline const QString ENTRY_GAME_LIBRARY = u"arcade"_s; + static inline const QString ENTRY_ANIM_LIBRARY = u"theatre"_s; + static inline const QString ENTRY_NOT_WORK = u"Not Working"_s; }; - class Table_Source + class Table_Game_Data { public: - static inline const QString NAME = "source"; - - static inline const QString COL_ID = "id"; - static inline const QString COL_NAME = "name"; - static inline const QString COL_DATE_ADDED = "dateAdded"; - static inline const QString COL_LAST_UPDATED = "lastUpdated"; - static inline const QString COL_SRC_FILE_URL = "sourceFileUrl"; - static inline const QString COL_BASE_URL = "baseUrl"; - static inline const QString COL_COUNT = "count"; - - static inline const QStringList COLUMN_LIST = {COL_ID, COL_NAME, COL_DATE_ADDED, COL_LAST_UPDATED, COL_SRC_FILE_URL, COL_BASE_URL, COL_COUNT}; + static inline const QString NAME = u"game_data"_s; + + static inline const QString COL_ID = u"id"_s; + static inline const QString COL_GAME_ID = u"gameId"_s; + static inline const QString COL_TITLE = u"title"_s; + static inline const QString COL_DATE_ADDED = u"dateAdded"_s; + static inline const QString COL_SHA256 = u"sha256"_s; + static inline const QString COL_CRC32 = u"crc32"_s; + static inline const QString COL_PRES_ON_DISK = u"presentOnDisk"_s; + static inline const QString COL_PATH = u"path"_s; + static inline const QString COL_SIZE = u"size"_s; + static inline const QString COL_PARAM = u"parameters"_s; + static inline const QString COL_APP_PATH = u"applicationPath"_s; + static inline const QString COL_LAUNCH_COMMAND = u"launchCommand"_s; + + static inline const QStringList COLUMN_LIST = {COL_ID, COL_GAME_ID, COL_TITLE, COL_DATE_ADDED, COL_SHA256, COL_CRC32, COL_PRES_ON_DISK, COL_PATH, COL_SIZE, COL_PARAM, + COL_APP_PATH, COL_LAUNCH_COMMAND}; }; - class Table_Source_Data + class Table_Add_App { public: - static inline const QString NAME = "source_data"; + static inline const QString NAME = u"additional_app"_s; - static inline const QString COL_ID = "id"; - static inline const QString COL_SOURCE_ID = "sourceId"; - static inline const QString COL_SHA256 = "sha256"; - static inline const QString COL_URL_PATH = "urlPath"; + static inline const QString COL_ID = u"id"_s; + static inline const QString COL_APP_PATH = u"applicationPath"_s; + static inline const QString COL_AUTORUN = u"autoRunBefore"_s; + static inline const QString COL_LAUNCH_COMMAND = u"launchCommand"_s; + static inline const QString COL_NAME = u"name"_s; + static inline const QString COL_WAIT_EXIT = u"waitForExit"_s; + static inline const QString COL_PARENT_ID = u"parentGameId"_s; + + static inline const QStringList COLUMN_LIST = {COL_ID, COL_APP_PATH, COL_AUTORUN, COL_LAUNCH_COMMAND, COL_NAME, COL_WAIT_EXIT, COL_PARENT_ID}; - static inline const QStringList COLUMN_LIST = {COL_ID, COL_SOURCE_ID, COL_SHA256, COL_URL_PATH}; + static inline const QString ENTRY_EXTRAS = u":extras:"_s; + static inline const QString ENTRY_MESSAGE = u":message:"_s; }; + class Table_Game_Tags_Tag { public: - static inline const QString NAME = "game_tags_tag"; + static inline const QString NAME = u"game_tags_tag"_s; - static inline const QString COL_GAME_ID = "gameId"; - static inline const QString COL_TAG_ID = "tagId"; + static inline const QString COL_GAME_ID = u"gameId"_s; + static inline const QString COL_TAG_ID = u"tagId"_s; static inline const QStringList COLUMN_LIST = {COL_GAME_ID, COL_TAG_ID}; }; @@ -169,11 +181,11 @@ class FP_FP_EXPORT Db : public QObject class Table_Tag { public: - static inline const QString NAME = "tag"; + static inline const QString NAME = u"tag"_s; - static inline const QString COL_ID = "id"; - static inline const QString COL_PRIMARY_ALIAS_ID = "primaryAliasId"; - static inline const QString COL_CATEGORY_ID = "categoryId"; + static inline const QString COL_ID = u"id"_s; + static inline const QString COL_PRIMARY_ALIAS_ID = u"primaryAliasId"_s; + static inline const QString COL_CATEGORY_ID = u"categoryId"_s; static inline const QStringList COLUMN_LIST = {COL_ID, COL_PRIMARY_ALIAS_ID, COL_CATEGORY_ID}; }; @@ -181,11 +193,11 @@ class FP_FP_EXPORT Db : public QObject class Table_Tag_Alias { public: - static inline const QString NAME = "tag_alias"; + static inline const QString NAME = u"tag_alias"_s; - static inline const QString COL_ID = "id"; - static inline const QString COL_TAG_ID = "tagId"; - static inline const QString COL_NAME = "name"; + static inline const QString COL_ID = u"id"_s; + static inline const QString COL_TAG_ID = u"tagId"_s; + static inline const QString COL_NAME = u"name"_s; static inline const QStringList COLUMN_LIST = {COL_ID, COL_TAG_ID, COL_NAME}; }; @@ -193,11 +205,11 @@ class FP_FP_EXPORT Db : public QObject class Table_Tag_Category { public: - static inline const QString NAME = "tag_category"; + static inline const QString NAME = u"tag_category"_s; - static inline const QString COL_ID = "id"; - static inline const QString COL_NAME = "name"; - static inline const QString COL_COLOR = "color"; + static inline const QString COL_ID = u"id"_s; + static inline const QString COL_NAME = u"name"_s; + static inline const QString COL_COLOR = u"color"_s; static inline const QStringList COLUMN_LIST = {COL_ID, COL_NAME, COL_COLOR}; @@ -269,48 +281,51 @@ class FP_FP_EXPORT Db : public QObject //-Class Variables----------------------------------------------------------------------------------------------- private: - static inline const QString DATABASE_CONNECTION_NAME = "flashpoint_database"; - - static inline const QList DATABASE_SPECS_LIST = {{Db::Table_Game::NAME, Db::Table_Game::COLUMN_LIST}, - {Db::Table_Add_App::NAME, Db::Table_Add_App::COLUMN_LIST}, - {Db::Table_Playlist::NAME, Db::Table_Playlist::COLUMN_LIST}, - {Db::Table_Playlist_Game::NAME, Db::Table_Playlist_Game::COLUMN_LIST}}; - static inline const QString GENERAL_QUERY_SIZE_COMMAND = "COUNT(1)"; + static inline const QString DATABASE_CONNECTION_NAME = u"flashpoint_database"_s; + + // TODO: Self register this somehow so that it doesnt need to be modified when tables are added or removed. + // Might require wrapping tables in actual classes + static inline const QList DATABASE_SPECS_LIST = { + {Db::Table_Game::NAME, Db::Table_Game::COLUMN_LIST}, + {Db::Table_Add_App::NAME, Db::Table_Add_App::COLUMN_LIST}, + {Db::Table_Game_Data::NAME, Db::Table_Game_Data::COLUMN_LIST}, + {Db::Table_Game_Tags_Tag::NAME, Db::Table_Game_Tags_Tag::COLUMN_LIST}, + {Db::Table_Tag::NAME, Db::Table_Tag::COLUMN_LIST}, + {Db::Table_Tag_Alias::NAME, Db::Table_Tag_Alias::COLUMN_LIST}, + {Db::Table_Tag_Category::NAME, Db::Table_Tag_Category::COLUMN_LIST}, + }; + static inline const QString GENERAL_QUERY_SIZE_COMMAND = u"COUNT(1)"_s; - static inline const QString GAME_ONLY_FILTER = Db::Table_Game::COL_LIBRARY + " = '" + Db::Table_Game::ENTRY_GAME_LIBRARY + "'"; - static inline const QString ANIM_ONLY_FILTER = Db::Table_Game::COL_LIBRARY + " = '" + Db::Table_Game::ENTRY_ANIM_LIBRARY + "'"; - static inline const QString GAME_AND_ANIM_FILTER = "(" + GAME_ONLY_FILTER + " OR " + ANIM_ONLY_FILTER + ")"; + static inline const QString GAME_ONLY_FILTER = Db::Table_Game::COL_LIBRARY + u" = '"_s + Db::Table_Game::ENTRY_GAME_LIBRARY + u"'"_s; + static inline const QString ANIM_ONLY_FILTER = Db::Table_Game::COL_LIBRARY + u" = '"_s + Db::Table_Game::ENTRY_ANIM_LIBRARY + u"'"_s; + static inline const QString GAME_AND_ANIM_FILTER = u"("_s + GAME_ONLY_FILTER + u" OR "_s + ANIM_ONLY_FILTER + u")"_s; // Error - static inline const QString ERR_DATABASE = "Flashpoint Database Error:"; - static inline const QString ERR_MISSING_TABLE = "The Flashpoint database is missing expected tables."; - static inline const QString ERR_TABLE_MISSING_COLUMN = "The Flashpoint database tables are missing expected columns."; - + static inline const QString ERR_MISSING_TABLE = u"The Flashpoint database is missing expected tables."_s; + static inline const QString ERR_TABLE_MISSING_COLUMN = u"The Flashpoint database tables are missing expected columns."_s; + static inline const QString ERR_ID_NOT_FOUND = u"An entry matching the specified ID could not be found in the Flashpoint database."_s; + static inline const QString ERR_ID_DUPLICATE_ENTRY = u"This should not be possible and may indicate an error within the Flashpoint database"_s; //-Instance Variables----------------------------------------------------------------------------------------------- private: bool mValid; - Qx::GenericError mError; + DbError mError; // Database information QSet mConnectedThreads; const QString mDatabaseName; - QStringList mPlatformList; + QStringList mPlatformNames; QStringList mPlaylistList; QMap mTagMap; // Order matters for display in tag selector //-Constructor------------------------------------------------------------------------------------------------- public: - explicit Db(QString databaseName, const Key&); + explicit Db(const QString& databaseName, const Key&); //-Destructor------------------------------------------------------------------------------------------------- public: ~Db(); -//-Class Functions-------------------------------------------------------------------------------------------- -private: - QString threadConnectionName(const QThread* thread); - //-Instance Functions------------------------------------------------------------------------------------------------------ private: // Validity @@ -319,8 +334,9 @@ class FP_FP_EXPORT Db : public QObject // Connection void closeConnection(const QThread* thread); void closeAllConnections(); + QString threadConnectionName(const QThread* thread); QSqlError getThreadConnection(QSqlDatabase& connection); - QSqlError makeNonBindQuery(QueryBuffer& resultBuffer, QSqlDatabase* database, QString queryCommand, QString sizeQueryCommand) const; + QSqlError makeNonBindQuery(QueryBuffer& resultBuffer, QSqlDatabase* database, const QString& queryCommand, const QString& sizeQueryCommand) const; // Init QSqlError checkDatabaseForRequiredTables(QSet& missingTablesBuffer); @@ -331,33 +347,32 @@ class FP_FP_EXPORT Db : public QObject public: // Validity bool isValid(); - Qx::GenericError error(); + DbError error(); // TODO: See if these query functions can be consolidated via by better filtration arguments // Queries - OFLIb - QSqlError queryGamesByPlatform(QList& resultBuffer, QStringList platforms, InclusionOptions inclusionOptions, + DbError queryGamesByPlatform(QList& resultBuffer, const QStringList& platforms, const InclusionOptions& inclusionOptions, std::optional*> idInclusionFilter = std::nullopt); - QSqlError queryAllAddApps(QueryBuffer& resultBuffer); - QSqlError queryPlaylistsByName(QueryBuffer& resultBuffer, QStringList playlists, InclusionOptions inclusionOptions); - QSqlError queryPlaylistGamesByPlaylist(QList& resultBuffer, const QList& playlistIds); - QSqlError queryPlaylistGameIds(QueryBuffer& resultBuffer, const QList& playlistIds); - QSqlError queryAllEntryTags(QueryBuffer& resultBuffer); + DbError queryAllAddApps(QueryBuffer& resultBuffer); + DbError queryAllEntryTags(QueryBuffer& resultBuffer); // Queries - CLIFp - QSqlError queryEntrys(QueryBuffer& resultBuffer, EntryFilter filter); - QSqlError queryEntryDataById(QueryBuffer& resultBuffer, QUuid appId); - QSqlError queryDataPackSource(QueryBuffer& resultBuffer); - QSqlError queryEntrySourceData(QueryBuffer& resultBuffer, QString appSha256Hex); - QSqlError queryAllGameIds(QueryBuffer& resultBuffer, LibraryFilter filter); + DbError queryEntrys(QueryBuffer& resultBuffer, const EntryFilter& filter); + DbError queryEntryDataById(QueryBuffer& resultBuffer, const QUuid& appId); + DbError queryAllGameIds(QueryBuffer& resultBuffer, const LibraryFilter& filter); // Info - QStringList platformList() const; - QStringList playlistList() const; + QStringList platformNames() const; QMap tags() const; // Checks - QSqlError entryUsesDataPack(bool& resultBuffer, QUuid gameId); + DbError entryUsesDataPack(bool& resultBuffer, const QUuid& gameId); + + // Helper + DbError getEntry(std::variant& entry, const QUuid& entryId); + DbError getGameData(GameData& data, const QUuid& gameId); + DbError updateGameDataOnDiskState(int packId, bool onDisk); //-Slots ------------------------------------------------------------------------------------------------------ private: diff --git a/lib/include/fp/fp-install.h b/lib/include/fp/fp-install.h index 4ac4ba1..089030c 100644 --- a/lib/include/fp/fp-install.h +++ b/lib/include/fp/fp-install.h @@ -12,17 +12,22 @@ // Qx Includes #include +#include // Project Includes -#include "fp/fp-json.h" +#include "fp/settings/fp-config.h" +#include "fp/settings/fp-execs.h" +#include "fp/settings/fp-preferences.h" +#include "fp/settings/fp-services.h" #include "fp/fp-macro.h" #include "fp/fp-db.h" #include "fp/fp-items.h" +#include "fp/fp-playlistmanager.h" namespace Fp { -inline const QString NAME = QStringLiteral("Flashpoint"); +inline const QString NAME = u"Flashpoint"_s; class FP_FP_EXPORT Install { @@ -32,61 +37,59 @@ enum class Edition {Ultimate, Infinity, Core}; //-Class Variables----------------------------------------------------------------------------------------------- public: // Ugh #if defined _WIN32 - static inline const QString LAUNCHER_NAME = "Flashpoint.exe"; + static inline const QString LAUNCHER_NAME = u"Flashpoint.exe"_s; #elif defined __linux__ - static inline const QString LAUNCHER_NAME = "flashpoint-launcher"; + static inline const QString LAUNCHER_NAME = u"flashpoint-launcher"_s; #endif private: - - // Validity check fail reasons - static inline const QString FILE_DNE = "A required file does not exist: %1"; - // Static paths - static inline const QString LAUNCHER_PATH = "Launcher/" + LAUNCHER_NAME; - static inline const QString DATABASE_PATH = "Data/flashpoint.sqlite"; - static inline const QString CONFIG_JSON_PATH = "Launcher/config.json"; - static inline const QString PREFERENCES_JSON_PATH = "preferences.json"; - static inline const QString VER_TXT_PATH = "version.txt"; + static inline const QString LAUNCHER_PATH = u"Launcher/"_s + LAUNCHER_NAME; + static inline const QString DATABASE_PATH = u"Data/flashpoint.sqlite"_s; + static inline const QString CONFIG_JSON_PATH = u"Launcher/config.json"_s; + static inline const QString PREFERENCES_JSON_PATH = u"preferences.json"_s; + static inline const QString VER_TXT_PATH = u"version.txt"_s; // File Info - static inline const QString IMAGE_EXT = ".png"; - + static inline const QString IMAGE_UC_EXT = u".png"_s; + static inline const QString IMAGE_C_EXT = u".jpg"_s; + static inline const QString IMAGE_C_URL_SUFFIX = u"?type=jpg"_s; // Dynamic path file names - static inline const QString SERVICES_JSON_NAME = "services.json"; - static inline const QString EXECS_JSON_NAME = "execs.json"; + static inline const QString SERVICES_JSON_NAME = u"services.json"_s; + static inline const QString EXECS_JSON_NAME = u"execs.json"_s; // Static Folders - static inline const QString EXTRAS_PATH = "Extras"; + static inline const QString EXTRAS_PATH = u"Extras"_s; // Dynamic path folder names - static inline const QString LOGOS_FOLDER_NAME = "Logos"; - static inline const QString SCREENSHOTS_FOLDER_NAME = "Screenshots"; - - // Settings - static inline const QString MACRO_FP_PATH = ""; + static inline const QString LOGOS_FOLDER_NAME = u"Logos"_s; + static inline const QString SCREENSHOTS_FOLDER_NAME = u"Screenshots"_s; // Error - static inline const QString ERR_INVALID = "Invalid Flashpoint Install:"; + static inline const QString ERR_FILE_MISSING = u"A required flashpoint install file is missing."_s; + + // Settings + static inline const QString MACRO_FP_PATH = u""_s; // Regex - static inline const QRegularExpression VERSION_NUMBER_REGEX = QRegularExpression("[fF]lashpoint (?.*?) "); + static inline const QRegularExpression VERSION_NUMBER_REGEX = QRegularExpression(u"[fF]lashpoint (?.*?) "_s); public: - static inline const QFileInfo SECURE_PLAYER_INFO = QFileInfo("FlashpointSecurePlayer.exe"); + static inline const QFileInfo SECURE_PLAYER_INFO = QFileInfo(u"FlashpointSecurePlayer.exe"_s); //-Instance Variables----------------------------------------------------------------------------------------------- private: // Validity bool mValid; - Qx::GenericError mError; + Qx::Error mError; // Files and directories QDir mRootDirectory; QDir mLogosDirectory; QDir mScreenshotsDirectory; QDir mExtrasDirectory; + QDir mPlaylistsDirectory; std::unique_ptr mLauncherFile; std::unique_ptr mDatabaseFile; std::shared_ptr mConfigJsonFile; @@ -96,20 +99,23 @@ enum class Edition {Ultimate, Infinity, Core}; std::unique_ptr mVersionFile; // Settings - Json::Config mConfig; - Json::Preferences mPreferences; - Json::Services mServices; - Json::Execs mExecs; + Config mConfig; + Preferences mPreferences; + Services mServices; + Execs mExecs; // Database Db* mDatabase = nullptr; + // Playlist Manager + PlaylistManager* mPlaylistManager = nullptr; + // Utilities MacroResolver* mMacroResolver = nullptr; //-Constructor------------------------------------------------------------------------------------------------- public: - Install(QString installPath); + Install(QString installPath, bool preloadPlaylists = false); //-Destructor------------------------------------------------------------------------------------------------- public: @@ -117,10 +123,10 @@ enum class Edition {Ultimate, Infinity, Core}; //-Class Functions------------------------------------------------------------------------------------------------------ private: - static QString standardImageSubPath(ImageType imageType, QUuid gameId); + static QString standardImageSubPath(QUuid gameId); public: - static Qx::GenericError appInvolvesSecurePlayer(bool& involvesBuffer, QFileInfo appInfo); + static Qx::Error appInvolvesSecurePlayer(bool& involvesBuffer, QFileInfo appInfo); //-Instance Functions------------------------------------------------------------------------------------------------------ private: @@ -129,7 +135,7 @@ enum class Edition {Ultimate, Infinity, Core}; public: // Validity bool isValid() const; - Qx::GenericError error() const; + Qx::Error error() const; // General information Edition edition() const; @@ -140,20 +146,23 @@ enum class Edition {Ultimate, Infinity, Core}; // Database Db* database(); + // Playlist Manager + PlaylistManager* playlistManager(); + // Support Application Checks // TODO: At some point create a "Settings" object that wraps all of these, would need to rename existing Fp::Settings - const Json::Config& config() const; - const Json::Preferences& preferences() const; - const Json::Services& services() const; - const Json::Execs& execs() const; + const Config& config() const; + const Preferences& preferences() const; + const Services& services() const; + const Execs& execs() const; // Data access QString fullPath() const; QDir logosDirectory() const; QDir screenshotsDirectory() const; QDir extrasDirectory() const; - QString imageLocalPath(ImageType imageType, QUuid gameId) const; - QUrl imageRemoteUrl(ImageType imageType, QUuid gameId) const; + QString imageLocalPath(ImageType imageType, const QUuid& gameId) const; + QUrl imageRemoteUrl(ImageType imageType, const QUuid& gameId) const; const MacroResolver* macroResolver() const; // Helper diff --git a/lib/include/fp/fp-items.h b/lib/include/fp/fp-items.h index aa41f26..9f0fdc5 100644 --- a/lib/include/fp/fp-items.h +++ b/lib/include/fp/fp-items.h @@ -9,6 +9,8 @@ #include #include +using namespace Qt::Literals::StringLiterals; + namespace Fp { //-Enums---------------------------------------------------------------------------------------------------------- @@ -30,7 +32,6 @@ class FP_FP_EXPORT Game QString mPublisher; QDateTime mDateAdded; QDateTime mDateModified; - QString mPlatform; bool mBroken; QString mPlayMode; QString mStatus; @@ -44,6 +45,7 @@ class FP_FP_EXPORT Game QString mLanguage; QString mOrderTitle; QString mLibrary; + QString mPlatformName; //-Constructor------------------------------------------------------------------------------------------------- public: @@ -58,7 +60,6 @@ class FP_FP_EXPORT Game QString publisher() const; QDateTime dateAdded() const; QDateTime dateModified() const; - QString platform() const; bool isBroken() const; QString playMode() const; QString status() const; @@ -72,6 +73,7 @@ class FP_FP_EXPORT Game QString language() const; QString orderTitle() const; QString library() const; + QString platformName() const; }; class FP_FP_EXPORT Game::Builder @@ -86,35 +88,108 @@ class FP_FP_EXPORT Game::Builder //-Class Functions--------------------------------------------------------------------------------------------- private: - static QString kosherizeRawDate(QString date); + static QString kosherizeRawDate(const QString& date); //-Instance Functions------------------------------------------------------------------------------------------ public: - Builder& wId(QString rawId); - Builder& wTitle(QString title); - Builder& wSeries(QString series); - Builder& wDeveloper(QString developer); - Builder& wPublisher(QString publisher); - Builder& wDateAdded(QString rawDateAdded); - Builder& wDateModified(QString rawDateModified); - Builder& wPlatform(QString platform); - Builder& wBroken(QString rawBroken); - Builder& wPlayMode(QString playMode); - Builder& wStatus(QString status); - Builder& wNotes(QString notes); - Builder& wSource(QString source); - Builder& wAppPath(QString appPath); - Builder& wLaunchCommand(QString launchCommand); - Builder& wReleaseDate(QString rawReleaseDate); - Builder& wVersion(QString version); - Builder& wOriginalDescription(QString originalDescription); - Builder& wLanguage(QString language); - Builder& wOrderTitle(QString orderTitle); - Builder& wLibrary(QString library); + Builder& wId(QStringView rawId); + Builder& wTitle(const QString& title); + Builder& wSeries(const QString& series); + Builder& wDeveloper(const QString& developer); + Builder& wPublisher(const QString& publisher); + Builder& wDateAdded(QStringView rawDateAdded); + Builder& wDateModified(QStringView rawDateModified); + Builder& wBroken(QStringView rawBroken); + Builder& wPlayMode(const QString& playMode); + Builder& wStatus(const QString& status); + Builder& wNotes(const QString& notes); + Builder& wSource(const QString& source); + Builder& wAppPath(const QString& appPath); + Builder& wLaunchCommand(const QString& launchCommand); + Builder& wReleaseDate(QStringView rawReleaseDate); + Builder& wVersion(const QString& version); + Builder& wOriginalDescription(const QString& originalDescription); + Builder& wLanguage(const QString& language); + Builder& wOrderTitle(const QString& orderTitle); + Builder& wLibrary(const QString& library); + Builder& wPlatformName(const QString& platformName); Game build(); }; +class FP_FP_EXPORT GameData +{ +//-Inner Classes---------------------------------------------------------------------------------------------------- +public: + class Builder; + +//-Instance Variables----------------------------------------------------------------------------------------------- +private: + bool mNull; + + quint32 mId; + QUuid mGameId; + QString mTitle; + QDateTime mDateAdded; + QString mSha256; + quint32 mCrc32; + bool mPresentOnDisk; + QString mPath; + quint32 mSize; + QString mParameters; + QString mAppPath; + QString mLaunchCommand; + +//-Constructor------------------------------------------------------------------------------------------------- +public: + GameData(); + +//-Instance Functions------------------------------------------------------------------------------------------ +public: + bool isNull() const; + + quint32 id() const; + QUuid gameId() const; + QString title() const; + QDateTime dateAdded() const; + QString sha256() const; + quint32 crc32() const; + bool presentOnDisk() const; + QString path() const; + quint32 size() const; + QString parameters() const; + QString appPath() const; + QString launchCommand() const; +}; + +class FP_FP_EXPORT GameData::Builder +{ +//-Instance Variables------------------------------------------------------------------------------------------ +private: + GameData mGameDataBlueprint; + +//-Constructor------------------------------------------------------------------------------------------------- +public: + Builder(); + +//-Instance Functions------------------------------------------------------------------------------------------ +public: + Builder& wId(QStringView rawId); + Builder& wGameId(QStringView rawId); + Builder& wTitle(const QString& title); + Builder& wDateAdded(QStringView rawDateAdded); + Builder& wSha256(const QString& sha256); + Builder& wCrc32(QStringView rawCrc32); + Builder& wPresentOnDisk(QStringView rawBroken); + Builder& wPath(const QString& path); + Builder& wSize(QStringView rawSize); + Builder& wParameters(const QString& parameters); + Builder& wAppPath(const QString& appPath); + Builder& wLaunchCommand(const QString& launchCommand); + + GameData build(); +}; + class FP_FP_EXPORT AddApp { //-Inner Classes---------------------------------------------------------------------------------------------------- @@ -123,8 +198,8 @@ class FP_FP_EXPORT AddApp //-Class Variables----------------------------------------------------------------------------------------------- private: - QString SPEC_PATH_MSG = ":message:"; - QString SPEC_PATH_EXTRA = ":extras:"; + static inline const QString SPEC_PATH_MSG = u":message:"_s; + static inline const QString SPEC_PATH_EXTRA = u":extras:"_s; //-Instance Variables----------------------------------------------------------------------------------------------- private: @@ -172,13 +247,13 @@ class FP_FP_EXPORT AddApp::Builder //-Instance Functions------------------------------------------------------------------------------------------ public: - Builder& wId(QString rawId); - Builder& wAppPath(QString appPath); - Builder& wAutorunBefore(QString rawAutorunBefore); - Builder& wLaunchCommand(QString launchCommand); - Builder& wName(QString name); - Builder& wWaitExit(QString rawWaitExit); - Builder& wParentId(QString rawParentId); + Builder& wId(QStringView rawId); + Builder& wAppPath(const QString& appPath); + Builder& wAutorunBefore(QStringView rawAutorunBefore); + Builder& wLaunchCommand(const QString& launchCommand); + Builder& wName(const QString& name); + Builder& wWaitExit(QStringView rawWaitExit); + Builder& wParentId(QStringView rawParentId); AddApp build(); }; @@ -223,53 +298,52 @@ class FP_FP_EXPORT Set::Builder Set build(); }; -class FP_FP_EXPORT Playlist +class FP_FP_EXPORT PlaylistGame { -//-Inner Classes---------------------------------------------------------------------------------------------------- + //-Inner Classes---------------------------------------------------------------------------------------------------- public: class Builder; -//-Instance Variables----------------------------------------------------------------------------------------------- + //-Instance Variables----------------------------------------------------------------------------------------------- private: - QUuid mId; - QString mTitle; - QString mDescription; - QString mAuthor; + int mId; + QUuid mPlaylistId; + int mOrder; + QUuid mGameId; -//-Constructor------------------------------------------------------------------------------------------------- + //-Constructor------------------------------------------------------------------------------------------------- public: - Playlist(); + PlaylistGame(); -//-Instance Functions------------------------------------------------------------------------------------------------------ + //-Instance Functions------------------------------------------------------------------------------------------------------ public: - QUuid id() const; - QString title() const; - QString description() const; - QString author() const; - + int id() const; + QUuid playlistId() const; + int order() const; + QUuid gameId() const; }; -class FP_FP_EXPORT Playlist::Builder +class FP_FP_EXPORT PlaylistGame::Builder { -//-Instance Variables------------------------------------------------------------------------------------------ + //-Instance Variables------------------------------------------------------------------------------------------ private: - Playlist mPlaylistBlueprint; + PlaylistGame mPlaylistGameBlueprint; -//-Constructor------------------------------------------------------------------------------------------------- + //-Constructor------------------------------------------------------------------------------------------------- public: Builder(); -//-Instance Functions------------------------------------------------------------------------------------------ + //-Instance Functions------------------------------------------------------------------------------------------ public: - Builder& wId(QString rawId); - Builder& wTitle(QString title); - Builder& wDescription(QString description); - Builder& wAuthor(QString author); + Builder& wId(int id); + Builder& wPlaylistId(QStringView rawPlaylistId); + Builder& wOrder(int order); + Builder& wGameId(QStringView rawGameId); - Playlist build(); + PlaylistGame build(); }; -class FP_FP_EXPORT PlaylistGame +class FP_FP_EXPORT Playlist { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -277,28 +351,35 @@ class FP_FP_EXPORT PlaylistGame //-Instance Variables----------------------------------------------------------------------------------------------- private: - int mId; - QUuid mPlaylistId; - int mOrder; - QUuid mGameId; + QUuid mId; + QString mTitle; + QString mDescription; + QString mAuthor; + QString mLibrary; + + QList mPlaylistGames; //-Constructor------------------------------------------------------------------------------------------------- public: - PlaylistGame(); + Playlist(); //-Instance Functions------------------------------------------------------------------------------------------------------ public: - int id() const; - QUuid playlistId() const; - int order() const; - QUuid gameId() const; + QUuid id() const; + QString title() const; + QString description() const; + QString author() const; + QString library() const; + const QList& playlistGames() const; + QList& playlistGames(); + }; -class FP_FP_EXPORT PlaylistGame::Builder +class FP_FP_EXPORT Playlist::Builder { //-Instance Variables------------------------------------------------------------------------------------------ private: - PlaylistGame mPlaylistGameBlueprint; + Playlist mPlaylistBlueprint; //-Constructor------------------------------------------------------------------------------------------------- public: @@ -306,12 +387,14 @@ class FP_FP_EXPORT PlaylistGame::Builder //-Instance Functions------------------------------------------------------------------------------------------ public: - Builder& wId(QString rawId); - Builder& wPlaylistId(QString rawPlaylistId); - Builder& wOrder(QString rawOrder); - Builder& wGameId(QString rawGameId); + Builder& wId(QStringView rawId); + Builder& wTitle(const QString& title); + Builder& wDescription(const QString& description); + Builder& wAuthor(const QString& author); + Builder& wLibrary(const QString& library); + Builder& wPlaylistGame(const PlaylistGame& playlistGame); - PlaylistGame build(); + Playlist build(); }; } diff --git a/lib/include/fp/fp-json.h b/lib/include/fp/fp-json.h deleted file mode 100644 index 2260d3b..0000000 --- a/lib/include/fp/fp-json.h +++ /dev/null @@ -1,262 +0,0 @@ -#ifndef FLASHPOINT_JSON_H -#define FLASHPOINT_JSON_H - -// Shared Lib Support -#include "fp/fp_export.h" - -// Qt Includes -#include -#include -#include - -// Qx Includes -#include - -// Project Includes -#include "fp/fp-macro.h" - -/* Remove the ancient built-in 'linux' define to avoid clash with exec.linux. - * No one should still be using it anyway and instead using __linux__. - */ -#undef linux - -namespace Fp -{ - -enum KnownDaemon{ - None = 0x0, - Docker = 0x1, - Qemu = 0x2 -}; -Q_DECLARE_FLAGS(KnownDaemons, KnownDaemon); -Q_DECLARE_OPERATORS_FOR_FLAGS(KnownDaemons); - -class FP_FP_EXPORT Json -{ -//-Inner Classes------------------------------------------------------------------------------------------------- -private: - class Object_Config - { - public: - static inline const QString KEY_FLASHPOINT_PATH = "flashpointPath"; // Reading this value is current redundant and unused, but this may change in the future - static inline const QString KEY_START_SERVER = "startServer"; - static inline const QString KEY_SERVER = "server"; - }; - - class Object_AppPathOverrides - { - public: - static inline const QString KEY_PATH = "path"; - static inline const QString KEY_OVERRIDE = "override"; - static inline const QString KEY_ENABLED = "enabled"; - }; - - class Object_Preferences - { - public: - static inline const QString KEY_IMAGE_FOLDER_PATH = "imageFolderPath"; - static inline const QString KEY_JSON_FOLDER_PATH = "jsonFolderPath"; - static inline const QString KEY_HTDOCS_FOLDER_PATH = "htdocsFolderPath"; - static inline const QString KEY_DATA_PACKS_FOLDER_PATH = "dataPacksFolderPath"; - static inline const QString KEY_ON_DEMAND_IMAGES = "onDemandImages"; - static inline const QString KEY_ON_DEMAND_BASE_URL = "onDemandBaseUrl"; - static inline const QString KEY_APP_PATH_OVERRIDES = "appPathOverrides"; - static inline const QString KEY_NATIVE_PLATFORMS = "nativePlatforms"; - static inline const QString KEY_BROWSER_MODE_PROXY = "browserModeProxy"; - }; - - class Object_ServerDaemon - { - public: - static inline const QString KEY_NAME = "name"; - static inline const QString KEY_PATH = "path"; - static inline const QString KEY_FILENAME = "filename"; - static inline const QString KEY_ARGUMENTS = "arguments"; - static inline const QString KEY_KILL = "kill"; - }; - - class Object_StartStop - { - public: - static inline const QString KEY_PATH = "path"; - static inline const QString KEY_FILENAME = "filename"; - static inline const QString KEY_ARGUMENTS = "arguments"; - }; - - class Object_Services - { - public: - static inline const QString KEY_WATCH = "watch"; - static inline const QString KEY_SERVER = "server"; - static inline const QString KEY_DAEMON = "daemon"; - static inline const QString KEY_START = "start"; - static inline const QString KEY_STOP = "stop"; - }; - - class Object_Execs - { - public: - static inline const QString KEY_EXECS = "execs"; - }; - - class Object_Exec - { - public: - static inline const QString KEY_WIN32 = "win32"; - static inline const QString KEY_LINUX = "linux"; - static inline const QString KEY_WINE = "wine"; - }; - - struct Settings {}; - -public: - struct ServerDaemon - { - QString name; - QString path; - QString filename; - QStringList arguments; - bool kill; - }; - - struct StartStop - { - QString path; - QString filename; - QStringList arguments; - - friend bool operator== (const StartStop& lhs, const StartStop& rhs) noexcept; - friend size_t qHash(const StartStop& key, size_t seed) noexcept; - }; - - struct Config : public Settings - { - QString flashpointPath; - bool startServer; - QString server; - }; - - struct AppPathOverride - { - QString path; - QString override; - bool enabled; - }; - - struct Preferences : public Settings - { - QString imageFolderPath; - QString jsonFolderPath; - QString htdocsFolderPath; - QString dataPacksFolderPath; - bool onDemandImages; - QString onDemandBaseUrl; - QList appPathOverrides; - QSet nativePlatforms; - QString browserModeProxy; - }; - - struct Services : public Settings - { - //QSet watches; - QHash servers; - QHash daemons; - QSet starts; - QSet stops; - KnownDaemons recognizedDaemons; // Non-standard - // TODO: If Settings container obj is made (see other todo), move this there - }; - - struct Exec - { - QString linux; - QString win32; - QString wine; - }; - - struct Execs : public Settings - { - QList list; - }; - - class SettingsReader - { - //-Class variables----------------------------------------------------------------------------------------------------- - public: - static inline const QString ERR_PARSING_JSON_DOC = "Error parsing JSON Document: %1"; - static inline const QString ERR_JSON_UNEXP_FORMAT = "Unexpected document format"; - - //-Instance Variables-------------------------------------------------------------------------------------------------- - protected: - Settings* mTargetSettings; - std::shared_ptr mSourceJsonFile; - - //-Constructor-------------------------------------------------------------------------------------------------------- - public: - SettingsReader(Settings* targetSettings, std::shared_ptr sourceJsonFile); - - //-Instance Functions------------------------------------------------------------------------------------------------- - private: - virtual Qx::GenericError parseDocument(const QJsonDocument& jsonDoc) = 0; - - public: - Qx::GenericError readInto(); - - }; - - class ConfigReader : public SettingsReader - { - //-Constructor-------------------------------------------------------------------------------------------------------- - public: - ConfigReader(Config* targetConfig, std::shared_ptr sourceJsonFile); - - //-Instance Functions------------------------------------------------------------------------------------------------- - private: - Qx::GenericError parseDocument(const QJsonDocument& configDoc); - }; - - class PreferencesReader : public SettingsReader - { - //-Constructor-------------------------------------------------------------------------------------------------------- - public: - PreferencesReader(Preferences* targetPreferences, std::shared_ptr sourceJsonFile); - - //-Instance Functions------------------------------------------------------------------------------------------------- - private: - Qx::GenericError parseDocument(const QJsonDocument& prefDoc); - Qx::GenericError parseAppPathOverride(AppPathOverride& apoBuffer, const QJsonValue& jvApo); - Qx::GenericError parseNativePlatform(QString& nativePlatformBuffer, const QJsonValue& jvNativePlatform); - }; - - class ServicesReader : public SettingsReader - { - //-Instance Variables-------------------------------------------------------------------------------------------------- - private: - const MacroResolver* mHostMacroResolver; - - //-Constructor-------------------------------------------------------------------------------------------------------- - public: - ServicesReader(Services* targetServices, std::shared_ptr sourceJsonFile, const MacroResolver* macroResolver); - - //-Instance Functions------------------------------------------------------------------------------------------------- - private: - Qx::GenericError parseDocument(const QJsonDocument& servicesDoc); - Qx::GenericError parseServerDaemon(ServerDaemon& serverBuffer, const QJsonValue& jvServer); - Qx::GenericError parseStartStop(StartStop& startStopBuffer, const QJsonValue& jvStartStop); - }; - - class ExecsReader : public SettingsReader - { - //-Constructor-------------------------------------------------------------------------------------------------------- - public: - ExecsReader(Execs* targetExecs, std::shared_ptr sourceJsonFile); - - //-Instance Functions------------------------------------------------------------------------------------------------- - private: - Qx::GenericError parseDocument(const QJsonDocument& execsDoc); - Qx::GenericError parseExec(Exec& execBuffer, const QJsonValue& jvExec); - }; -}; -} - -#endif // FLASHPOINT_JSON_H diff --git a/lib/include/fp/fp-macro.h b/lib/include/fp/fp-macro.h index 0d4e8ae..1b0387b 100644 --- a/lib/include/fp/fp-macro.h +++ b/lib/include/fp/fp-macro.h @@ -7,6 +7,8 @@ // Qt Includes #include +using namespace Qt::Literals::StringLiterals; + namespace Fp { @@ -24,7 +26,7 @@ class FP_FP_EXPORT MacroResolver //-Class Variables----------------------------------------------------------------------------------------------- private: - static inline const QString FP_PATH = ""; + static inline const QString FP_PATH = u""_s; //-Instance Variables----------------------------------------------------------------------------------------------- private: @@ -32,7 +34,7 @@ class FP_FP_EXPORT MacroResolver //-Constructor------------------------------------------------------------------------------------------------- public: - MacroResolver(QString installPath, const Key&); // Will need to be improved if many more macros are added + MacroResolver(const QString& installPath, const Key&); // Will need to be improved if many more macros are added //-Instance Functions------------------------------------------------------------------------------------------------------ public: diff --git a/lib/include/fp/fp-playlistmanager.h b/lib/include/fp/fp-playlistmanager.h new file mode 100644 index 0000000..6fc774b --- /dev/null +++ b/lib/include/fp/fp-playlistmanager.h @@ -0,0 +1,57 @@ +#ifndef FLASHPOINT_PLAYLISTMANAGER_H +#define FLASHPOINT_PLAYLISTMANAGER_H + +// Shared Lib Support +#include "fp/fp_export.h" + +// Qt Includes +#include + +// Qx Includes +#include + +// Project Includes +#include "fp/fp-items.h" + +namespace Fp +{ + +class FP_FP_EXPORT PlaylistManager +{ +//-Inner Classes------------------------------------------------------------------------------------------------- +public: + class Key + { + friend class Install; + private: + Key() {}; + Key(const Key&) = default; + }; + +//-Class Variables----------------------------------------------------------------------------------------------- +private: + +//-Instance Variables----------------------------------------------------------------------------------------------- +private: + bool mPopulated; + QDir mFolder; + QList mPlaylists; + QStringList mTitles; + +//-Constructor------------------------------------------------------------------------------------------------- +public: + explicit PlaylistManager(const QDir& folder, const Key&); + +//-Instance Functions------------------------------------------------------------------------------------------------------ +private: + +public: + bool isPopulated() const; + Qx::Error populate(); + QList playlists() const; + QStringList playlistTitles() const; +}; + +} + +#endif // FLASHPOINT_PLAYLISTMANAGER_H diff --git a/lib/include/fp/settings/fp-config.h b/lib/include/fp/settings/fp-config.h new file mode 100644 index 0000000..da05e22 --- /dev/null +++ b/lib/include/fp/settings/fp-config.h @@ -0,0 +1,30 @@ +#ifndef FLASHPOINT_CONFIG_H +#define FLASHPOINT_CONFIG_H + +// Project Includes +#include "fp/settings/fp-settings.h" + +namespace Fp +{ + +struct FP_FP_EXPORT Config : public Settings +{ + QString flashpointPath; + bool startServer; + QString server; +}; + +class FP_FP_EXPORT ConfigReader : public SettingsReader +{ +//-Constructor-------------------------------------------------------------------------------------------------------- +public: + ConfigReader(Config* targetConfig, std::shared_ptr sourceJsonFile); + +//-Instance Functions------------------------------------------------------------------------------------------------- +private: + Qx::JsonError parseDocument(const QJsonDocument& configDoc); +}; + +} + +#endif // FLASHPOINT_CONFIG_H diff --git a/lib/include/fp/settings/fp-execs.h b/lib/include/fp/settings/fp-execs.h new file mode 100644 index 0000000..44e9032 --- /dev/null +++ b/lib/include/fp/settings/fp-execs.h @@ -0,0 +1,40 @@ +#ifndef FLASHPOINT_EXECS_H +#define FLASHPOINT_EXECS_H + +// Project Includes +#include "fp/settings/fp-settings.h" + +/* Remove the ancient built-in 'linux' define to avoid clash with Exec.linux. + * No one should still be using it anyway and instead using __linux__. + */ +#undef linux + +namespace Fp +{ + +struct FP_FP_EXPORT Exec +{ + QString linux; + QString win32; + QString wine; +}; + +struct FP_FP_EXPORT Execs : public Settings +{ + QList list; +}; + +class FP_FP_EXPORT ExecsReader : public SettingsReader +{ +//-Constructor-------------------------------------------------------------------------------------------------------- +public: + ExecsReader(Execs* targetExecs, std::shared_ptr sourceJsonFile); + +//-Instance Functions------------------------------------------------------------------------------------------------- +private: + Qx::JsonError parseDocument(const QJsonDocument& execsDoc); +}; + +} + +#endif // FLASHPOINT_EXECS_H diff --git a/lib/include/fp/settings/fp-preferences.h b/lib/include/fp/settings/fp-preferences.h new file mode 100644 index 0000000..0300a00 --- /dev/null +++ b/lib/include/fp/settings/fp-preferences.h @@ -0,0 +1,74 @@ +#ifndef FLASHPOINT_PREFERENCES_H +#define FLASHPOINT_PREFERENCES_H + +// Qt Includes +#include + +// Project Includes +#include "fp/settings/fp-settings.h" + +namespace Fp +{ + +struct FP_FP_EXPORT AppPathOverride +{ + QString path; + QString override; + bool enabled; +}; + +struct FP_FP_EXPORT GameDataSource +{ + QList arguments; + QString name; + QString type; +}; + +//struct FP_FP_EXPORT GameMetadataSource_GamesTags +//{ +// QDateTime actualUpdateTime; +// QDateTime latestDeleteTime; +// QDateTime latestUpdateTime; +//}; + +struct FP_FP_EXPORT GameMetadataSource +{ + QString baseUrl; + //GameMetadataSource_GamesTags games; + QString name; + //GameMetadataSource_GamesTags tags; +}; + +struct FP_FP_EXPORT Preferences : public Settings +{ + QString fpfssBaseUrl; + QHash gameDataSources; + QHash gameMetadataSources; + QString imageFolderPath; + QString playlistFolderPath; + QString jsonFolderPath; + QString htdocsFolderPath; + QString dataPacksFolderPath; + bool onDemandImages; + bool onDemandImagesCompressed; + QString onDemandBaseUrl; + QList appPathOverrides; + QSet nativePlatforms; + QString browserModeProxy; + QString server; +}; + +class FP_FP_EXPORT PreferencesReader : public SettingsReader +{ +//-Constructor-------------------------------------------------------------------------------------------------------- +public: + PreferencesReader(Preferences* targetPreferences, std::shared_ptr sourceJsonFile); + +//-Instance Functions------------------------------------------------------------------------------------------------- +private: + Qx::JsonError parseDocument(const QJsonDocument& prefDoc); +}; + +} + +#endif // FLASHPOINT_PREFERENCES_H diff --git a/lib/include/fp/settings/fp-services.h b/lib/include/fp/settings/fp-services.h new file mode 100644 index 0000000..ef6b349 --- /dev/null +++ b/lib/include/fp/settings/fp-services.h @@ -0,0 +1,69 @@ +#ifndef FLASHPOINT_SERVICES_H +#define FLASHPOINT_SERVICES_H + +// Qt Includes +#include + +// Project Includes +#include "fp/settings/fp-settings.h" +#include "fp/fp-macro.h" + +namespace Fp +{ + +enum KnownDaemon{ + None = 0x0, + Docker = 0x1, + Qemu = 0x2 +}; +Q_DECLARE_FLAGS(KnownDaemons, KnownDaemon); +Q_DECLARE_OPERATORS_FOR_FLAGS(KnownDaemons); + +struct FP_FP_EXPORT ServerDaemon +{ + QString name; + QString path; + QString filename; + QStringList arguments; + bool kill; +}; + +struct FP_FP_EXPORT StartStop +{ + QString path; + QString filename; + QStringList arguments; + + friend bool operator== (const StartStop& lhs, const StartStop& rhs) noexcept; + friend size_t qHash(const StartStop& key, size_t seed) noexcept; +}; + +struct FP_FP_EXPORT Services : public Settings +{ + //QSet watches; + QHash server; + QHash daemon; + QSet start; + QSet stop; + KnownDaemons recognizedDaemons; // Non-standard + // TODO: ^If Settings container obj is made (see other todo), move this there +}; + +class FP_FP_EXPORT ServicesReader : public SettingsReader +{ +//-Instance Variables-------------------------------------------------------------------------------------------------- +private: + const MacroResolver* mHostMacroResolver; + +//-Constructor-------------------------------------------------------------------------------------------------------- +public: + ServicesReader(Services* targetServices, std::shared_ptr sourceJsonFile, const MacroResolver* macroResolver); + +//-Instance Functions------------------------------------------------------------------------------------------------- +private: + Qx::JsonError parseDocument(const QJsonDocument& servicesDoc); +}; + +} + +#endif // FLASHPOINT_SERVICES_H diff --git a/lib/include/fp/settings/fp-settings.h b/lib/include/fp/settings/fp-settings.h new file mode 100644 index 0000000..52e9747 --- /dev/null +++ b/lib/include/fp/settings/fp-settings.h @@ -0,0 +1,44 @@ +#ifndef FLASHPOINT_SETTINGS_H +#define FLASHPOINT_SETTINGS_H + +// Shared Lib Support +#include "fp/fp_export.h" + +// Qt Includes +#include +#include +#include +#include + +// Qx Includes +#include +#include +#include + +namespace Fp +{ + +struct FP_FP_EXPORT Settings {}; + +class FP_FP_EXPORT SettingsReader +{ +//-Instance Variables-------------------------------------------------------------------------------------------------- +protected: + Settings* mTargetSettings; + std::shared_ptr mSourceJsonFile; + +//-Constructor-------------------------------------------------------------------------------------------------------- +public: + SettingsReader(Settings* targetSettings, std::shared_ptr sourceJsonFile); + +//-Instance Functions------------------------------------------------------------------------------------------------- +private: + virtual Qx::JsonError parseDocument(const QJsonDocument& jsonDoc) = 0; + +public: + Qx::Error readInto(); +}; + +} + +#endif // FLASHPOINT_SETTINGS_H diff --git a/lib/src/fp-db.cpp b/lib/src/fp-db.cpp index 6034eb8..584eb7f 100644 --- a/lib/src/fp-db.cpp +++ b/lib/src/fp-db.cpp @@ -3,10 +3,50 @@ // Qx Includes #include +#include namespace Fp { +//=============================================================================================================== +// DbError +//=============================================================================================================== + +//-Constructor------------------------------------------------------------------------------------------------ +//Private: +DbError::DbError(Type t, const QString& c, const QString& d): + mType(t), + mCause(c), + mDetails(d) +{} + +//Public: +DbError::DbError() : + mType(NoError) +{} + +//-Class Functions--------------------------------------------------------------- +//Private: +DbError DbError::fromSqlError(const QSqlError& e) +{ + return e.isValid() ? DbError(SqlError, e.text()) : DbError(); +} + +//-Instance Functions------------------------------------------------------------------------------------------------ +//Private: +Qx::Severity DbError::deriveSeverity() const { return Qx::Critical; } +quint32 DbError::deriveValue() const { return mType; } +QString DbError::derivePrimary() const { return ERR_STRINGS.value(mType); } +QString DbError::deriveSecondary() const { return mCause; } +QString DbError::deriveDetails() const { return mDetails; } + +//Public: +bool DbError::isValid() const { return mType != NoError; } +DbError::Type DbError::type() const { return mType; } +QString DbError::cause() const { return mCause; } +QString DbError::details() const { return mDetails; } + + //=============================================================================================================== // DB::TAG_CATEGORY //=============================================================================================================== @@ -21,7 +61,7 @@ bool operator< (const Db::TagCategory& lhs, const Db::TagCategory& rhs) noexcept //-Constructor------------------------------------------------------------------------------------------------ //Public: -Db::Db(QString databaseName, const Key&) : +Db::Db(const QString& databaseName, const Key&) : QObject(), mValid(false), // Instance is invalid until proven otherwise mDatabaseName(databaseName) @@ -35,15 +75,15 @@ Db::Db(QString databaseName, const Key&) : QSet missingTables; if((databaseError = checkDatabaseForRequiredTables(missingTables)).isValid()) { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_DATABASE, databaseError.text()); + mError = DbError(DbError::SqlError, databaseError.text()); return; } // Check if tables are missing if(!missingTables.isEmpty()) { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_MISSING_TABLE, QString(), - QStringList(missingTables.begin(), missingTables.end()).join("\n")); + mError = DbError(DbError::InvalidSchema, ERR_MISSING_TABLE, + QStringList(missingTables.begin(), missingTables.end()).join(u"\n"_s)); return; } @@ -51,28 +91,28 @@ Db::Db(QString databaseName, const Key&) : QSet missingColumns; if((databaseError = checkDatabaseForRequiredColumns(missingColumns)).isValid()) { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_DATABASE, databaseError.text()); + mError = DbError(DbError::SqlError, databaseError.text()); return; } // Check if columns are missing if(!missingColumns.isEmpty()) { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_MISSING_TABLE, QString(), - QStringList(missingColumns.begin(), missingColumns.end()).join("\n")); + mError = DbError(DbError::InvalidSchema, ERR_MISSING_TABLE, + QStringList(missingColumns.begin(), missingColumns.end()).join(u"\n"_s)); return; } // Populate item members if((databaseError = populateAvailableItems()).isValid()) { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_DATABASE, databaseError.text()); + mError = DbError(DbError::SqlError, databaseError.text()); return; } if((databaseError = populateTags()).isValid()) { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_DATABASE, databaseError.text()); + mError = DbError(DbError::SqlError, databaseError.text()); return; } @@ -95,8 +135,8 @@ QString Db::threadConnectionName(const QThread* thread) // Important to also salt using instance "id" so that // different instances don't use the same connection return DATABASE_CONNECTION_NAME + - "_i" + QString::number((quint64)this, 16) + - "_t" + QString::number((quint64)thread, 16); + u"_i"_s + QString::number((quint64)this, 16) + + u"_t"_s + QString::number((quint64)thread, 16); } @@ -104,7 +144,7 @@ QString Db::threadConnectionName(const QThread* thread) //Private: void Db::nullify() { - mPlatformList.clear(); + mPlatformNames.clear(); mPlaylistList.clear(); mTagMap.clear(); } @@ -151,8 +191,8 @@ QSqlError Db::getThreadConnection(QSqlDatabase& connection) } else { - connection = QSqlDatabase::addDatabase("QSQLITE", tcn); - connection.setConnectOptions("QSQLITE_OPEN_READONLY"); + connection = QSqlDatabase::addDatabase(u"QSQLITE"_s, tcn); + //connection.setConnectOptions("QSQLITE_OPEN_READONLY"); Lib features some DB writing now connection.setDatabaseName(mDatabaseName); if(connection.open()) @@ -174,7 +214,7 @@ QSqlError Db::getThreadConnection(QSqlDatabase& connection) } } -QSqlError Db::makeNonBindQuery(QueryBuffer& resultBuffer, QSqlDatabase* database, QString queryCommand, QString sizeQueryCommand) const +QSqlError Db::makeNonBindQuery(QueryBuffer& resultBuffer, QSqlDatabase* database, const QString& queryCommand, const QString& sizeQueryCommand) const { // Create main query QSqlQuery mainQuery(*database); @@ -208,7 +248,7 @@ QSqlError Db::makeNonBindQuery(QueryBuffer& resultBuffer, QSqlDatabase* database //Public: bool Db::isValid() { return mValid; } -Qx::GenericError Db::error() { return mError; } +DbError Db::error() { return mError; } QSqlError Db::checkDatabaseForRequiredTables(QSet& missingTablesReturnBuffer) { @@ -237,7 +277,7 @@ QSqlError Db::checkDatabaseForRequiredTables(QSet& missingTablesReturnB return QSqlError(); } -QSqlError Db::checkDatabaseForRequiredColumns(QSet &missingColumsReturnBuffer) +QSqlError Db::checkDatabaseForRequiredColumns(QSet& missingColumsReturnBuffer) { // Ensure return buffer starts empty @@ -258,7 +298,7 @@ QSqlError Db::checkDatabaseForRequiredColumns(QSet &missingColumsReturn existingColumns.clear(); // Make column name query - QSqlQuery columnQuery("PRAGMA table_info(" + tableAndColumns.name + ")", fpDb); + QSqlQuery columnQuery(u"PRAGMA table_info("_s + tableAndColumns.name + u")"_s, fpDb); // Return if error occurs if(columnQuery.lastError().isValid()) @@ -266,12 +306,12 @@ QSqlError Db::checkDatabaseForRequiredColumns(QSet &missingColumsReturn // Parse query while(columnQuery.next()) - existingColumns.insert(columnQuery.value("name").toString()); + existingColumns.insert(columnQuery.value(u"name"_s).toString()); // Check for missing columns for(const QString& column : tableAndColumns.columns) if(!existingColumns.contains(column)) - missingColumsReturnBuffer.insert(tableAndColumns.name + ": " + column); + missingColumsReturnBuffer.insert(tableAndColumns.name + u": "_s + column); } @@ -288,11 +328,11 @@ QSqlError Db::populateAvailableItems() return dbError; // Ensure lists are reset - mPlatformList.clear(); + mPlatformNames.clear(); mPlaylistList.clear(); // Make platform query - QSqlQuery platformQuery("SELECT DISTINCT " + Table_Game::COL_PLATFORM + " FROM " + Table_Game::NAME, fpDb); + QSqlQuery platformQuery(u"SELECT DISTINCT "_s + Table_Game::COL_PLATFORM_NAME + u" FROM "_s + Table_Game::NAME, fpDb); // Return if error occurs if(platformQuery.lastError().isValid()) @@ -300,24 +340,10 @@ QSqlError Db::populateAvailableItems() // Parse query while(platformQuery.next()) - mPlatformList.append(platformQuery.value(Table_Game::COL_PLATFORM).toString()); - - // Sort list - mPlatformList.sort(); - - // Make playlist query - QSqlQuery playlistQuery("SELECT DISTINCT " + Table_Playlist::COL_TITLE + " FROM " + Table_Playlist::NAME, fpDb); - - // Return if error occurs - if(playlistQuery.lastError().isValid()) - return playlistQuery.lastError(); - - // Parse query - while(playlistQuery.next()) - mPlaylistList.append(playlistQuery.value(Db::Table_Playlist::COL_TITLE).toString()); + mPlatformNames.append(platformQuery.value(Table_Game::COL_PLATFORM_NAME).toString()); // Sort list - mPlaylistList.sort(); + mPlatformNames.sort(); // Return invalid SqlError return QSqlError(); @@ -338,7 +364,7 @@ QSqlError Db::populateTags() QHash primaryAliases; // Make tag category query - QSqlQuery categoryQuery("SELECT `" + Table_Tag_Category::COLUMN_LIST.join("`,`") + "` FROM " + Table_Tag_Category::NAME, fpDb); + QSqlQuery categoryQuery(u"SELECT `"_s + Table_Tag_Category::COLUMN_LIST.join(u"`,`"_s) + u"` FROM "_s + Table_Tag_Category::NAME, fpDb); // Return if error occurs if(categoryQuery.lastError().isValid()) @@ -355,7 +381,7 @@ QSqlError Db::populateTags() } // Make tag alias query - QSqlQuery aliasQuery("SELECT `" + Table_Tag_Alias::COLUMN_LIST.join("`,`") + "` FROM " + Table_Tag_Alias::NAME, fpDb); + QSqlQuery aliasQuery(u"SELECT `"_s + Table_Tag_Alias::COLUMN_LIST.join(u"`,`"_s) + u"` FROM "_s + Table_Tag_Alias::NAME, fpDb); // Return if error occurs if(aliasQuery.lastError().isValid()) @@ -366,7 +392,7 @@ QSqlError Db::populateTags() primaryAliases[aliasQuery.value(Table_Tag_Alias::COL_ID).toInt()] = aliasQuery.value(Table_Tag_Alias::COL_NAME).toString(); // Make tag query - QSqlQuery tagQuery("SELECT `" + Table_Tag::COLUMN_LIST.join("`,`") + "` FROM " + Table_Tag::NAME, fpDb); + QSqlQuery tagQuery(u"SELECT `"_s + Table_Tag::COLUMN_LIST.join(u"`,`"_s) + u"` FROM "_s + Table_Tag::NAME, fpDb); // Return if error occurs if(tagQuery.lastError().isValid()) @@ -386,7 +412,7 @@ QSqlError Db::populateTags() return QSqlError(); } -QSqlError Db::queryGamesByPlatform(QList& resultBuffer, QStringList platforms, InclusionOptions inclusionOptions, +DbError Db::queryGamesByPlatform(QList& resultBuffer, const QStringList& platforms, const InclusionOptions& inclusionOptions, std::optional*> idInclusionFilter) { // Ensure return buffer is reset @@ -394,26 +420,26 @@ QSqlError Db::queryGamesByPlatform(QList& resultBuffer, QStringList // Empty shortcuts if(platforms.isEmpty() || (idInclusionFilter.has_value() && idInclusionFilter.value()->isEmpty())) - return QSqlError(); + return DbError(); // Get database QSqlDatabase fpDb; QSqlError dbError = getThreadConnection(fpDb); if(dbError.isValid()) - return dbError; + return DbError::fromSqlError(dbError); // Determine game exclusion filter from tag exclusions if applicable QSet idExclusionFilter; if(!inclusionOptions.excludedTagIds.isEmpty()) { // Make game tag sets query - QString tagIdCSV = Qx::String::join(inclusionOptions.excludedTagIds, [](int tagId){return QString::number(tagId);}, "','"); - QSqlQuery tagQuery("SELECT `" + Table_Game_Tags_Tag::COL_GAME_ID + "` FROM " + Table_Game_Tags_Tag::NAME + - " WHERE " + Table_Game_Tags_Tag::COL_TAG_ID + " IN('" + tagIdCSV + "')", fpDb); + QString tagIdCSV = Qx::String::join(inclusionOptions.excludedTagIds, [](int tagId){return QString::number(tagId);}, u"','"_s); + QSqlQuery tagQuery(u"SELECT `"_s + Table_Game_Tags_Tag::COL_GAME_ID + u"` FROM "_s + Table_Game_Tags_Tag::NAME + + u" WHERE "_s + Table_Game_Tags_Tag::COL_TAG_ID + u" IN('"_s + tagIdCSV + u"')"_s, fpDb); QSqlError tagQueryError = tagQuery.lastError(); if(tagQueryError.isValid()) - return tagQueryError; + return DbError::fromSqlError(tagQueryError); // Populate exclusion filter while(tagQuery.next()) @@ -423,27 +449,27 @@ QSqlError Db::queryGamesByPlatform(QList& resultBuffer, QStringList for(const QString& platform : platforms) { // Create platform query string - QString placeholder = ":platform"; - QString baseQueryCommand = "SELECT %1 FROM " + Table_Game::NAME + " WHERE " + - Table_Game::COL_PLATFORM + " = " + placeholder + " AND "; + QString placeholder = u":platform"_s; + QString baseQueryCommand = u"SELECT %1 FROM "_s + Table_Game::NAME + u" WHERE "_s + + Table_Game::COL_PLATFORM_NAME + u" = "_s + placeholder + u" AND "_s; // Handle filtering QString filteredQueryCommand = baseQueryCommand.append(inclusionOptions.includeAnimations ? GAME_AND_ANIM_FILTER : GAME_ONLY_FILTER); if(!idExclusionFilter.isEmpty()) { - QString gameIdCSV = Qx::String::join(idExclusionFilter, [](QUuid id){return id.toString(QUuid::WithoutBraces);}, "','"); - filteredQueryCommand += " AND " + Table_Game::COL_ID + " NOT IN('" + gameIdCSV + "')"; + QString gameIdCSV = Qx::String::join(idExclusionFilter, [](QUuid id){return id.toString(QUuid::WithoutBraces);}, u"','"_s); + filteredQueryCommand += u" AND "_s + Table_Game::COL_ID + u" NOT IN('"_s + gameIdCSV + u"')"_s; } if(idInclusionFilter.has_value()) { - QString gameIdCSV = Qx::String::join(*idInclusionFilter.value(), [](QUuid id){return id.toString(QUuid::WithoutBraces);}, "','"); - filteredQueryCommand += " AND " + Table_Game::COL_ID + " IN('" + gameIdCSV + "')"; + QString gameIdCSV = Qx::String::join(*idInclusionFilter.value(), [](QUuid id){return id.toString(QUuid::WithoutBraces);}, u"','"_s); + filteredQueryCommand += u" AND "_s + Table_Game::COL_ID + u" IN('"_s + gameIdCSV + u"')"_s; } // Create final command strings - QString mainQueryCommand = filteredQueryCommand.arg("`" + Table_Game::COLUMN_LIST.join("`,`") + "`"); + QString mainQueryCommand = filteredQueryCommand.arg(u"`"_s + Table_Game::COLUMN_LIST.join(u"`,`"_s) + u"`"_s); QString sizeQueryCommand = filteredQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); // Create main query and bind current platform @@ -454,7 +480,7 @@ QSqlError Db::queryGamesByPlatform(QList& resultBuffer, QStringList // Execute query and return if error occurs if(!initialQuery.exec()) - return initialQuery.lastError(); + return DbError::fromSqlError(initialQuery.lastError()); // Create size query and bind current platform QSqlQuery sizeQuery(fpDb); @@ -463,7 +489,7 @@ QSqlError Db::queryGamesByPlatform(QList& resultBuffer, QStringList // Execute query and return if error occurs if(!sizeQuery.exec()) - return sizeQuery.lastError(); + return DbError::fromSqlError(sizeQuery.lastError()); // Get query size sizeQuery.next(); @@ -474,11 +500,11 @@ QSqlError Db::queryGamesByPlatform(QList& resultBuffer, QStringList resultBuffer.append({platform, initialQuery, querySize}); } - // Return invalid SqlError - return QSqlError(); + // Return invalid error + return DbError(); } -QSqlError Db::queryAllAddApps(QueryBuffer& resultBuffer) +DbError Db::queryAllAddApps(QueryBuffer& resultBuffer) { // Ensure return buffer is effectively null resultBuffer = QueryBuffer(); @@ -487,216 +513,57 @@ QSqlError Db::queryAllAddApps(QueryBuffer& resultBuffer) QSqlDatabase fpDb; QSqlError dbError = getThreadConnection(fpDb); if(dbError.isValid()) - return dbError; + return DbError::fromSqlError(dbError); // Make query - QString baseQueryCommand = "SELECT %1 FROM " + Table_Add_App::NAME; - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Add_App::COLUMN_LIST.join("`,`") + "`"); + QString baseQueryCommand = u"SELECT %1 FROM "_s + Table_Add_App::NAME; + QString mainQueryCommand = baseQueryCommand.arg(u"`"_s + Table_Add_App::COLUMN_LIST.join(u"`,`"_s) + u"`"_s); QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); resultBuffer.source = Table_Add_App::NAME; - return makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand); -} - -QSqlError Db::queryPlaylistsByName(QueryBuffer& resultBuffer, QStringList playlists, InclusionOptions inclusionOptions) -{ - // Return blank result if no playlists are selected - if(playlists.isEmpty()) - { - resultBuffer.source = QString(); - resultBuffer.result = QSqlQuery(); - resultBuffer.size = 0; - - return QSqlError(); - } - else - { - // Ensure return buffer is effectively null - resultBuffer = QueryBuffer(); - - // Get database - QSqlDatabase fpDb; - QSqlError dbError = getThreadConnection(fpDb); - if(dbError.isValid()) - return dbError; - - // Create selected playlists query string - QString placeHolders = QString("?,").repeated(playlists.size()); - placeHolders.chop(1); // Remove trailing ? - QString baseQueryCommand = "SELECT %1 FROM " + Table_Playlist::NAME + " WHERE " + - Table_Playlist::COL_TITLE + " IN (" + placeHolders + ") AND " + - (inclusionOptions.includeAnimations ? GAME_AND_ANIM_FILTER : GAME_ONLY_FILTER); - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Playlist::COLUMN_LIST.join("`,`") + "`"); - QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); - - // Create main query and bind selected playlists - QSqlQuery mainQuery(fpDb); - mainQuery.setForwardOnly(true); - mainQuery.prepare(mainQueryCommand); - for(const QString& playlist : playlists) - mainQuery.addBindValue(playlist); - - // Execute query and return if error occurs - if(!mainQuery.exec()) - return mainQuery.lastError(); - - // Create size query and bind selected playlists - QSqlQuery sizeQuery(fpDb); - sizeQuery.setForwardOnly(true); - sizeQuery.prepare(sizeQueryCommand); - for(const QString& playlist : playlists) - sizeQuery.addBindValue(playlist); - - // Execute query and return if error occurs - if(!sizeQuery.exec()) - return sizeQuery.lastError(); - - // Get query size - sizeQuery.next(); - int querySize = sizeQuery.value(0).toInt(); - - // Set buffer instance to result - resultBuffer.source = Table_Playlist::NAME; - resultBuffer.result = mainQuery; - resultBuffer.size = querySize; - - // Return invalid SqlError - return QSqlError(); - } -} - -QSqlError Db::queryPlaylistGamesByPlaylist(QList& resultBuffer, const QList& playlistIds) -{ - // Ensure return buffer is empty - resultBuffer.clear(); - - // Get database - QSqlDatabase fpDb; - QSqlError dbError = getThreadConnection(fpDb); - if(dbError.isValid()) - return dbError; - - for(QUuid playlistId : playlistIds) // Naturally returns empty list if no playlists are selected - { - // Query all games for the current playlist - QString baseQueryCommand = "SELECT %1 FROM " + Table_Playlist_Game::NAME + " WHERE " + - Table_Playlist_Game::COL_PLAYLIST_ID + " = '" + playlistId.toString(QUuid::WithoutBraces) + "'"; - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Playlist_Game::COLUMN_LIST.join("`,`") + "`"); - QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); - - // Make query - QSqlError queryError; - QueryBuffer queryResult; - queryResult.source = playlistId.toString(); - - if((queryError = makeNonBindQuery(queryResult, &fpDb, mainQueryCommand, sizeQueryCommand)).isValid()) - return queryError; - - // Add result to buffer if there were any hits - if(queryResult.size > 0) - resultBuffer.append(queryResult); - } - - // Return invalid SqlError - return QSqlError(); -} - -QSqlError Db::queryPlaylistGameIds(QueryBuffer& resultBuffer, const QList& playlistIds) -{ - // Ensure return buffer is empty - resultBuffer = QueryBuffer(); - - // Get database - QSqlDatabase fpDb; - QSqlError dbError = getThreadConnection(fpDb); - if(dbError.isValid()) - return dbError; - - // Create playlist ID query string - QString idCSV = Qx::String::join(playlistIds, [](QUuid id){return id.toString(QUuid::WithoutBraces);}, "','"); - - // Query all game IDs that fall under given the playlists - QString baseQueryCommand = "SELECT %1 FROM " + Table_Playlist_Game::NAME + " WHERE " + - Table_Playlist_Game::COL_PLAYLIST_ID + " IN('" + idCSV + "')"; - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Playlist_Game::COL_GAME_ID + "`"); - QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); - - // Make query - QSqlError queryError; - resultBuffer.source = Table_Playlist_Game::NAME; - - if((queryError = makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand)).isValid()) - return queryError; - - // Return invalid SqlError - return QSqlError(); - + return DbError::fromSqlError(makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand)); } -QSqlError Db::queryAllEntryTags(QueryBuffer& resultBuffer) -{ - // Ensure return buffer is empty - resultBuffer = QueryBuffer(); - - // Get database - QSqlDatabase fpDb; - QSqlError dbError = getThreadConnection(fpDb); - if(dbError.isValid()) - return dbError; - - // Query all tags tied to game IDs - QString baseQueryCommand = "SELECT %1 FROM " + Table_Game_Tags_Tag::NAME; - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Game_Tags_Tag::COL_GAME_ID + "`"); - QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); - - // Make query - QSqlError queryError; - resultBuffer.source = Table_Playlist_Game::NAME; - - // Return invalid SqlError - return makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand); -} - -QSqlError Db::queryEntrys(QueryBuffer& resultBuffer, EntryFilter filter) +DbError Db::queryEntrys(QueryBuffer& resultBuffer, const EntryFilter& filter) { // Ensure return buffer is effectively null resultBuffer = QueryBuffer(); // Query Constants - const QString where = " WHERE "; - const QString nd = " AND "; - const QString likeTempl = " LIKE '%%1%' ESCAPE '\\'"; + const QString where = u" WHERE "_s; + const QString nd = u" AND "_s; + const QString likeTempl = u" LIKE '%%1%' ESCAPE '\\'"_s; // Get database QSqlDatabase fpDb; QSqlError dbError = getThreadConnection(fpDb); if(dbError.isValid()) - return dbError; + return DbError::fromSqlError(dbError); // Check for entry as a game first if(filter.type != EntryType::AddApp) { // Assemble Base Query Command - QString baseQueryCommand = "SELECT %1 FROM " + Table_Game::NAME; + QString baseQueryCommand = u"SELECT %1 FROM "_s + Table_Game::NAME; if(!filter.parent.isNull() || !filter.id.isNull() || !filter.name.isNull()) { baseQueryCommand += where; if(!filter.id.isNull()) - baseQueryCommand += Table_Game::COL_ID + " == '" + filter.id.toString(QUuid::WithoutBraces) + "'" + nd; + baseQueryCommand += Table_Game::COL_ID + u" == '"_s + filter.id.toString(QUuid::WithoutBraces) + u"'"_s + nd; if(!filter.parent.isNull()) - baseQueryCommand += Table_Game::COL_PARENT_ID + " == '" + filter.parent.toString(QUuid::WithoutBraces) + "'" + nd; + baseQueryCommand += Table_Game::COL_PARENT_ID + u" == '"_s + filter.parent.toString(QUuid::WithoutBraces) + u"'"_s + nd; if(!filter.name.isNull()) { if(filter.exactName) - baseQueryCommand += Table_Game::COL_TITLE + " == '" + filter.name + "'" + nd; + baseQueryCommand += Table_Game::COL_TITLE + u" == '"_s + filter.name + u"'"_s + nd; else { // Escape name to account for SQL LITE % QString escapedName = filter.name; - escapedName.replace(R"(\)", R"(\\)"); // Have to escape the escape char - escapedName.replace(R"(%)",R"(\%)"); + escapedName.replace(uR"(\)"_s, uR"(\\)"_s); // Have to escape the escape char + escapedName.replace(uR"(%)"_s, uR"(\%)"_s); baseQueryCommand += Table_Game::COL_TITLE + likeTempl.arg(escapedName) + nd; } @@ -707,7 +574,7 @@ QSqlError Db::queryEntrys(QueryBuffer& resultBuffer, EntryFilter filter) } // Assemble final query commands - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Game::COLUMN_LIST.join("`,`") + "`"); + QString mainQueryCommand = baseQueryCommand.arg(u"`"_s + Table_Game::COLUMN_LIST.join(u"`,`"_s) + u"`"_s); QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); // Make query @@ -715,45 +582,45 @@ QSqlError Db::queryEntrys(QueryBuffer& resultBuffer, EntryFilter filter) resultBuffer.source = Table_Game::NAME; if((queryError = makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand)).isValid()) - return queryError; + return DbError::fromSqlError(queryError); // Return result if one or more results were found (receiver handles situation in latter case) if(resultBuffer.size >= 1) - return QSqlError(); + return DbError(); } // Check for entry as an additional app second if(filter.type != EntryType::Primary) { // Assemble Base Query Command - QString baseQueryCommand = "SELECT %1 FROM " + Table_Add_App::NAME; + QString baseQueryCommand = u"SELECT %1 FROM "_s + Table_Add_App::NAME; if(!filter.parent.isNull() || !filter.id.isNull() || !filter.name.isNull()) { baseQueryCommand += where; if(!filter.id.isNull()) - baseQueryCommand += Table_Add_App::COL_ID + " == '" + filter.id.toString(QUuid::WithoutBraces) + "'" + nd; + baseQueryCommand += Table_Add_App::COL_ID + u" == '"_s + filter.id.toString(QUuid::WithoutBraces) + u"'"_s + nd; if(!filter.parent.isNull()) - baseQueryCommand += Table_Add_App::COL_PARENT_ID + " == '" + filter.parent.toString(QUuid::WithoutBraces) + "'" + nd; + baseQueryCommand += Table_Add_App::COL_PARENT_ID + u" == '"_s + filter.parent.toString(QUuid::WithoutBraces) + u"'"_s + nd; if(!filter.name.isNull()) { if(filter.exactName) - baseQueryCommand += Table_Add_App::COL_NAME + " == '" + filter.name + "'" + nd; + baseQueryCommand += Table_Add_App::COL_NAME + u" == '"_s + filter.name + u"'"_s + nd; else { // Escape name to account for SQL LITE % QString escapedName = filter.name; - escapedName.replace(R"(\)", R"(\\)"); // Have to escape the escape char - escapedName.replace(R"(%)",R"(\%)"); + escapedName.replace(uR"(\)"_s, uR"(\\)"_s); // Have to escape the escape char + escapedName.replace(uR"(%)"_s, uR"(\%)"_s); baseQueryCommand += Table_Add_App::COL_NAME + likeTempl.arg(escapedName) + nd; } } if(filter.playableOnly) { - baseQueryCommand += Table_Add_App::COL_APP_PATH + " NOT IN ('" + Table_Add_App::ENTRY_EXTRAS + "','" + - Table_Add_App::ENTRY_MESSAGE + "') AND " + Table_Add_App::COL_AUTORUN + " != 1" + + baseQueryCommand += Table_Add_App::COL_APP_PATH + u" NOT IN ('"_s + Table_Add_App::ENTRY_EXTRAS + u"','"_s + + Table_Add_App::ENTRY_MESSAGE + u"') AND "_s + Table_Add_App::COL_AUTORUN + u" != 1"_s + nd; } @@ -762,7 +629,7 @@ QSqlError Db::queryEntrys(QueryBuffer& resultBuffer, EntryFilter filter) } // Assemble final query commands - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Add_App::COLUMN_LIST.join("`,`") + "`"); + QString mainQueryCommand = baseQueryCommand.arg(u"`"_s + Table_Add_App::COLUMN_LIST.join(u"`,`"_s) + u"`"_s); QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); // Make query @@ -770,43 +637,22 @@ QSqlError Db::queryEntrys(QueryBuffer& resultBuffer, EntryFilter filter) resultBuffer.source = Table_Add_App::NAME; if((queryError = makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand)).isValid()) - return queryError; + return DbError::fromSqlError(queryError); // Return result if one or more results were found (receiver handles situation in latter case) if(resultBuffer.size >= 1) - return QSqlError(); + return DbError(); } // No result found, return - return QSqlError(); + return DbError(); } -QSqlError Db::queryEntryDataById(QueryBuffer& resultBuffer, QUuid appId) +DbError Db::queryEntryDataById(QueryBuffer& resultBuffer, const QUuid& appId) { - // Ensure return buffer is effectively null - resultBuffer = QueryBuffer(); - - // Get database - QSqlDatabase fpDb; - QSqlError dbError = getThreadConnection(fpDb); - if(dbError.isValid()) - return dbError; - - // Setup ID query - QString baseQueryCommand = "SELECT %1 FROM " + Table_Game_Data::NAME + " WHERE " + - Table_Game_Data::COL_GAME_ID + " == '" + appId.toString(QUuid::WithoutBraces) + "'"; - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Game_Data::COLUMN_LIST.join("`,`") + "`"); - QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); - - // Make query - QSqlError queryError; - resultBuffer.source = Table_Game_Data::NAME; + // A few entries have more than one data pack. The results are sorted + // by most to least recently added - return makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand); -} - -QSqlError Db::queryDataPackSource(QueryBuffer& resultBuffer) -{ // Ensure return buffer is effectively null resultBuffer = QueryBuffer(); @@ -814,45 +660,23 @@ QSqlError Db::queryDataPackSource(QueryBuffer& resultBuffer) QSqlDatabase fpDb; QSqlError dbError = getThreadConnection(fpDb); if(dbError.isValid()) - return dbError; + return DbError::fromSqlError(dbError); // Setup ID query - QString baseQueryCommand = "SELECT %1 FROM " + Table_Source::NAME; - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Source::COLUMN_LIST.join("`,`") + "`"); + QString baseQueryCommand = u"SELECT %1 FROM "_s + Table_Game_Data::NAME + u" WHERE "_s + + Table_Game_Data::COL_GAME_ID + u" == '"_s + appId.toString(QUuid::WithoutBraces) + u"' "_s + + u"ORDER BY "_s + Table_Game_Data::COL_DATE_ADDED + u" DESC"_s; + QString mainQueryCommand = baseQueryCommand.arg(u"`"_s + Table_Game_Data::COLUMN_LIST.join(u"`,`"_s) + u"`"_s); QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); // Make query QSqlError queryError; - resultBuffer.source = Table_Source::NAME; - - return makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand); -} - -QSqlError Db::queryEntrySourceData(QueryBuffer& resultBuffer, QString appSha256Hex) -{ - // Ensure return buffer is effectively null - resultBuffer = QueryBuffer(); - - // Get database - QSqlDatabase fpDb; - QSqlError dbError = getThreadConnection(fpDb); - if(dbError.isValid()) - return dbError; - - // Setup ID query - QString baseQueryCommand = "SELECT %1 FROM " + Table_Source_Data::NAME + " WHERE " + - Table_Source_Data::COL_SHA256 + " == '" + appSha256Hex + "'"; - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Source_Data::COLUMN_LIST.join("`,`") + "`"); - QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); - - // Make query - QSqlError queryError; - resultBuffer.source = Table_Source_Data::NAME; + resultBuffer.source = Table_Game_Data::NAME; - return makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand); + return DbError::fromSqlError(makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand)); } -QSqlError Db::queryAllGameIds(QueryBuffer& resultBuffer, LibraryFilter includeFilter) +DbError Db::queryAllGameIds(QueryBuffer& resultBuffer, const LibraryFilter& includeFilter) { // Ensure return buffer is effectively null resultBuffer = QueryBuffer(); @@ -861,27 +685,26 @@ QSqlError Db::queryAllGameIds(QueryBuffer& resultBuffer, LibraryFilter includeFi QSqlDatabase fpDb; QSqlError dbError = getThreadConnection(fpDb); if(dbError.isValid()) - return dbError; + return DbError::fromSqlError(dbError); // Make query - QString baseQueryCommand = "SELECT %2 FROM " + Table_Game::NAME + " WHERE " + - Table_Game::COL_STATUS + " != '" + Table_Game::ENTRY_NOT_WORK + "'%1"; - baseQueryCommand = baseQueryCommand.arg(includeFilter == LibraryFilter::Game ? " AND " + GAME_ONLY_FILTER : (includeFilter == LibraryFilter::Anim ? " AND " + ANIM_ONLY_FILTER : "")); - QString mainQueryCommand = baseQueryCommand.arg("`" + Table_Game::COL_ID + "`"); + QString baseQueryCommand = u"SELECT %2 FROM "_s + Table_Game::NAME + u" WHERE "_s + + Table_Game::COL_STATUS + u" != '"_s + Table_Game::ENTRY_NOT_WORK + u"'%1"_s; + baseQueryCommand = baseQueryCommand.arg(includeFilter == LibraryFilter::Game ? u" AND "_s + GAME_ONLY_FILTER : (includeFilter == LibraryFilter::Anim ? u" AND "_s + ANIM_ONLY_FILTER : u""_s)); + QString mainQueryCommand = baseQueryCommand.arg(u"`"_s + Table_Game::COL_ID + u"`"_s); QString sizeQueryCommand = baseQueryCommand.arg(GENERAL_QUERY_SIZE_COMMAND); resultBuffer.source = Table_Game::NAME; - return makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand); + return DbError::fromSqlError(makeNonBindQuery(resultBuffer, &fpDb, mainQueryCommand, sizeQueryCommand)); } -QStringList Db::platformList() const { return mPlatformList; } -QStringList Db::playlistList() const { return mPlaylistList; } +QStringList Db::platformNames() const { return mPlatformNames; } //TODO: Probably should use RAII for this. QMap Db::tags() const { return mTagMap; } -QSqlError Db::entryUsesDataPack(bool& resultBuffer, QUuid gameId) +DbError Db::entryUsesDataPack(bool& resultBuffer, const QUuid& gameId) { /* NOTE: The launcher performs this check and other data pack tasks by checking if the `activeDataId` column - * of the `game` table has a value, and if it does that matching that to the `id` column in the `game_data` + * of the `game` table has a value, and if it does, then matching that to the `id` column in the `game_data` * table to get the game's data pack info. This requires slightly less processing, but the way it's done here * is ultimately fine and technically handles typos/errors in the database slightly better since it's a more * direct check. Ultimately this should be switched over to the official method though. @@ -901,11 +724,11 @@ QSqlError Db::entryUsesDataPack(bool& resultBuffer, QUuid gameId) QSqlDatabase fpDb; QSqlError dbError = getThreadConnection(fpDb); if(dbError.isValid()) - return dbError; + return DbError::fromSqlError(dbError); // Make query - QString packCheckQueryCommand = "SELECT " + GENERAL_QUERY_SIZE_COMMAND + " FROM " + Table_Game_Data::NAME + " WHERE " + - Table_Game_Data::COL_GAME_ID + " == '" + gameId.toString(QUuid::WithoutBraces) + "'"; + QString packCheckQueryCommand = u"SELECT "_s + GENERAL_QUERY_SIZE_COMMAND + u" FROM "_s + Table_Game_Data::NAME + u" WHERE "_s + + Table_Game_Data::COL_GAME_ID + u" == '"_s + gameId.toString(QUuid::WithoutBraces) + u"'"_s; QSqlQuery packCheckQuery(fpDb); packCheckQuery.setForwardOnly(true); @@ -913,14 +736,151 @@ QSqlError Db::entryUsesDataPack(bool& resultBuffer, QUuid gameId) // Execute query and return if error occurs if(!packCheckQuery.exec()) - return packCheckQuery.lastError(); + return DbError::fromSqlError(packCheckQuery.lastError()); // Set buffer based on result packCheckQuery.next(); resultBuffer = packCheckQuery.value(0).toInt() > 0; - // Return invalid SqlError - return QSqlError(); + // Return invalid error + return DbError(); +} + +DbError Db::getEntry(std::variant& entry, const QUuid& entryId) +{ + // Find title + Db::EntryFilter mainFilter{.type = Fp::Db::EntryType::PrimaryThenAddApp, .id = entryId}; + + Fp::Db::QueryBuffer searchResult; + DbError searchError = queryEntrys(searchResult, mainFilter); + if(searchError.isValid()) + return searchError; + + // Check if ID was found and that only one instance was found + if(searchResult.size == 0) + return DbError(DbError::IncompleteSearch, ERR_ID_NOT_FOUND); + else if(searchResult.size > 1) + return DbError(DbError::IdCollision, ERR_ID_DUPLICATE_ENTRY); + + // Advance result to only record + searchResult.result.next(); + + // Fill variant + if(searchResult.source == Db::Table_Add_App::NAME) + { + AddApp::Builder fpAab; + fpAab.wId(searchResult.result.value(Fp::Db::Table_Add_App::COL_ID).toString()); + fpAab.wAppPath(searchResult.result.value(Fp::Db::Table_Add_App::COL_APP_PATH).toString()); + fpAab.wAutorunBefore(searchResult.result.value(Fp::Db::Table_Add_App::COL_AUTORUN).toString()); + fpAab.wLaunchCommand(searchResult.result.value(Fp::Db::Table_Add_App::COL_LAUNCH_COMMAND).toString()); + fpAab.wName(searchResult.result.value(Fp::Db::Table_Add_App::COL_NAME).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpAab.wWaitExit(searchResult.result.value(Fp::Db::Table_Add_App::COL_WAIT_EXIT).toString()); + fpAab.wParentId(searchResult.result.value(Fp::Db::Table_Add_App::COL_PARENT_ID).toString()); + + entry = fpAab.build(); + } + else if(searchResult.source == Db::Table_Game::NAME) + { + Game::Builder fpGb; + fpGb.wId(searchResult.result.value(Fp::Db::Table_Game::COL_ID).toString()); + fpGb.wTitle(searchResult.result.value(Fp::Db::Table_Game::COL_TITLE).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpGb.wSeries(searchResult.result.value(Fp::Db::Table_Game::COL_SERIES).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpGb.wDeveloper(searchResult.result.value(Fp::Db::Table_Game::COL_DEVELOPER).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpGb.wPublisher(searchResult.result.value(Fp::Db::Table_Game::COL_PUBLISHER).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpGb.wDateAdded(searchResult.result.value(Fp::Db::Table_Game::COL_DATE_ADDED).toString()); + fpGb.wDateModified(searchResult.result.value(Fp::Db::Table_Game::COL_DATE_MODIFIED).toString()); + fpGb.wBroken(searchResult.result.value(Fp::Db::Table_Game::COL_BROKEN).toString()); + fpGb.wPlayMode(searchResult.result.value(Fp::Db::Table_Game::COL_PLAY_MODE).toString()); + fpGb.wStatus(searchResult.result.value(Fp::Db::Table_Game::COL_STATUS).toString()); + fpGb.wNotes(searchResult.result.value(Fp::Db::Table_Game::COL_NOTES).toString()); + fpGb.wSource(searchResult.result.value(Fp::Db::Table_Game::COL_SOURCE).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpGb.wAppPath(searchResult.result.value(Fp::Db::Table_Game::COL_APP_PATH).toString()); + fpGb.wLaunchCommand(searchResult.result.value(Fp::Db::Table_Game::COL_LAUNCH_COMMAND).toString()); + fpGb.wReleaseDate(searchResult.result.value(Fp::Db::Table_Game::COL_RELEASE_DATE).toString()); + fpGb.wVersion(searchResult.result.value(Fp::Db::Table_Game::COL_VERSION).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpGb.wOriginalDescription(searchResult.result.value(Fp::Db::Table_Game::COL_ORIGINAL_DESC).toString()); + fpGb.wLanguage(searchResult.result.value(Fp::Db::Table_Game::COL_LANGUAGE).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpGb.wOrderTitle(searchResult.result.value(Fp::Db::Table_Game::COL_ORDER_TITLE).toString().remove(Qx::RegularExpression::LINE_BREAKS)); + fpGb.wLibrary(searchResult.result.value(Fp::Db::Table_Game::COL_LIBRARY).toString()); + fpGb.wPlatformName(searchResult.result.value(Fp::Db::Table_Game::COL_PLATFORM_NAME).toString()); + + entry = fpGb.build(); + } + else + qFatal("Entry search result source must be 'game' or 'additional_app'"); + + return DbError(); +} + +DbError Db::getGameData(GameData& data, const QUuid& gameId) +{ + // Clear buffer + data = GameData(); + + // Get entry data + DbError searchError; + Fp::Db::QueryBuffer searchResult; + + if((searchError = queryEntryDataById(searchResult, gameId)).isValid()) + return searchError; + + // Check if ID was found and if so that only one instance was found + if(searchResult.size == 0) + return DbError(); // Game doesn't have data pack + else if(searchResult.size > 1) + qWarning("Entry with more than one data pack, using most recent."); + + // Advance result to first record + searchResult.result.next(); + + // Fill buffer + GameData::Builder fpGdb; + fpGdb.wId(searchResult.result.value(Fp::Db::Table_Game_Data::COL_ID).toString()); + fpGdb.wGameId(searchResult.result.value(Fp::Db::Table_Game_Data::COL_GAME_ID).toString()); + fpGdb.wTitle(searchResult.result.value(Fp::Db::Table_Game_Data::COL_TITLE).toString()); + fpGdb.wDateAdded(searchResult.result.value(Fp::Db::Table_Game_Data::COL_DATE_ADDED).toString()); + fpGdb.wSha256(searchResult.result.value(Fp::Db::Table_Game_Data::COL_SHA256).toString()); + fpGdb.wCrc32(searchResult.result.value(Fp::Db::Table_Game_Data::COL_CRC32).toString()); + fpGdb.wPresentOnDisk(searchResult.result.value(Fp::Db::Table_Game_Data::COL_PRES_ON_DISK).toString()); + fpGdb.wPath(searchResult.result.value(Fp::Db::Table_Game_Data::COL_PATH).toString()); + fpGdb.wSize(searchResult.result.value(Fp::Db::Table_Game_Data::COL_SIZE).toString()); + fpGdb.wParameters(searchResult.result.value(Fp::Db::Table_Game_Data::COL_PARAM).toString()); + fpGdb.wAppPath(searchResult.result.value(Fp::Db::Table_Game_Data::COL_APP_PATH).toString()); + fpGdb.wLaunchCommand(searchResult.result.value(Fp::Db::Table_Game_Data::COL_LAUNCH_COMMAND).toString()); + + data = fpGdb.build(); + + return DbError(); +} + +DbError Db::updateGameDataOnDiskState(int packId, bool onDisk) +{ + // Get database + QSqlDatabase fpDb; + QSqlError dbError = getThreadConnection(fpDb); + if(dbError.isValid()) + return DbError::fromSqlError(dbError); + + // Make query + QString dataUpdateCommand = u"UPDATE "_s + Table_Game_Data::NAME + u" SET "_s + Table_Game_Data::COL_PRES_ON_DISK + u" = "_s + QString::number(onDisk) + + u" WHERE "_s + Table_Game_Data::COL_ID + u" = "_s + QString::number(packId); + + QSqlQuery packUpdateQuery(fpDb); + packUpdateQuery.setForwardOnly(true); + packUpdateQuery.prepare(dataUpdateCommand); + + // Execute query and return if error occurs + if(!packUpdateQuery.exec()) + return DbError::fromSqlError(packUpdateQuery.lastError()); + + // Check that expected count was affected + int rows = packUpdateQuery.numRowsAffected(); + if(rows < 1) + return DbError(DbError::UpdateIneffective, Table_Game_Data::NAME + u" SET "_s + Table_Game_Data::COL_PRES_ON_DISK); + else if(rows > 1) + return DbError(DbError::UpdateTooMany, Table_Game_Data::NAME + u" SET "_s + Table_Game_Data::COL_PRES_ON_DISK); + + return DbError(); } //-Slots ------------------------------------------------------------------------------------------------------ diff --git a/lib/src/fp-install.cpp b/lib/src/fp-install.cpp index 2452e6d..e6b4381 100644 --- a/lib/src/fp-install.cpp +++ b/lib/src/fp-install.cpp @@ -2,8 +2,8 @@ #include "fp/fp-install.h" // Qx Includes -#include #include +#include namespace Fp { @@ -13,19 +13,19 @@ namespace Fp //-Constructor------------------------------------------------------------------------------------------------ //Public: -Install::Install(QString installPath) : +Install::Install(QString installPath, bool preloadPlaylists) : mValid(false) // Install is invalid until proven otherwise { QScopeGuard validityGuard([this](){ nullify(); }); // Automatically nullify on fail // Initialize static files and directories mRootDirectory = QDir(installPath); - mLauncherFile = std::make_unique(installPath + "/" + LAUNCHER_PATH); - mDatabaseFile = std::make_unique(installPath + "/" + DATABASE_PATH); - mConfigJsonFile = std::make_shared(installPath + "/" + CONFIG_JSON_PATH); - mPreferencesJsonFile = std::make_shared(installPath + "/" + PREFERENCES_JSON_PATH); - mVersionFile = std::make_unique(installPath + "/" + VER_TXT_PATH); - mExtrasDirectory = QDir(installPath + "/" + EXTRAS_PATH); + mLauncherFile = std::make_unique(installPath + u"/"_s + LAUNCHER_PATH); + mDatabaseFile = std::make_unique(installPath + u"/"_s + DATABASE_PATH); + mConfigJsonFile = std::make_shared(installPath + u"/"_s + CONFIG_JSON_PATH); + mPreferencesJsonFile = std::make_shared(installPath + u"/"_s + PREFERENCES_JSON_PATH); + mVersionFile = std::make_unique(installPath + u"/"_s + VER_TXT_PATH); + mExtrasDirectory = QDir(installPath + u"/"_s + EXTRAS_PATH); // Create macro resolver mMacroResolver = new MacroResolver(mRootDirectory.absolutePath(), {}); @@ -46,47 +46,35 @@ Install::Install(QString installPath) : QFileInfo fileInfo(*file); if(!fileInfo.exists() || !fileInfo.isFile()) { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_INVALID, FILE_DNE.arg(fileInfo.filePath())); + mError = Qx::GenericError(Qx::Critical, 1, ERR_FILE_MISSING, fileInfo.filePath()); return; } } // Get settings - Qx::GenericError readReport; - - Json::ConfigReader configReader(&mConfig, mConfigJsonFile); - if((readReport = configReader.readInto()).isValid()) - { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_INVALID, readReport.primaryInfo() + " [" + readReport.secondaryInfo() + "]"); + ConfigReader configReader(&mConfig, mConfigJsonFile); + if((mError = configReader.readInto()).isValid()) return; - } - Json::PreferencesReader prefReader(&mPreferences, mPreferencesJsonFile); - if((readReport = prefReader.readInto()).isValid()) - { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_INVALID, readReport.primaryInfo() + " [" + readReport.secondaryInfo() + "]"); + PreferencesReader prefReader(&mPreferences, mPreferencesJsonFile); + if((mError = prefReader.readInto()).isValid()) return; - } - mServicesJsonFile = std::make_shared(installPath + "/" + mPreferences.jsonFolderPath + "/" + SERVICES_JSON_NAME); - mExecsJsonFile = std::make_shared(installPath + "/" + mPreferences.jsonFolderPath + "/" + EXECS_JSON_NAME); - mLogosDirectory = QDir(installPath + "/" + mPreferences.imageFolderPath + '/' + LOGOS_FOLDER_NAME); - mScreenshotsDirectory = QDir(installPath + "/" + mPreferences.imageFolderPath + '/' + SCREENSHOTS_FOLDER_NAME); - Json::ServicesReader servicesReader(&mServices, mServicesJsonFile, mMacroResolver); - if((readReport = servicesReader.readInto()).isValid()) - { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_INVALID, readReport.primaryInfo() + " [" + readReport.secondaryInfo() + "]"); + mServicesJsonFile = std::make_shared(installPath + u"/"_s + mPreferences.jsonFolderPath + u"/"_s + SERVICES_JSON_NAME); + mExecsJsonFile = std::make_shared(installPath + u"/"_s + mPreferences.jsonFolderPath + u"/"_s + EXECS_JSON_NAME); + mLogosDirectory = QDir(installPath + u"/"_s + mPreferences.imageFolderPath + '/' + LOGOS_FOLDER_NAME); + mScreenshotsDirectory = QDir(installPath + u"/"_s + mPreferences.imageFolderPath + '/' + SCREENSHOTS_FOLDER_NAME); + mPlaylistsDirectory = QDir(installPath + u"/"_s + mPreferences.playlistFolderPath); + + ServicesReader servicesReader(&mServices, mServicesJsonFile, mMacroResolver); + if((mError = servicesReader.readInto()).isValid()) return; - } if(mExecsJsonFile->exists()) // Optional { - Json::ExecsReader execsReader(&mExecs, mExecsJsonFile); - if((readReport = execsReader.readInto()).isValid()) - { - mError = Qx::GenericError(Qx::GenericError::Critical, ERR_INVALID, readReport.primaryInfo() + " [" + readReport.secondaryInfo() + "]"); + ExecsReader execsReader(&mExecs, mExecsJsonFile); + if((mError = execsReader.readInto()).isValid()) return; - } } // Add database @@ -98,6 +86,15 @@ Install::Install(QString installPath) : return; } + // Add playlists manager + mPlaylistManager = new PlaylistManager(mPlaylistsDirectory, {}); + + if(preloadPlaylists) + { + if(mError = mPlaylistManager->populate(); mError.isValid()) + return; + } + // Give the OK mValid = true; validityGuard.dismiss(); @@ -111,18 +108,20 @@ Install::~Install() delete mMacroResolver; if(mDatabase) delete mDatabase; + if(mPlaylistManager) + delete mPlaylistManager; } //-Class Functions------------------------------------------------------------------------------------------------ //Private: -QString Install::standardImageSubPath(ImageType imageType, QUuid gameId) +QString Install::standardImageSubPath(QUuid gameId) { QString gameIdString = gameId.toString(QUuid::WithoutBraces); - return gameIdString.left(2) + '/' + gameIdString.mid(2, 2) + '/' + gameIdString + IMAGE_EXT; + return gameIdString.left(2) + '/' + gameIdString.mid(2, 2) + '/' + gameIdString; } //Public: -Qx::GenericError Install::appInvolvesSecurePlayer(bool& involvesBuffer, QFileInfo appInfo) +Qx::Error Install::appInvolvesSecurePlayer(bool& involvesBuffer, QFileInfo appInfo) { // Reset buffer involvesBuffer = false; @@ -130,9 +129,9 @@ Qx::GenericError Install::appInvolvesSecurePlayer(bool& involvesBuffer, QFileInf if(appInfo.fileName().contains(SECURE_PLAYER_INFO.baseName())) { involvesBuffer = true; - return Qx::GenericError(); + return Qx::Error(); } - else if(appInfo.suffix().compare("bat", Qt::CaseInsensitive) == 0) + else if(appInfo.suffix().compare(u"bat"_s, Qt::CaseInsensitive) == 0) { // Check if bat uses secure player QFile batFile(appInfo.absoluteFilePath()); @@ -140,12 +139,12 @@ Qx::GenericError Install::appInvolvesSecurePlayer(bool& involvesBuffer, QFileInf // Check for read errors if(readReport.isFailure()) - return Qx::GenericError(Qx::GenericError::Critical, readReport.outcome(), readReport.outcomeInfo()); + return Qx::Error(readReport).setSeverity(Qx::Critical); else - return Qx::GenericError(); + return Qx::Error(); } else - return Qx::GenericError(); + return Qx::Error(); } //-Instance Functions------------------------------------------------------------------------------------------------ @@ -157,6 +156,7 @@ void Install::nullify() mLogosDirectory = QDir(); mScreenshotsDirectory = QDir(); mExtrasDirectory = QDir(); + mPlaylistsDirectory = QDir(); mLauncherFile.reset(); mDatabaseFile.reset(); mConfigJsonFile.reset(); @@ -167,6 +167,8 @@ void Install::nullify() qxDelete(mMacroResolver); if(mDatabase) qxDelete(mDatabase); + if(mPlaylistManager) + qxDelete(mPlaylistManager); // Settings mConfig = {}; @@ -176,14 +178,14 @@ void Install::nullify() //Public: bool Install::isValid() const { return mValid; } -Qx::GenericError Install::error() const { return mError; } +Qx::Error Install::error() const { return mError; } Install::Edition Install::edition() const { QString nameVer = nameVersionString(); - return nameVer.contains("ultimate", Qt::CaseInsensitive) ? Edition::Ultimate : - nameVer.contains("infinity", Qt::CaseInsensitive) ? Edition::Infinity : + return nameVer.contains(u"ultimate"_s, Qt::CaseInsensitive) ? Edition::Ultimate : + nameVer.contains(u"infinity"_s, Qt::CaseInsensitive) ? Edition::Infinity : Edition::Core; } @@ -204,7 +206,7 @@ Qx::VersionNumber Install::version() const if(versionMatch.hasMatch()) { - Qx::VersionNumber fpVersion = Qx::VersionNumber::fromString(versionMatch.captured("version")); + Qx::VersionNumber fpVersion = Qx::VersionNumber::fromString(versionMatch.captured(u"version"_s)); if(!fpVersion.isNull()) return fpVersion; } @@ -222,27 +224,37 @@ QString Install::launcherChecksum() const } Db* Install::database() { return mDatabase; } +PlaylistManager* Install::playlistManager() { return mPlaylistManager; } -const Json::Config& Install::config() const { return mConfig; } -const Json::Preferences& Install::preferences() const { return mPreferences; } -const Json::Services& Install::services() const { return mServices; } -const Json::Execs& Install::execs() const { return mExecs; } +const Config& Install::config() const { return mConfig; } +const Preferences& Install::preferences() const { return mPreferences; } +const Services& Install::services() const { return mServices; } +const Execs& Install::execs() const { return mExecs; } QString Install::fullPath() const { return mRootDirectory.absolutePath(); } QDir Install::logosDirectory() const { return mLogosDirectory; } QDir Install::screenshotsDirectory() const { return mScreenshotsDirectory; } QDir Install::extrasDirectory() const { return mExtrasDirectory; } -QString Install::imageLocalPath(ImageType imageType, QUuid gameId) const +QString Install::imageLocalPath(ImageType imageType, const QUuid& gameId) const { const QDir& sourceDir = imageType == ImageType::Logo ? mLogosDirectory : mScreenshotsDirectory; - return sourceDir.absolutePath() + '/' + standardImageSubPath(imageType, gameId); + bool compressed = mPreferences.onDemandImagesCompressed; + QString localSubPath = standardImageSubPath(gameId) + (compressed ? IMAGE_C_EXT : IMAGE_UC_EXT); + + return sourceDir.absolutePath() + '/' + localSubPath; } -QUrl Install::imageRemoteUrl(ImageType imageType, QUuid gameId) const +QUrl Install::imageRemoteUrl(ImageType imageType, const QUuid& gameId) const { const QString typeFolder = (imageType == ImageType::Logo ? LOGOS_FOLDER_NAME : SCREENSHOTS_FOLDER_NAME); - return QUrl(mPreferences.onDemandBaseUrl + typeFolder + '/' + standardImageSubPath(imageType, gameId)); + bool compressed = mPreferences.onDemandImagesCompressed; + QString remoteSubPath = standardImageSubPath(gameId) + IMAGE_UC_EXT; + + if(compressed) + remoteSubPath += IMAGE_C_URL_SUFFIX; + + return QUrl(mPreferences.onDemandBaseUrl + typeFolder + '/' + remoteSubPath); } const MacroResolver* Install::macroResolver() const { return mMacroResolver; } @@ -250,7 +262,7 @@ const MacroResolver* Install::macroResolver() const { return mMacroResolver; } QString Install::resolveAppPathOverrides(const QString& appPath) const { // Check if path has an associated override - for(const Json::AppPathOverride& override : qAsConst(mPreferences.appPathOverrides)) + for(const AppPathOverride& override : qAsConst(mPreferences.appPathOverrides)) { if(override.path == appPath && override.enabled) return override.override; @@ -265,7 +277,7 @@ QString Install::resolveExecSwaps(const QString& appPath, const QString& platfor bool preferNative = mPreferences.nativePlatforms.contains(platform); // Check if path has an associated swap - for(const Json::Exec& swap : qAsConst(mExecs.list)) + for(const Exec& swap : qAsConst(mExecs.list)) { if(swap.win32 == appPath) { diff --git a/lib/src/fp-items.cpp b/lib/src/fp-items.cpp index 3a822b1..eea4bbd 100644 --- a/lib/src/fp-items.cpp +++ b/lib/src/fp-items.cpp @@ -24,7 +24,6 @@ QString Game::developer() const { return mDeveloper; } QString Game::publisher() const { return mPublisher; } QDateTime Game::dateAdded() const { return mDateAdded; } QDateTime Game::dateModified() const { return mDateModified; } -QString Game::platform() const { return mPlatform; } QString Game::playMode() const { return mPlayMode; } bool Game::isBroken() const { return mBroken; } QString Game::status() const { return mStatus; } @@ -38,6 +37,7 @@ QString Game::originalDescription() const { return mOriginalDescription; } QString Game::language() const { return mLanguage; } QString Game::orderTitle() const { return mOrderTitle; } QString Game::library() const { return mLibrary; } +QString Game::platformName() const { return mPlatformName; } //=============================================================================================================== // Game::Builder @@ -49,10 +49,10 @@ Game::Builder::Builder() {} //-Class Functions--------------------------------------------------------------------------------------------- //Private: -QString Game::Builder::kosherizeRawDate(QString date) +QString Game::Builder::kosherizeRawDate(const QString& date) { - static const QString DEFAULT_MONTH = "-01"; - static const QString DEFAULT_DAY = "-01"; + static const QString DEFAULT_MONTH = u"-01"_s; + static const QString DEFAULT_DAY = u"-01"_s; if(Qx::String::isOnlyNumbers(date) && date.length() == 4) // Year only return date + DEFAULT_MONTH + DEFAULT_DAY; @@ -71,30 +71,82 @@ QString Game::Builder::kosherizeRawDate(QString date) //-Instance Functions------------------------------------------------------------------------------------------ //Public: -Game::Builder& Game::Builder::wId(QString rawId) { mGameBlueprint.mId = QUuid(rawId); return *this; } -Game::Builder& Game::Builder::wTitle(QString title) { mGameBlueprint.mTitle = title; return *this; } -Game::Builder& Game::Builder::wSeries(QString series) { mGameBlueprint.mSeries = series; return *this; } -Game::Builder& Game::Builder::wDeveloper(QString developer) { mGameBlueprint.mDeveloper = developer; return *this; } -Game::Builder& Game::Builder::wPublisher(QString publisher) { mGameBlueprint.mPublisher = publisher; return *this; } -Game::Builder& Game::Builder::wDateAdded(QString rawDateAdded) { mGameBlueprint.mDateAdded = QDateTime::fromString(rawDateAdded, Qt::ISODateWithMs); return *this; } -Game::Builder& Game::Builder::wDateModified(QString rawDateModified) { mGameBlueprint.mDateModified = QDateTime::fromString(rawDateModified, Qt::ISODateWithMs); return *this; } -Game::Builder& Game::Builder::wPlatform(QString platform) { mGameBlueprint.mPlatform = platform; return *this; } -Game::Builder& Game::Builder::wBroken(QString rawBroken) { mGameBlueprint.mBroken = rawBroken.toInt() != 0; return *this; } -Game::Builder& Game::Builder::wPlayMode(QString playMode) { mGameBlueprint.mPlayMode = playMode; return *this; } -Game::Builder& Game::Builder::wStatus(QString status) { mGameBlueprint.mStatus = status; return *this; } -Game::Builder& Game::Builder::wNotes(QString notes) { mGameBlueprint.mNotes = notes; return *this; } -Game::Builder& Game::Builder::wSource(QString source) { mGameBlueprint.mSource = source; return *this; } -Game::Builder& Game::Builder::wAppPath(QString appPath) { mGameBlueprint.mAppPath = appPath; return *this; } -Game::Builder& Game::Builder::wLaunchCommand(QString launchCommand) { mGameBlueprint.mLaunchCommand = launchCommand; return *this; } -Game::Builder& Game::Builder::wReleaseDate(QString rawReleaseDate) { mGameBlueprint.mReleaseDate = QDateTime::fromString(kosherizeRawDate(rawReleaseDate), Qt::ISODate); return *this; } -Game::Builder& Game::Builder::wVersion(QString version) { mGameBlueprint.mVersion = version; return *this; } -Game::Builder& Game::Builder::wOriginalDescription(QString originalDescription) { mGameBlueprint.mOriginalDescription = originalDescription; return *this; } -Game::Builder& Game::Builder::wLanguage(QString language) { mGameBlueprint.mLanguage = language; return *this; } -Game::Builder& Game::Builder::wOrderTitle(QString orderTitle) { mGameBlueprint.mOrderTitle = orderTitle; return *this; } -Game::Builder& Game::Builder::wLibrary(QString library) { mGameBlueprint.mLibrary = library; return *this; } +Game::Builder& Game::Builder::wId(QStringView rawId) { mGameBlueprint.mId = QUuid(rawId); return *this; } +Game::Builder& Game::Builder::wTitle(const QString& title) { mGameBlueprint.mTitle = title; return *this; } +Game::Builder& Game::Builder::wSeries(const QString& series) { mGameBlueprint.mSeries = series; return *this; } +Game::Builder& Game::Builder::wDeveloper(const QString& developer) { mGameBlueprint.mDeveloper = developer; return *this; } +Game::Builder& Game::Builder::wPublisher(const QString& publisher) { mGameBlueprint.mPublisher = publisher; return *this; } +Game::Builder& Game::Builder::wDateAdded(QStringView rawDateAdded) { mGameBlueprint.mDateAdded = QDateTime::fromString(rawDateAdded, Qt::ISODateWithMs); return *this; } +Game::Builder& Game::Builder::wDateModified(QStringView rawDateModified) { mGameBlueprint.mDateModified = QDateTime::fromString(rawDateModified, Qt::ISODateWithMs); return *this; } +Game::Builder& Game::Builder::wBroken(QStringView rawBroken) { mGameBlueprint.mBroken = rawBroken.toInt() != 0; return *this; } +Game::Builder& Game::Builder::wPlayMode(const QString& playMode) { mGameBlueprint.mPlayMode = playMode; return *this; } +Game::Builder& Game::Builder::wStatus(const QString& status) { mGameBlueprint.mStatus = status; return *this; } +Game::Builder& Game::Builder::wNotes(const QString& notes) { mGameBlueprint.mNotes = notes; return *this; } +Game::Builder& Game::Builder::wSource(const QString& source) { mGameBlueprint.mSource = source; return *this; } +Game::Builder& Game::Builder::wAppPath(const QString& appPath) { mGameBlueprint.mAppPath = appPath; return *this; } +Game::Builder& Game::Builder::wLaunchCommand(const QString& launchCommand) { mGameBlueprint.mLaunchCommand = launchCommand; return *this; } +Game::Builder& Game::Builder::wReleaseDate(QStringView rawReleaseDate) { mGameBlueprint.mReleaseDate = QDateTime::fromString(kosherizeRawDate(rawReleaseDate.toString()), Qt::ISODate); return *this; } +Game::Builder& Game::Builder::wVersion(const QString& version) { mGameBlueprint.mVersion = version; return *this; } +Game::Builder& Game::Builder::wOriginalDescription(const QString& originalDescription) { mGameBlueprint.mOriginalDescription = originalDescription; return *this; } +Game::Builder& Game::Builder::wLanguage(const QString& language) { mGameBlueprint.mLanguage = language; return *this; } +Game::Builder& Game::Builder::wOrderTitle(const QString& orderTitle) { mGameBlueprint.mOrderTitle = orderTitle; return *this; } +Game::Builder& Game::Builder::wLibrary(const QString& library) { mGameBlueprint.mLibrary = library; return *this; } +Game::Builder& Game::Builder::wPlatformName(const QString& platformName) { mGameBlueprint.mPlatformName = platformName; return *this; } Game Game::Builder::build() { return mGameBlueprint; } +//=============================================================================================================== +// GameData +//=============================================================================================================== + +//-Constructor------------------------------------------------------------------------------------------------ +//Public: +GameData::GameData() : + mNull(true) +{} + +//-Instance Functions------------------------------------------------------------------------------------------------ +//Public: +bool GameData::isNull() const { return mNull; } + +quint32 GameData::id() const { return mId; } +QUuid GameData::gameId() const { return mGameId; } +QString GameData::title() const { return mTitle; } +QDateTime GameData::dateAdded() const { return mDateAdded; } +QString GameData::sha256() const { return mSha256; } +quint32 GameData::crc32() const { return mCrc32; } +bool GameData::presentOnDisk() const { return mPresentOnDisk; } +QString GameData::path() const { return mPath; } +quint32 GameData::size() const { return mSize; } +QString GameData::parameters() const { return mParameters; } +QString GameData::appPath() const { return mAppPath; } +QString GameData::launchCommand() const { return mLaunchCommand; } + +//=============================================================================================================== +// GameData::Builder +//=============================================================================================================== + +//-Constructor------------------------------------------------------------------------------------------------- +//Public: +GameData::Builder::Builder() {} + +//-Instance Functions------------------------------------------------------------------------------------------ +//Public: +GameData::Builder& GameData::Builder::wId(QStringView rawId) { mGameDataBlueprint.mId = rawId.toInt(); return *this; } +GameData::Builder& GameData::Builder::wGameId(QStringView rawId) { mGameDataBlueprint.mGameId = QUuid(rawId); return *this; } +GameData::Builder& GameData::Builder::wTitle(const QString& title) { mGameDataBlueprint.mTitle = title; return *this; } +GameData::Builder& GameData::Builder::wDateAdded(QStringView rawDateAdded) { mGameDataBlueprint.mDateAdded = QDateTime::fromString(rawDateAdded, Qt::ISODateWithMs); return *this; } +GameData::Builder& GameData::Builder::wSha256(const QString& sha256) { mGameDataBlueprint.mSha256 = sha256; return *this; } +GameData::Builder& GameData::Builder::wCrc32(QStringView rawCrc32) { mGameDataBlueprint.mCrc32 = rawCrc32.toInt(); return *this; } +GameData::Builder& GameData::Builder::wPresentOnDisk(QStringView rawBroken) { mGameDataBlueprint.mPresentOnDisk = rawBroken.toInt() != 0; return *this; } +GameData::Builder& GameData::Builder::wPath(const QString& path) { mGameDataBlueprint.mPath = path; return *this; } +GameData::Builder& GameData::Builder::wSize(QStringView rawSize) { mGameDataBlueprint.mSize = rawSize.toInt(); return *this; } +GameData::Builder& GameData::Builder::wParameters(const QString& parameters) { mGameDataBlueprint.mParameters = parameters; return *this; } +GameData::Builder& GameData::Builder::wAppPath(const QString& appPath) { mGameDataBlueprint.mAppPath = appPath; return *this; } +GameData::Builder& GameData::Builder::wLaunchCommand(const QString& launchCommand) { mGameDataBlueprint.mLaunchCommand = launchCommand; return *this; } + +GameData GameData::Builder::build() { mGameDataBlueprint.mNull = false; return mGameDataBlueprint; } + //=============================================================================================================== // AddApp //=============================================================================================================== @@ -152,13 +204,13 @@ AddApp::Builder::Builder() {} //-Instance Functions------------------------------------------------------------------------------------------ //Public: -AddApp::Builder& AddApp::Builder::wId(QString rawId) { mAddAppBlueprint.mId = QUuid(rawId); return *this; } -AddApp::Builder& AddApp::Builder::wAppPath(QString appPath) { mAddAppBlueprint.mAppPath = appPath; return *this; } -AddApp::Builder& AddApp::Builder::wAutorunBefore(QString rawAutorunBefore) { mAddAppBlueprint.mAutorunBefore = rawAutorunBefore.toInt() != 0; return *this; } -AddApp::Builder& AddApp::Builder::wLaunchCommand(QString launchCommand) { mAddAppBlueprint.mLaunchCommand = launchCommand; return *this; } -AddApp::Builder& AddApp::Builder::wName(QString name) { mAddAppBlueprint.mName = name; return *this; } -AddApp::Builder& AddApp::Builder::wWaitExit(QString rawWaitExit) { mAddAppBlueprint.mWaitExit = rawWaitExit.toInt() != 0; return *this; } -AddApp::Builder& AddApp::Builder::wParentId(QString rawParentId) { mAddAppBlueprint.mParentId = QUuid(rawParentId); return *this; } +AddApp::Builder& AddApp::Builder::wId(QStringView rawId) { mAddAppBlueprint.mId = QUuid(rawId); return *this; } +AddApp::Builder& AddApp::Builder::wAppPath(const QString& appPath) { mAddAppBlueprint.mAppPath = appPath; return *this; } +AddApp::Builder& AddApp::Builder::wAutorunBefore(QStringView rawAutorunBefore) { mAddAppBlueprint.mAutorunBefore = rawAutorunBefore.toInt() != 0; return *this; } +AddApp::Builder& AddApp::Builder::wLaunchCommand(const QString& launchCommand) { mAddAppBlueprint.mLaunchCommand = launchCommand; return *this; } +AddApp::Builder& AddApp::Builder::wName(const QString& name) { mAddAppBlueprint.mName = name; return *this; } +AddApp::Builder& AddApp::Builder::wWaitExit(QStringView rawWaitExit) { mAddAppBlueprint.mWaitExit = rawWaitExit.toInt() != 0; return *this; } +AddApp::Builder& AddApp::Builder::wParentId(QStringView rawParentId) { mAddAppBlueprint.mParentId = QUuid(rawParentId); return *this; } AddApp AddApp::Builder::build() { return mAddAppBlueprint; } @@ -192,77 +244,73 @@ Set::Builder& Set::Builder::wAddApps(const QList& addApps) { mSetBluepri Set Set::Builder::build() { return mSetBlueprint; } //=============================================================================================================== -// Playlist +// PlaylistGame //=============================================================================================================== -//-Constructor------------------------------------------------------------------------------------------------- +//-Constructor------------------------------------------------------------------------------------------------ //Public: -Playlist::Playlist() {} +PlaylistGame::PlaylistGame() {} -//-Instance Functions------------------------------------------------------------------------------------------------------ +//-Instance Functions------------------------------------------------------------------------------------------------ //Public: -QUuid Playlist::id() const { return mId; } -QString Playlist::title() const { return mTitle; } -QString Playlist::description() const { return mDescription; } -QString Playlist::author() const { return mAuthor; } + +int PlaylistGame::id() const { return mId; } +QUuid PlaylistGame::playlistId() const { return mPlaylistId; } +int PlaylistGame::order() const { return mOrder; } +QUuid PlaylistGame::gameId() const { return mGameId; } //=============================================================================================================== -// Playlist::Builder +// PlaylistGame::Builder //=============================================================================================================== //-Constructor------------------------------------------------------------------------------------------------- //Public: -Playlist::Builder::Builder() {} +PlaylistGame::Builder::Builder() {} //-Instance Functions------------------------------------------------------------------------------------------ //Public: -Playlist::Builder& Playlist::Builder::wId(QString rawId) { mPlaylistBlueprint.mId = QUuid(rawId); return *this; } -Playlist::Builder& Playlist::Builder::wTitle(QString title) { mPlaylistBlueprint.mTitle = title; return *this; } -Playlist::Builder& Playlist::Builder::wDescription(QString description) { mPlaylistBlueprint.mDescription = description; return *this; } -Playlist::Builder& Playlist::Builder::wAuthor(QString author) { mPlaylistBlueprint.mAuthor = author; return *this; } +PlaylistGame::Builder& PlaylistGame::Builder::wId(int id) { mPlaylistGameBlueprint.mId = id; return *this; } +PlaylistGame::Builder& PlaylistGame::Builder::wPlaylistId(QStringView rawPlaylistId) { mPlaylistGameBlueprint.mPlaylistId = QUuid(rawPlaylistId); return *this; } +PlaylistGame::Builder& PlaylistGame::Builder::wOrder(int order) { mPlaylistGameBlueprint.mOrder = order; return *this; } +PlaylistGame::Builder& PlaylistGame::Builder::wGameId(QStringView rawGameId) { mPlaylistGameBlueprint.mGameId = QUuid(rawGameId); return *this; } -Playlist Playlist::Builder::build() { return mPlaylistBlueprint; } +PlaylistGame PlaylistGame::Builder::build() { return mPlaylistGameBlueprint; } //=============================================================================================================== -// PlaylistGame +// Playlist //=============================================================================================================== -//-Constructor------------------------------------------------------------------------------------------------ +//-Constructor------------------------------------------------------------------------------------------------- //Public: -PlaylistGame::PlaylistGame() {} +Playlist::Playlist() {} -//-Instance Functions------------------------------------------------------------------------------------------------ +//-Instance Functions------------------------------------------------------------------------------------------------------ //Public: - -int PlaylistGame::id() const { return mId; } -QUuid PlaylistGame::playlistId() const { return mPlaylistId; } -int PlaylistGame::order() const { return mOrder; } -QUuid PlaylistGame::gameId() const { return mGameId; } +QUuid Playlist::id() const { return mId; } +QString Playlist::title() const { return mTitle; } +QString Playlist::description() const { return mDescription; } +QString Playlist::author() const { return mAuthor; } +QString Playlist::library() const { return mLibrary; } +const QList& Playlist::playlistGames() const { return mPlaylistGames; } +QList& Playlist::playlistGames() { return mPlaylistGames; } //=============================================================================================================== -// PlaylistGame::Builder +// Playlist::Builder //=============================================================================================================== //-Constructor------------------------------------------------------------------------------------------------- //Public: - PlaylistGame::Builder::Builder() {} +Playlist::Builder::Builder() {} //-Instance Functions------------------------------------------------------------------------------------------ //Public: - PlaylistGame::Builder& PlaylistGame::Builder::wId(QString rawId) { mPlaylistGameBlueprint.mId = rawId.toInt(); return *this; } - PlaylistGame::Builder& PlaylistGame::Builder::wPlaylistId(QString rawPlaylistId) { mPlaylistGameBlueprint.mPlaylistId = QUuid(rawPlaylistId); return *this; } - - PlaylistGame::Builder& PlaylistGame::Builder::wOrder(QString rawOrder) - { - bool validInt = false; - mPlaylistGameBlueprint.mOrder = rawOrder.toInt(&validInt); - if(!validInt) - mPlaylistGameBlueprint.mOrder = -1; +Playlist::Builder& Playlist::Builder::wId(QStringView rawId) { mPlaylistBlueprint.mId = QUuid(rawId); return *this; } +Playlist::Builder& Playlist::Builder::wTitle(const QString& title) { mPlaylistBlueprint.mTitle = title; return *this; } +Playlist::Builder& Playlist::Builder::wDescription(const QString& description) { mPlaylistBlueprint.mDescription = description; return *this; } +Playlist::Builder& Playlist::Builder::wAuthor(const QString& author) { mPlaylistBlueprint.mAuthor = author; return *this; } +Playlist::Builder& Playlist::Builder::wLibrary(const QString& library) { mPlaylistBlueprint.mLibrary = library; return *this; } +Playlist::Builder& Playlist::Builder::wPlaylistGame(const PlaylistGame& playlistGame) { mPlaylistBlueprint.mPlaylistGames.append(playlistGame); return *this; } - return *this; - } - - PlaylistGame::Builder& PlaylistGame::Builder::wGameId(QString rawGameId) { mPlaylistGameBlueprint.mGameId = QUuid(rawGameId); return *this; } +Playlist Playlist::Builder::build() { return mPlaylistBlueprint; } - PlaylistGame PlaylistGame::Builder::build() { return mPlaylistGameBlueprint; } -}; +} diff --git a/lib/src/fp-json.cpp b/lib/src/fp-json.cpp deleted file mode 100644 index 424c2db..0000000 --- a/lib/src/fp-json.cpp +++ /dev/null @@ -1,479 +0,0 @@ -// Unit Includes -#include "fp/fp-json.h" - -// Qt Includes -#include -#include -#include - -// Qx Includes -#include -#include - -namespace Fp -{ - -//=============================================================================================================== -// JSON::START_STOP -//=============================================================================================================== - -//-Operators---------------------------------------------------------------------------------------------------- -//Public: -bool operator== (const Json::StartStop& lhs, const Json::StartStop& rhs) noexcept -{ - return lhs.path == rhs.path && lhs.filename == rhs.filename && lhs.arguments == rhs.arguments; -} - -//-Hashing------------------------------------------------------------------------------------------------------ -size_t qHash(const Json::StartStop& key, size_t seed) noexcept -{ - QtPrivate::QHashCombine hash; - seed = hash(seed, key.path); - seed = hash(seed, key.filename); - seed = hash(seed, key.arguments); - - return seed; -} - -//=============================================================================================================== -// JSON::SETTINGS_READER -//=============================================================================================================== - -//-Constructor-------------------------------------------------------------------------------------------------------- -//Public: -Json::SettingsReader::SettingsReader(Settings* targetSettings, std::shared_ptr sourceJsonFile) : - mTargetSettings(targetSettings), - mSourceJsonFile(sourceJsonFile) -{} - -//-Instance Functions------------------------------------------------------------------------------------------------- -//Public: -Qx::GenericError Json::SettingsReader::readInto() -{ - // Load original JSON file - QByteArray settingsData; - Qx::IoOpReport settingsLoadReport = Qx::readBytesFromFile(settingsData, *mSourceJsonFile); - - if(settingsLoadReport.isFailure()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), settingsLoadReport.outcomeInfo()); - - // Parse original JSON data - QJsonParseError parseError; - QJsonDocument settingsDocument = QJsonDocument::fromJson(settingsData, &parseError); - - if(settingsDocument.isNull()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), parseError.errorString()); - else - { - // Ensure top level container is object - if(!settingsDocument.isObject()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), ERR_JSON_UNEXP_FORMAT); - - return parseDocument(settingsDocument); - } -} - -//=============================================================================================================== -// JSON::CONFIG_READER -//=============================================================================================================== - -//-Constructor-------------------------------------------------------------------------------------------------------- -//Public: -Json::ConfigReader::ConfigReader(Config* targetConfig, std::shared_ptr sourceJsonFile) : - SettingsReader(targetConfig, sourceJsonFile) -{} - -//-Instance Functions------------------------------------------------------------------------------------------------- -//Private: -Qx::GenericError Json::ConfigReader::parseDocument(const QJsonDocument& configDoc) -{ - // Get derivation specific target - Config* targetConfig = static_cast(mTargetSettings); - - // Get values - Qx::GenericError valueError; - - if((valueError = Qx::Json::checkedKeyRetrieval(targetConfig->flashpointPath, configDoc.object(), Object_Config::KEY_FLASHPOINT_PATH)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(targetConfig->startServer, configDoc.object(), Object_Config::KEY_START_SERVER)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(targetConfig->server, configDoc.object(), Object_Config::KEY_SERVER)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Return invalid error on success - return Qx::GenericError(); - -} - -//=============================================================================================================== -// JSON::PREFERENCES_READER -//=============================================================================================================== - -//-Constructor-------------------------------------------------------------------------------------------------------- -//Public: -Json::PreferencesReader::PreferencesReader(Preferences* targetPreferences, std::shared_ptr sourceJsonFile) : - SettingsReader(targetPreferences, sourceJsonFile) -{} - -//-Instance Functions------------------------------------------------------------------------------------------------- -//Private: -Qx::GenericError Json::PreferencesReader::parseDocument(const QJsonDocument& prefDoc) -{ - // Get derivation specific target - Preferences* targetPreferences = static_cast(mTargetSettings); - - // Get values - Qx::GenericError valueError; - - // Single value - if((valueError = Qx::Json::checkedKeyRetrieval(targetPreferences->imageFolderPath, prefDoc.object(), Object_Preferences::KEY_IMAGE_FOLDER_PATH)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(targetPreferences->jsonFolderPath, prefDoc.object(), Object_Preferences::KEY_JSON_FOLDER_PATH)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(targetPreferences->htdocsFolderPath, prefDoc.object(), Object_Preferences::KEY_HTDOCS_FOLDER_PATH)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(targetPreferences->dataPacksFolderPath, prefDoc.object(), Object_Preferences::KEY_DATA_PACKS_FOLDER_PATH)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(targetPreferences->onDemandImages, prefDoc.object(), Object_Preferences::KEY_ON_DEMAND_IMAGES)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(targetPreferences->onDemandBaseUrl, prefDoc.object(), Object_Preferences::KEY_ON_DEMAND_BASE_URL)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(targetPreferences->browserModeProxy, prefDoc.object(), Object_Preferences::KEY_BROWSER_MODE_PROXY)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Get app path overrides - QJsonArray appPathOverrides; - if((valueError = Qx::Json::checkedKeyRetrieval(appPathOverrides, prefDoc.object(), Object_Preferences::KEY_APP_PATH_OVERRIDES)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Parse app path overrides - for(const QJsonValue& jvApo : qAsConst(appPathOverrides)) - { - AppPathOverride apoBuffer; - if((valueError = parseAppPathOverride(apoBuffer, jvApo)).isValid()) - return valueError; - - targetPreferences->appPathOverrides.append(apoBuffer); - } - - // Get native platforms - QJsonArray nativePlatforms; - if((valueError = Qx::Json::checkedKeyRetrieval(nativePlatforms, prefDoc.object(), Object_Preferences::KEY_NATIVE_PLATFORMS)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Parse native platforms - for(const QJsonValue& jvNativePlatform : qAsConst(nativePlatforms)) - { - QString nativePlatformBuffer; - if((valueError = parseNativePlatform(nativePlatformBuffer, jvNativePlatform)).isValid()) - return valueError; - - targetPreferences->nativePlatforms.insert(nativePlatformBuffer); - } - - // Return invalid error on success - return Qx::GenericError(); -} - -Qx::GenericError Json::PreferencesReader::parseAppPathOverride(AppPathOverride& apoBuffer, const QJsonValue& jvApo) -{ - // Ensure array element is Object - if(!jvApo.isObject()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), ERR_JSON_UNEXP_FORMAT); - - // Get app path override Object - QJsonObject joApo = jvApo.toObject(); - - // Value error checking buffer - Qx::GenericError valueError; - - // Get direct values - if((valueError = Qx::Json::checkedKeyRetrieval(apoBuffer.path, joApo, Object_AppPathOverrides::KEY_PATH)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(apoBuffer.override, joApo, Object_AppPathOverrides::KEY_OVERRIDE)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(apoBuffer.enabled, joApo, Object_AppPathOverrides::KEY_ENABLED)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Return invalid error on success - return Qx::GenericError(); -} - -Qx::GenericError Json::PreferencesReader::parseNativePlatform(QString& nativePlatformBuffer, const QJsonValue& jvNativePlatform) -{ - // Ensure array element is String - if(!jvNativePlatform.isString()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), ERR_JSON_UNEXP_FORMAT); - - // Get native platform string - nativePlatformBuffer = jvNativePlatform.toString(); - - // Return invalid error on success - return Qx::GenericError(); -} - -//=============================================================================================================== -// JSON::SERVICES_READER -//=============================================================================================================== - -//-Constructor-------------------------------------------------------------------------------------------------------- -//Public: -Json::ServicesReader::ServicesReader(Services* targetServices, std::shared_ptr sourceJsonFile, const MacroResolver* macroResolver) : - SettingsReader(targetServices, sourceJsonFile), - mHostMacroResolver(macroResolver) -{} - -//-Instance Functions------------------------------------------------------------------------------------------------- -//Private: -Qx::GenericError Json::ServicesReader::parseDocument(const QJsonDocument& servicesDoc) -{ - // Get derivation specific target - Services* targetServices = static_cast(mTargetSettings); - - // Value error checking buffer - Qx::GenericError valueError; - - // Get watches - // TODO: include logs - - // Get servers - QJsonArray jaServers; - if((valueError = Qx::Json::checkedKeyRetrieval(jaServers, servicesDoc.object(), Object_Services::KEY_SERVER)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Parse servers - for(const QJsonValue& jvServer : qAsConst(jaServers)) - { - ServerDaemon serverBuffer; - if((valueError = parseServerDaemon(serverBuffer, jvServer)).isValid()) - return valueError; - - targetServices->servers.insert(serverBuffer.name, serverBuffer); - } - - // Get daemons - QJsonArray jaDaemons; - if((valueError = Qx::Json::checkedKeyRetrieval(jaDaemons, servicesDoc.object(), Object_Services::KEY_DAEMON)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Parse daemons - for(const QJsonValue& jvDaemon : qAsConst(jaDaemons)) - { - ServerDaemon daemonBuffer; - if((valueError = parseServerDaemon(daemonBuffer, jvDaemon)).isValid()) - return valueError; - - targetServices->daemons.insert(daemonBuffer.name, daemonBuffer); - - /* NOTE: If for some reason this list becomes large, use a hash instead - * (e.g. if(hash.contains("NAME")){ recognizedDaemons.setFlag(hash["NAME]); } ) - */ - if(daemonBuffer.name.contains("qemu", Qt::CaseInsensitive) || - daemonBuffer.filename.contains("qemu", Qt::CaseInsensitive)) - targetServices->recognizedDaemons.setFlag(KnownDaemon::Qemu); - else if(daemonBuffer.name.contains("docker", Qt::CaseInsensitive) || - daemonBuffer.filename.contains("docker", Qt::CaseInsensitive)) - targetServices->recognizedDaemons.setFlag(KnownDaemon::Docker); - } - - // Get starts - QJsonArray jaStarts; - if((valueError = Qx::Json::checkedKeyRetrieval(jaStarts, servicesDoc.object(), Object_Services::KEY_START)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Parse starts - for(const QJsonValue& jvStart : qAsConst(jaStarts)) - { - StartStop startStopBuffer; - if((valueError = parseStartStop(startStopBuffer, jvStart)).isValid()) - return valueError; - - targetServices->starts.insert(startStopBuffer); - } - - // Get stops - QJsonArray jaStops; - if((valueError = Qx::Json::checkedKeyRetrieval(jaStops, servicesDoc.object(), Object_Services::KEY_STOP)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Parse starts - for(const QJsonValue& jvStop : qAsConst(jaStops)) - { - StartStop startStopBuffer; - if((valueError = parseStartStop(startStopBuffer, jvStop)).isValid()) - return valueError; - - targetServices->stops.insert(startStopBuffer); - } - - // Return invalid error on success - return Qx::GenericError(); -} - -Qx::GenericError Json::ServicesReader::parseServerDaemon(ServerDaemon& serverBuffer, const QJsonValue& jvServer) -{ - // Ensure array element is Object - if(!jvServer.isObject()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), ERR_JSON_UNEXP_FORMAT); - - // Get server Object - QJsonObject joServer = jvServer.toObject(); - - // Value error checking buffer - Qx::GenericError valueError; - - // Get direct values - if((valueError = Qx::Json::checkedKeyRetrieval(serverBuffer.name, joServer, Object_ServerDaemon::KEY_NAME)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(serverBuffer.path, joServer, Object_ServerDaemon::KEY_PATH)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(serverBuffer.filename, joServer, Object_ServerDaemon::KEY_FILENAME)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(serverBuffer.kill, joServer, Object_ServerDaemon::KEY_KILL)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Get arguments - QJsonArray jaArgs; - if((valueError = Qx::Json::checkedKeyRetrieval(jaArgs, joServer, Object_ServerDaemon::KEY_ARGUMENTS)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - for(const QJsonValue& jvArg : qAsConst(jaArgs)) - { - // Ensure array element is String - if(!jvArg.isString()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), ERR_JSON_UNEXP_FORMAT); - - serverBuffer.arguments.append(jvArg.toString()); - } - - // Resolve macros for relevant variables - serverBuffer.path = mHostMacroResolver->resolve(serverBuffer.path); - for(QString& arg : serverBuffer.arguments) - arg = mHostMacroResolver->resolve(arg); - - // Return invalid error on success - return Qx::GenericError(); -} - -Qx::GenericError Json::ServicesReader::parseStartStop(StartStop& startStopBuffer, const QJsonValue& jvStartStop) -{ - // Ensure return buffer is null - startStopBuffer = StartStop(); - - // Ensure array element is Object - if(!jvStartStop.isObject()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), ERR_JSON_UNEXP_FORMAT); - - // Get server Object - QJsonObject joStartStop = jvStartStop.toObject(); - - // Value error checking buffer - Qx::GenericError valueError; - - // Get direct values - if((valueError = Qx::Json::checkedKeyRetrieval(startStopBuffer.path, joStartStop , Object_StartStop::KEY_PATH)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - if((valueError = Qx::Json::checkedKeyRetrieval(startStopBuffer.filename, joStartStop, Object_StartStop::KEY_FILENAME)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Get arguments - QJsonArray jaArgs; - if((valueError = Qx::Json::checkedKeyRetrieval(jaArgs, joStartStop, Object_StartStop::KEY_ARGUMENTS)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - for(const QJsonValue& jvArg : qAsConst(jaArgs)) - { - // Ensure array element is String - if(!jvArg.isString()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), ERR_JSON_UNEXP_FORMAT); - - startStopBuffer.arguments.append(jvArg.toString()); - } - - // Resolve macros for relevant variables - startStopBuffer.path = mHostMacroResolver->resolve(startStopBuffer.path); - for(QString& arg : startStopBuffer.arguments) - arg = mHostMacroResolver->resolve(arg); - - // Return invalid error on success - return Qx::GenericError(); -} - -//=============================================================================================================== -// JSON::EXECS_READER -//=============================================================================================================== - -//-Constructor-------------------------------------------------------------------------------------------------------- -//Public: -Json::ExecsReader::ExecsReader(Execs* targetExecs, std::shared_ptr sourceJsonFile) : - SettingsReader(targetExecs, sourceJsonFile) -{} - -//-Instance Functions------------------------------------------------------------------------------------------------- -//Private: -Qx::GenericError Json::ExecsReader::parseDocument(const QJsonDocument& execsDoc) -{ - // Get derivation specific target - Execs* targetExecs = static_cast(mTargetSettings); - - // Value error checking buffer - Qx::GenericError valueError; - - // Get exec entries - QJsonArray jaExecs; - if((valueError = Qx::Json::checkedKeyRetrieval(jaExecs, execsDoc.object(), Object_Execs::KEY_EXECS)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - // Parse starts - for(const QJsonValue& jvExec : qAsConst(jaExecs)) - { - Exec execBuffer; - if((valueError = parseExec(execBuffer, jvExec)).isValid()) - return valueError; - - targetExecs->list.append(execBuffer); - } - - // Return invalid error on success - return Qx::GenericError(); -} - -Qx::GenericError Json::ExecsReader::parseExec(Exec& execBuffer, const QJsonValue& jvExec) -{ - // Ensure array element is Object - if(!jvExec.isObject()) - return Qx::GenericError(Qx::GenericError::Critical, ERR_PARSING_JSON_DOC.arg(mSourceJsonFile->fileName()), ERR_JSON_UNEXP_FORMAT); - - // Get exec Object - QJsonObject joExec = jvExec.toObject(); - - // Value error checking buffer - Qx::GenericError valueError; - - // Get direct values (ignore errors for optional values) - if((valueError = Qx::Json::checkedKeyRetrieval(execBuffer.win32, joExec , Object_Exec::KEY_WIN32)).isValid()) - return valueError.setErrorLevel(Qx::GenericError::Critical); - - Qx::Json::checkedKeyRetrieval(execBuffer.linux, joExec , Object_Exec::KEY_LINUX); - Qx::Json::checkedKeyRetrieval(execBuffer.wine, joExec , Object_Exec::KEY_WINE); - - - // Return invalid error on success - return Qx::GenericError(); -} - -} diff --git a/lib/src/fp-macro.cpp b/lib/src/fp-macro.cpp index 85af2c9..0d588dc 100644 --- a/lib/src/fp-macro.cpp +++ b/lib/src/fp-macro.cpp @@ -10,7 +10,7 @@ namespace Fp //-Constructor------------------------------------------------------------------------------------------------ //Public: -MacroResolver::MacroResolver(QString installPath, const Key&) +MacroResolver::MacroResolver(const QString& installPath, const Key&) { mMacroMap[FP_PATH] = installPath; } diff --git a/lib/src/fp-playlistmanager.cpp b/lib/src/fp-playlistmanager.cpp new file mode 100644 index 0000000..fb410bf --- /dev/null +++ b/lib/src/fp-playlistmanager.cpp @@ -0,0 +1,127 @@ +// Unit Includes +#include "fp/fp-playlistmanager.h" + +// Qt Includes +#include + +// Qx Includes +#include +#include + +namespace Json +{ + +struct PlaylistGame +{ + int id; + QString playlistId; + int order; + QString gameId; + + QX_JSON_STRUCT( + id, + playlistId, + order, + gameId + ); +}; + +struct Playlist +{ + QString id; + QList games; + QString title; + QString description; + QString author; + QString library; + + QX_JSON_STRUCT( + id, + games, + title, + description, + author, + library + ); +}; + +} + +namespace Fp +{ + +//=============================================================================================================== +// PlaylistManager +//=============================================================================================================== + +//-Constructor------------------------------------------------------------------------------------------------ +//Public: +PlaylistManager::PlaylistManager(const QDir& folder, const Key&) : + mPopulated(false), + mFolder(folder) +{ + mFolder.setNameFilters({u"*.json"_s}); + mFolder.setFilter(QDir::Files); +} + +//-Instance Functions------------------------------------------------------------------------------------------------ +//Public: +bool PlaylistManager::isPopulated() const { return mPopulated; } + +Qx::Error PlaylistManager::populate() +{ + if(mPopulated) + return Qx::Error(); + + mPopulated = true; + + // Parse each JSON format playlist + QDirIterator playlistItr(mFolder); + while (playlistItr.hasNext()) + { + QFile playlistFile(playlistItr.next()); + + // Read raw data + QByteArray playlistData; + if(Qx::IoOpReport rr = Qx::readBytesFromFile(playlistData, playlistFile); rr.isFailure()) + return rr; + + // Parse to JSON + QJsonParseError parseError; + QJsonDocument playlistDoc = QJsonDocument::fromJson(playlistData, &parseError); + if(parseError.error != QJsonParseError::NoError) + return parseError; + + // Parse to known JSON structure + Json::Playlist jPlaylist; + if(Qx::JsonError je = Qx::parseJson(jPlaylist, playlistDoc); je.isValid()) + return je; + + // Convert to FP item + Playlist::Builder pb; + pb.wId(jPlaylist.id) + .wTitle(jPlaylist.title) + .wDescription(jPlaylist.description) + .wAuthor(jPlaylist.author); + + for(const Json::PlaylistGame& jPlaylistGame : qAsConst(jPlaylist.games)) + { + PlaylistGame::Builder pgb; + pgb.wId(jPlaylistGame.id) + .wPlaylistId(jPlaylistGame.playlistId) + .wOrder(jPlaylistGame.order) + .wGameId(jPlaylistGame.gameId); + + pb.wPlaylistGame(pgb.build()); + } + + mTitles.append(jPlaylist.title); + mPlaylists.append(pb.build()); + } + + return Qx::Error(); +} + +QList PlaylistManager::playlists() const { return mPlaylists; } +QStringList PlaylistManager::playlistTitles() const { return mTitles; } +} diff --git a/lib/src/settings/fp-config.cpp b/lib/src/settings/fp-config.cpp new file mode 100644 index 0000000..db1732c --- /dev/null +++ b/lib/src/settings/fp-config.cpp @@ -0,0 +1,35 @@ +// Unit Includes +#include "fp/settings/fp-config.h" + +// Json struct parsing implementation +QX_JSON_STRUCT_OUTSIDE(Fp::Config, + flashpointPath, + startServer, + server +) + +namespace Fp +{ + +//=============================================================================================================== +// ConfigReader +//=============================================================================================================== + +//-Constructor-------------------------------------------------------------------------------------------------------- +//Public: +ConfigReader::ConfigReader(Config* targetConfig, std::shared_ptr sourceJsonFile) : + SettingsReader(targetConfig, sourceJsonFile) +{} + +//-Instance Functions------------------------------------------------------------------------------------------------- +//Private: +Qx::JsonError ConfigReader::parseDocument(const QJsonDocument& configDoc) +{ + // Get derivation specific target + Config* targetConfig = static_cast(mTargetSettings); + + // Parse + return Qx::parseJson(*targetConfig, configDoc); +} + +} diff --git a/lib/src/settings/fp-execs.cpp b/lib/src/settings/fp-execs.cpp new file mode 100644 index 0000000..07f056b --- /dev/null +++ b/lib/src/settings/fp-execs.cpp @@ -0,0 +1,38 @@ +// Unit Includes +#include "fp/settings/fp-execs.h" + +// Json struct parsing implementation +QX_JSON_STRUCT_OUTSIDE(Fp::Exec, + linux, + win32, + wine +); + +QX_JSON_STRUCT_OUTSIDE(Fp::Execs, + list +); + +namespace Fp +{ +//=============================================================================================================== +// ExecsReader +//=============================================================================================================== + +//-Constructor-------------------------------------------------------------------------------------------------------- +//Public: +ExecsReader::ExecsReader(Execs* targetExecs, std::shared_ptr sourceJsonFile) : + SettingsReader(targetExecs, sourceJsonFile) +{} + +//-Instance Functions------------------------------------------------------------------------------------------------- +//Private: +Qx::JsonError ExecsReader::parseDocument(const QJsonDocument& execsDoc) +{ + // Get derivation specific target + Execs* targetExecs = static_cast(mTargetSettings); + + // Parse + return Qx::parseJson(*targetExecs, execsDoc); +} + +} diff --git a/lib/src/settings/fp-preferences.cpp b/lib/src/settings/fp-preferences.cpp new file mode 100644 index 0000000..aa66ca3 --- /dev/null +++ b/lib/src/settings/fp-preferences.cpp @@ -0,0 +1,85 @@ +// Unit Includes +#include "fp/settings/fp-preferences.h" + +// Qx Includes +#include + +// Configure key generator for mappable types +namespace QxJson +{ + +template<> +QString keygen(const Fp::GameDataSource& value) +{ + return value.name; +}; + +template<> +QString keygen(const Fp::GameMetadataSource& value) +{ + return value.name; +}; + +} + +// Json struct parsing implementation +QX_JSON_STRUCT_OUTSIDE(Fp::AppPathOverride, + path, + override, + enabled +); + +QX_JSON_STRUCT_OUTSIDE(Fp::GameDataSource, + arguments, + name, + type +); + +QX_JSON_STRUCT_OUTSIDE(Fp::GameMetadataSource, + baseUrl, + name +); + +QX_JSON_STRUCT_OUTSIDE(Fp::Preferences, + fpfssBaseUrl, + gameDataSources, + gameMetadataSources, + imageFolderPath, + playlistFolderPath, + jsonFolderPath, + htdocsFolderPath, + dataPacksFolderPath, + onDemandImages, + onDemandImagesCompressed, + onDemandBaseUrl, + appPathOverrides, + nativePlatforms, + browserModeProxy, + server +); + +namespace Fp +{ + +//=============================================================================================================== +// PreferencesReader +//=============================================================================================================== + +//-Constructor-------------------------------------------------------------------------------------------------------- +//Public: +PreferencesReader::PreferencesReader(Preferences* targetPreferences, std::shared_ptr sourceJsonFile) : + SettingsReader(targetPreferences, sourceJsonFile) +{} + +//-Instance Functions------------------------------------------------------------------------------------------------- +//Private: +Qx::JsonError PreferencesReader::parseDocument(const QJsonDocument& prefDoc) +{ + // Get derivation specific target + Preferences* targetPreferences = static_cast(mTargetSettings); + + // Parse + return Qx::parseJson(*targetPreferences, prefDoc); +} + +} diff --git a/lib/src/settings/fp-services.cpp b/lib/src/settings/fp-services.cpp new file mode 100644 index 0000000..bea03d8 --- /dev/null +++ b/lib/src/settings/fp-services.cpp @@ -0,0 +1,107 @@ +// Unit Includes +#include "fp/settings/fp-services.h" + +// Qx Includes +#include + +// Json struct parsing implementation + +// Configure key generator for mappable types +namespace QxJson +{ + +template<> +QString keygen(const Fp::ServerDaemon& value) +{ + return value.name; +}; + +} + +// Json struct parsing implementation +QX_JSON_STRUCT_OUTSIDE(Fp::ServerDaemon, + name, + path, + filename, + arguments, + kill +); + +QX_JSON_STRUCT_OUTSIDE(Fp::StartStop, + path, + filename, + arguments +); + +QX_JSON_STRUCT_OUTSIDE(Fp::Services, + server, + daemon, + start, + stop, +); + +namespace Fp +{ +//=============================================================================================================== +// StartStop +//=============================================================================================================== + +//-Operators---------------------------------------------------------------------------------------------------- +//Public: +bool operator== (const StartStop& lhs, const StartStop& rhs) noexcept +{ + return lhs.path == rhs.path && lhs.filename == rhs.filename && lhs.arguments == rhs.arguments; +} + +//-Hashing------------------------------------------------------------------------------------------------------ +size_t qHash(const StartStop& key, size_t seed) noexcept +{ + QtPrivate::QHashCombine hash; + seed = hash(seed, key.path); + seed = hash(seed, key.filename); + seed = hash(seed, key.arguments); + + return seed; +} + +//=============================================================================================================== +// ServicesReader +//=============================================================================================================== + +//-Constructor-------------------------------------------------------------------------------------------------------- +//Public: +ServicesReader::ServicesReader(Services* targetServices, std::shared_ptr sourceJsonFile, const MacroResolver* macroResolver) : + SettingsReader(targetServices, sourceJsonFile), + mHostMacroResolver(macroResolver) +{} + +//-Instance Functions------------------------------------------------------------------------------------------------- +//Private: +Qx::JsonError ServicesReader::parseDocument(const QJsonDocument& servicesDoc) +{ + // Get derivation specific target + Services* targetServices = static_cast(mTargetSettings); + + // Parse + Qx::JsonError err = Qx::parseJson(*targetServices, servicesDoc); + if(err.isValid()) + return err; + + // Check for known daemons + for(const ServerDaemon& d : qAsConst(targetServices->daemon)) + { + /* NOTE: If for some reason this list becomes large, use a hash instead + * (e.g. if(hash.contains(u"NAME"_s)){ recognizedDaemons.setFlag(hash["NAME]); } ) + */ + if(d.name.contains(u"qemu"_s, Qt::CaseInsensitive) || + d.filename.contains(u"qemu"_s, Qt::CaseInsensitive)) + targetServices->recognizedDaemons.setFlag(KnownDaemon::Qemu); + else if(d.name.contains(u"docker"_s, Qt::CaseInsensitive) || + d.filename.contains(u"docker"_s, Qt::CaseInsensitive)) + targetServices->recognizedDaemons.setFlag(KnownDaemon::Docker); + } + + return Qx::JsonError(); +} + +} diff --git a/lib/src/settings/fp-settings.cpp b/lib/src/settings/fp-settings.cpp new file mode 100644 index 0000000..e12d847 --- /dev/null +++ b/lib/src/settings/fp-settings.cpp @@ -0,0 +1,48 @@ +// Unit Includes +#include "fp/settings/fp-settings.h" + +// Qt Includes +#include +#include +#include + +// Qx Includes +#include +#include + +namespace Fp +{ + +//=============================================================================================================== +// SettingsReader +//=============================================================================================================== + +//-Constructor-------------------------------------------------------------------------------------------------------- +//Public: +SettingsReader::SettingsReader(Settings* targetSettings, std::shared_ptr sourceJsonFile) : + mTargetSettings(targetSettings), + mSourceJsonFile(sourceJsonFile) +{} + +//-Instance Functions------------------------------------------------------------------------------------------------- +//Public: +Qx::Error SettingsReader::readInto() +{ + // Load original JSON file + QByteArray settingsData; + Qx::IoOpReport settingsLoadReport = Qx::readBytesFromFile(settingsData, *mSourceJsonFile); + + if(settingsLoadReport.isFailure()) + return settingsLoadReport; + + // Parse original JSON data + QJsonParseError parseError; + QJsonDocument settingsDocument = QJsonDocument::fromJson(settingsData, &parseError); + + if(settingsDocument.isNull()) + return Qx::Error(parseError).setSeverity(Qx::Critical); + else + return parseDocument(settingsDocument); +} + +}