Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.23.0...3.26.0)
# Project
# NOTE: DON'T USE TRAILING ZEROS IN VERSIONS
project(libfp
VERSION 0.6
VERSION 0.6.1
LANGUAGES CXX
DESCRIPTION "C++ support library for Flashpoint Archive"
)
Expand Down Expand Up @@ -53,7 +53,7 @@ set(LIBFP_QX_COMPONENTS

include(OB/FetchQx)
ob_fetch_qx(
REF "v0.7"
REF "v0.7.0.3"
COMPONENTS
${LIBFP_QX_COMPONENTS}
)
Expand Down
1 change: 1 addition & 0 deletions lib/include/fp/fp-db.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ class FP_FP_EXPORT Db

// Write
DbError updateGameDataOnDiskState(QList<int> packIds, bool onDisk);
DbError updateGamePlayRecords(const QUuid& gameId, qint64 additionalSeconds);

// Helper
DbError entryUsesDataPack(bool& resultBuffer, const QUuid& gameId);
Expand Down
4 changes: 4 additions & 0 deletions lib/include/fp/fp-items.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class FP_FP_EXPORT Game
QString language;
QString orderTitle;
QString library;
qint64 playtime;
qint64 playCounter;
QString platformName;
QString ruffleSupport; // Could be an enum
};
Expand Down Expand Up @@ -97,6 +99,8 @@ class FP_FP_EXPORT Game
QString language() const;
QString orderTitle() const;
Library library() const;
qint64 playtime() const;
qint64 playCounter() const;
QString platformName() const;
QString ruffleSupport() const;
};
Expand Down
7 changes: 5 additions & 2 deletions lib/include/fp/fp-toolkit.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class FP_FP_EXPORT Toolkit

//-Instance Variables-----------------------------------------------------------------------------------------------
private:
const Install& mInstall;
Install& mInstall;

// Implementation Details
QString mEntryLocalLogoTemplate;
Expand All @@ -59,7 +59,7 @@ class FP_FP_EXPORT Toolkit

//-Constructor-------------------------------------------------------------------------------------------------
public:
Toolkit(const Install& install, const Key&);
Toolkit(Install& install, const Key&);

//-Class Functions-----------------------------------------------------------------------------------------------
private:
Expand Down Expand Up @@ -95,6 +95,9 @@ class FP_FP_EXPORT Toolkit
QUrl datapackUrl(const Fp::GameData& gameData) const;
bool datapackIsPresent(const Fp::GameData& gameData) const;

// Tags
bool entryIsExtreme(const QUuid& id) const;
bool entryIsExtreme(const Entry& entry) const;
};

}
Expand Down
15 changes: 15 additions & 0 deletions lib/include/fp/settings/fp-preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@
namespace Fp
{

struct FP_FP_EXPORT TagFilter
{
QString name;
QString description;
bool enabled;
QStringList tags;
QStringList categories;
QStringList childFilters;
bool extreme;
//QByteArray iconBase64;
};

struct FP_FP_EXPORT AppPathOverride
{
QString path;
Expand Down Expand Up @@ -41,9 +53,12 @@ struct FP_FP_EXPORT GameMetadataSource

struct FP_FP_EXPORT Preferences : public Settings
{
QList<TagFilter> tagFilters;
std::optional<QString> fpfssBaseUrl;
std::optional<QHash<QString, GameDataSource>> gameDataSources;
std::optional<QHash<QString, GameMetadataSource>> gameMetadataSources;
bool enablePlaytimeTracking;
bool enablePlaytimeTrackingExtreme;
QString imageFolderPath;
QString logoFolderPath;
QString playlistFolderPath;
Expand Down
29 changes: 27 additions & 2 deletions lib/src/fp-db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ QX_SQL_STRUCT_OUTSIDE_FULL(Fp::Game::Sql, "game", GameSqlQ,
dateModified, broken, playMode, status, notes, source,
applicationPath, launchCommand, releaseDate, version,
originalDescription, language, orderTitle, library,
platformName, ruffleSupport
playtime, playCounter, platformName, ruffleSupport
);

QX_SQL_STRUCT_OUTSIDE_FULL(Fp::GameData::Sql, "game_data", GameDataSqlQ,
Expand Down Expand Up @@ -415,7 +415,7 @@ Qx::SqlError Db::populateTags()
.primaryAlias = tagAliasMap.value(sql.primaryAliasId),
.category = {}
};
int catId = sql.categoryId;
int catId = sql.categoryId; // NOTE: Ignore garbage value warning
Q_ASSERT(mTagDirectory.contains(catId));
TagCategory& tc = mTagDirectory[catId];
tag.category = tc.name; // CoW reduces overhead
Expand Down Expand Up @@ -877,6 +877,31 @@ DbError Db::updateGameDataOnDiskState(QList<int> packIds, bool onDisk)
return DbError();
}

DbError Db::updateGamePlayRecords(const QUuid& gameId, qint64 additionalSeconds)
{
// Make query
auto updateQuery = mDatabase.UPDATE<Game::Sql>()
.SET(
GameSqlQ::playtime ^= GameSqlQ::playtime + additionalSeconds,
GameSqlQ::playCounter ^= GameSqlQ::playCounter + 1
)
.WHERE(GameSqlQ::id == gameId);

int affected;
if(auto err = updateQuery.execute(affected); err.isValid())
return err;

// Check that expected count was affected
int expected = 1;
if(affected != expected)
{
return DbError(DbError::UpdateRowMismatch, GameSqlQ::_.toString() + u" SET "_s + GameSqlQ::playtime.toString() + u", "_s + GameSqlQ::playCounter.toString(),
u"%1 instead of %2"_s.arg(affected, expected));
}

return DbError();
}

/* TODO: Technically this is a shortcut. The regular launcher will check for Game Redirects in all cases where an ID is searched
* for, often using coalesce (see https://github.com/FlashpointProject/FPA-Rust/blob/03a4ddc4af9ae0b2773c5f678268cb9c944d893f/crates/flashpoint-archive/src/game/mod.rs#L323).
* This makes sense if anyone is using this lib for any reason (which although that is the intention, currently no one is); but in the case of CLIFp/FIL, where
Expand Down
2 changes: 2 additions & 0 deletions lib/src/fp-items.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ QDateTime Game::releaseDate() const { return mData.releaseDate; }
QString Game::version() const { return mData.version; }
QString Game::originalDescription() const { return mData.originalDescription; }
QString Game::language() const { return mData.language; }
qint64 Game::playtime() const { return mData.playtime; }
qint64 Game::playCounter() const { return mData.playCounter; }
QString Game::orderTitle() const { return mData.orderTitle; }

Library Game::library() const
Expand Down
87 changes: 86 additions & 1 deletion lib/src/fp-toolkit.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
// Unit Includes
#include "fp/fp-toolkit.h"

// Standard Library Includes
#include <ranges>

// Qx Includes
#include <qx/utility/qx-helpers.h>

// Project Includes
#include "fp/fp-install.h"

Expand All @@ -13,7 +19,7 @@ namespace Fp

//-Constructor------------------------------------------------------------------------------------------------
//Public:
Toolkit::Toolkit(const Install& install, const Key&) :
Toolkit::Toolkit(Install& install, const Key&) :
mInstall(install)
{
// Setup (if this class grows expansive enough, these should be shifted towards RAII instead of doing it all at construction
Expand Down Expand Up @@ -218,4 +224,83 @@ bool Toolkit::datapackIsPresent(const Fp::GameData& gameData) const
return true;
}

bool Toolkit::entryIsExtreme(const QUuid& id) const
{
/* Going from an ID here to the Entry version, which just goes back to
* an ID seems wasteful, but we don't know if `id` belongs to a game or
* additional app, and in the case of an additional app we'd need to
* retreive it's data anyway to get `parentGameId`, so this is really only
* slightly wasteful in the case where `id` is a valid game id.
*/

// Maybe this should return an error, but for now a warning is fine if the.
Entry e;
if(auto err = mInstall.database()->getEntry(e, id); err)
{
qWarning("could not check if %s is extreme, entry not found", qPrintable(id.toString()));
return false;
}

return entryIsExtreme(e);
}

bool Toolkit::entryIsExtreme(const Entry& entry) const
{
QUuid id = entry.visit(qxFuncAggregate{
[](const Game& g) { return g.id(); },
[](const AddApp& aa) { return aa.parentGameId(); }
});

// Maybe this should return an error, but for now a warning is fine if the.
GameTags gt;
if(auto err = mInstall.database()->getGameTags(gt, id); err)
{
qWarning("could not check if %s is extreme, entry tags not found", qPrintable(id.toString()));
return false;
}
QStringList tags = gt.tags();

auto& tagFilters = mInstall.preferences().tagFilters;

// Clang++15 does not support range adapters (i.e. which make Qt containers work with ranges)
#if defined(__clang__) && __clang_major__ <= 15
return std::ranges::any_of(tags, [&](const QString& tag) {
const auto ttag = tag.trimmed();
for(const auto& tf : tagFilters)
{
if(!tf.extreme)
continue;

for(const QString& extremeTag : tf.tags)
if(extremeTag == ttag)
return true;
}

return false;
});

#else
auto extremeTagsView =
//tagFilters |
std::ranges::subrange(tagFilters.begin(), tagFilters.end()) |
std::views::filter([](const auto& tf) { return tf.extreme; }) |
std::views::transform([](const auto& tf) -> const QStringList& {
return tf.tags;
}) |
std::views::join;

/* TODO: Perhaps the tags in GameTags should be pre-trimmed during construction, but for now we just
* trim here as the vanilia launcher does in order to prevent any unexpeted edge cases where needing
* to match tags exactly (including errant whitespace) matters.
*/
return std::ranges::any_of(tags, [&](const QString& tag){
const auto ttag = tag.trimmed();
// TODO: C++23, replace with std::ranges::contains()
return std::ranges::find(extremeTagsView, ttag) != std::ranges::end(extremeTagsView);
});
#endif
}



}
2 changes: 2 additions & 0 deletions lib/src/settings/fp-preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ QX_JSON_STRUCT_OUTSIDE(Fp::Preferences,
fpfssBaseUrl,
gameDataSources,
gameMetadataSources,
enablePlaytimeTracking,
enablePlaytimeTrackingExtreme,
imageFolderPath,
logoFolderPath,
playlistFolderPath,
Expand Down