Skip to content

Commit

Permalink
LB: Add platforms under PlatformCategory "Flashpoint"
Browse files Browse the repository at this point in the history
  • Loading branch information
oblivioncth committed Jul 20, 2023
1 parent 31ac0b8 commit 13993eb
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 145 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ set(FIL_QX_COMPONENTS

include(OB/FetchQx)
ob_fetch_qx(
REF "2ff15119534672fb04d47c24628054e03a645819"
REF "4951c7abcb49033122f08545f80bf38ccf025b21"
COMPONENTS
${FIL_QX_COMPONENTS}
)
Expand Down
68 changes: 51 additions & 17 deletions app/src/frontend/fe-data.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <qx/core/qx-error.h>
#include <qx/core/qx-abstracterror.h>
#include <qx/utility/qx-macros.h>
#include <qx/utility/qx-concepts.h>

// libfp Includes
#include <fp/fp-items.h>
Expand Down Expand Up @@ -40,6 +41,32 @@

namespace Fe
{
//-Concepts------------------------------------------------------------------------------------------------------
template<typename T>
concept raw_item = std::derived_from<T, Item>;

template<typename T>
concept shared_item = Qx::specializes<T, std::shared_ptr> && std::derived_from<typename T::element_type, Item>;

template<typename T>
concept raw_basic_item = std::derived_from<T, BasicItem>;

template<typename T>
concept shared_basic_item = Qx::specializes<T, std::shared_ptr> && std::derived_from<typename T::element_type, BasicItem>;

template<typename T>
concept item = raw_item<T> || shared_item<T>;

template<typename T>
concept basic_item = raw_basic_item<T> || shared_basic_item<T>;

template<class K>
concept updateable_item_container = Qx::qassociative<K> && item<typename K::mapped_type>;

template<class K>
concept updateable_basicitem_container = Qx::qassociative<K> && basic_item<typename K::mapped_type> &&
std::same_as<typename K::key_type, QUuid>;

//-External Reference--------------------------------------------------------------------------------------------
class Install;
class DataDoc;
Expand Down Expand Up @@ -278,12 +305,19 @@ class UpdateableDoc : public DataDoc
protected:
explicit UpdateableDoc(Install* const parent, const QString& docPath, QString docName, UpdateOptions updateOptions);

//-Class Functions-----------------------------------------------------------------------------------------------------
template<typename T>
T* itemPtr(T& item) { return &item; }

template<typename T>
T* itemPtr(std::shared_ptr<T> item) { return item.get(); }

//-Instance Functions--------------------------------------------------------------------------------------------------
protected:
template <typename T, typename K>
requires std::derived_from<T, Item>
void finalizeUpdateableItems(QHash<K, std::shared_ptr<T>>& existingItems,
QHash<K, std::shared_ptr<T>>& finalItems)
template <typename C>
requires updateable_item_container<C>
void finalizeUpdateableItems(C& existingItems,
C& finalItems)
{
// Copy items to final list if obsolete entries are to be kept
if(!mUpdateOptions.removeObsolete)
Expand All @@ -293,20 +327,20 @@ class UpdateableDoc : public DataDoc
existingItems.clear();
}

template <typename T, typename K>
requires std::derived_from<T, Item>
void addUpdateableItem(QHash<K, std::shared_ptr<T>>& existingItems,
QHash<K, std::shared_ptr<T>>& finalItems,
K key,
std::shared_ptr<T> newItem)
template <typename C>
requires updateable_item_container<C>
void addUpdateableItem(C& existingItems,
C& finalItems,
C::key_type key,
C::mapped_type newItem)
{
// Check if game exists
// Check if item exists
if(existingItems.contains(key))
{
// Replace if existing update is on, move existing otherwise
if(mUpdateOptions.importMode == ImportMode::NewAndExisting)
{
newItem->transferOtherFields(existingItems[key]->otherFields());
itemPtr(newItem)->transferOtherFields(itemPtr(existingItems[key])->otherFields());
finalItems[key] = newItem;
existingItems.remove(key);
}
Expand All @@ -321,11 +355,11 @@ class UpdateableDoc : public DataDoc
finalItems[key] = newItem;
}

template <typename T>
requires std::derived_from<T, BasicItem>
void addUpdateableItem(QHash<QUuid, std::shared_ptr<T>>& existingItems,
QHash<QUuid, std::shared_ptr<T>>& finalItems,
std::shared_ptr<T> newItem)
template <typename C>
requires updateable_basicitem_container<C>
void addUpdateableItem(C& existingItems,
C& finalItems,
C::mapped_type newItem)
{
addUpdateableItem(existingItems,
finalItems,
Expand Down
130 changes: 80 additions & 50 deletions app/src/frontend/launchbox/lb-data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ namespace Element_Platform
const QString NAME = "Platform";

const QString ELEMENT_NAME = "Name";
const QString ELEMENT_CATEGORY = "Category";
};

namespace Element_PlatformFolder
Expand All @@ -103,6 +104,9 @@ namespace Element_PlatformFolder
namespace Element_PlatformCategory
{
const QString NAME = "PlatformCategory";

const QString ELEMENT_NAME = "Name";
const QString ELEMENT_NESTED_NAME = "NestedName";
};

const QString ROOT_ELEMENT = "LaunchBox";
Expand Down Expand Up @@ -793,8 +797,9 @@ bool PlaylistDoc::Writer::writePlaylistGame(const PlaylistGame& playlistGame)

//-Constructor--------------------------------------------------------------------------------------------------------
//Public:
PlatformsConfigDoc::PlatformsConfigDoc(Install* const parent, const QString& xmlPath, const DocKey&) :
Fe::DataDoc(parent, xmlPath, STD_NAME)
PlatformsConfigDoc::PlatformsConfigDoc(Install* const parent, const QString& xmlPath, Fe::UpdateOptions updateOptions,
const DocKey&) :
Fe::UpdateableDoc(parent, xmlPath, STD_NAME, updateOptions)
{}

//-Instance Functions--------------------------------------------------------------------------------------------------
Expand All @@ -804,40 +809,63 @@ Fe::DataDoc::Type PlatformsConfigDoc::type() const { return Fe::DataDoc::Type::C
//Public:
bool PlatformsConfigDoc::isEmpty() const
{
return mPlatforms.isEmpty() && mPlatformFolders.isEmpty() && mPlatformCategories.isEmpty();
return mPlatformsFinal.isEmpty() && mPlatformsExisting.isEmpty() &&
mPlatformFoldersFinal.isEmpty() && mPlatformFoldersExisting.isEmpty() &&
mPlatformCategoriesFinal.isEmpty() && mPlatformCategoriesExisting.isEmpty();
}

const QHash<QString, Platform>& PlatformsConfigDoc::platforms() const { return mPlatforms; }
const QMap<QString, QMap<QString, QString>>& PlatformsConfigDoc::platformFolders() const { return mPlatformFolders; }
const QList<PlatformCategory>& PlatformsConfigDoc::platformCategories() const { return mPlatformCategories; }

bool PlatformsConfigDoc::containsPlatform(QString name) { return mPlatforms.contains(name); }
const QHash<QString, Platform>& PlatformsConfigDoc::finalPlatforms() const { return mPlatformsFinal; }
const QMap<QString, PlatformFolder>& PlatformsConfigDoc::finalPlatformFolders() const { return mPlatformFoldersFinal; }
const QMap<QString, PlatformCategory>& PlatformsConfigDoc::finalPlatformCategories() const { return mPlatformCategoriesFinal; }

void PlatformsConfigDoc::addPlatform(Platform platform)
{
// Add platform, don't need to add media folders as LB will automatically set them to the defaults
mPlatforms[platform.name()] = platform;
addUpdateableItem(mPlatformsExisting, mPlatformsFinal, platform.name(), platform);
}

void PlatformsConfigDoc::removePlatform(QString name)
{
// Remove platform and any of its media folders (so LB will reset them to default)
mPlatforms.remove(name);
mPlatformFolders.remove(name);
mPlatformsFinal.remove(name);
mPlatformsExisting.remove(name);
removePlatformFolders(name);
}

void PlatformsConfigDoc::setMediaFolder(QString platform, QString mediaType, QString folderPath)
void PlatformsConfigDoc::addPlatformFolder(PlatformFolder platformFolder)
{
// Add platform if it's missing as LB will not add it by default just because it has media folders
if(!containsPlatform(platform))
{
Platform::Builder pb;
pb.wName(platform);
addPlatform(pb.build());
}
addUpdateableItem(mPlatformFoldersExisting, mPlatformFoldersFinal, platformFolder.identifier(), platformFolder);
}

void PlatformsConfigDoc::removePlatformFolders(QString platformName)
{
auto culler = [&platformName](QMap<QString, PlatformFolder>::iterator itr){
return itr.value().platform() == platformName;
};
mPlatformFoldersExisting.removeIf(culler);
mPlatformFoldersFinal.removeIf(culler);
}

// Set/add media folder
mPlatformFolders[platform][mediaType] = folderPath;
void PlatformsConfigDoc::addPlatformCategory(PlatformCategory platformCategory)
{
addUpdateableItem(mPlatformCategoriesExisting, mPlatformCategoriesFinal, platformCategory.name(), platformCategory);
}

void PlatformsConfigDoc::removePlatformCategory(QString categoryName)
{
mPlatformCategoriesFinal.remove(categoryName);
mPlatformCategoriesExisting.remove(categoryName);
}

void PlatformsConfigDoc::finalize()
{
// Finalize derived
finalizeUpdateableItems(mPlatformsExisting, mPlatformsFinal);
finalizeUpdateableItems(mPlatformFoldersExisting, mPlatformFoldersFinal);
finalizeUpdateableItems(mPlatformCategoriesExisting, mPlatformCategoriesFinal);

// Finalize base
Fe::UpdateableDoc::finalize();
}

//===============================================================================================================
Expand Down Expand Up @@ -889,32 +917,31 @@ void PlatformsConfigDoc::Reader::parsePlatform()
}

// Build Platform and add to document
std::shared_ptr<Platform> existingPlatform = pb.buildShared();
static_cast<PlatformsConfigDoc*>(mTargetDocument)->mPlatforms.insert(existingPlatform->name(), *existingPlatform);
Platform existingPlatform = pb.build();
static_cast<PlatformsConfigDoc*>(mTargetDocument)->mPlatformsExisting[existingPlatform.name()] = existingPlatform;
}

Fe::DocHandlingError PlatformsConfigDoc::Reader::parsePlatformFolder()
{
// Platform Folder to Build
QString platform;
QString mediaType;
QString folderPath;
PlatformFolder::Builder pfb;

// Cover all children
while(mStreamReader.readNextStartElement())
{
if(mStreamReader.name() == Xml::Element_PlatformFolder::ELEMENT_MEDIA_TYPE)
mediaType = mStreamReader.readElementText();
pfb.wMediaType(mStreamReader.readElementText());
else if(mStreamReader.name() == Xml::Element_PlatformFolder::ELEMENT_FOLDER_PATH)
folderPath = mStreamReader.readElementText();
pfb.wFolderPath(mStreamReader.readElementText());
else if(mStreamReader.name() == Xml::Element_PlatformFolder::ELEMENT_PLATFORM)
platform = mStreamReader.readElementText();
pfb.wPlatform(mStreamReader.readElementText());
else
return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocInvalidType);
}

// Add to document
static_cast<PlatformsConfigDoc*>(mTargetDocument)->mPlatformFolders[platform][mediaType] = folderPath;
// Build PlatformFolder and add to document
PlatformFolder existingPlatformFolder = pfb.build();
static_cast<PlatformsConfigDoc*>(mTargetDocument)->mPlatformFoldersExisting[existingPlatformFolder.identifier()] = existingPlatformFolder;

return Fe::DocHandlingError();
}
Expand All @@ -927,12 +954,19 @@ void PlatformsConfigDoc::Reader::parsePlatformCategory()
// Cover all children
while(mStreamReader.readNextStartElement())
{
// No specific elements are of interest for now
pcb.wOtherField({mStreamReader.name().toString(), mStreamReader.readElementText()});
if(mStreamReader.name() == Xml::Element_PlatformCategory::ELEMENT_NAME)
pcb.wName(mStreamReader.readElementText());
else if(mStreamReader.name() == Xml::Element_PlatformCategory::ELEMENT_NESTED_NAME)
pcb.wNestedName(mStreamReader.readElementText());
else
pcb.wOtherField({mStreamReader.name().toString(), mStreamReader.readElementText()});
}

// Build
PlatformCategory pc = pcb.build();

// Build Playlist Header and add to document
static_cast<PlatformsConfigDoc*>(mTargetDocument)->mPlatformCategories.append(pcb.build());
static_cast<PlatformsConfigDoc*>(mTargetDocument)->mPlatformCategoriesExisting[pc.name()] = pc;
}

//===============================================================================================================
Expand All @@ -951,25 +985,21 @@ PlatformsConfigDoc::Writer::Writer(PlatformsConfigDoc* sourceDoc) :
bool PlatformsConfigDoc::Writer::writeSourceDoc()
{
// Write all platforms
for(const Platform& platform : static_cast<PlatformsConfigDoc*>(mSourceDocument)->platforms())
for(const Platform& platform : static_cast<PlatformsConfigDoc*>(mSourceDocument)->finalPlatforms())
{
if(!writePlatform(platform))
return false;
}

// Write all platform folders
const QMap<QString, QMap<QString, QString>>& platformFolderMap = static_cast<PlatformsConfigDoc*>(mSourceDocument)->platformFolders();
QMap<QString, QMap<QString, QString>>::const_iterator i;
for(i = platformFolderMap.constBegin(); i != platformFolderMap.constEnd(); i++)
for(const PlatformFolder& platformFolder : static_cast<PlatformsConfigDoc*>(mSourceDocument)->finalPlatformFolders())
{
QMap<QString, QString>::const_iterator j;
for(j = i.value().constBegin(); j != i.value().constEnd(); j++)
if(!writePlatformFolder(i.key(), j.key(), j.value()))
return false;
if(!writePlatformFolder(platformFolder))
return false;
}

// Write all platform categories
for(const PlatformCategory& platformCategory : static_cast<PlatformsConfigDoc*>(mSourceDocument)->platformCategories())
for(const PlatformCategory& platformCategory : static_cast<PlatformsConfigDoc*>(mSourceDocument)->finalPlatformCategories())
{
if(!writePlatformCategory(platformCategory))
return false;
Expand All @@ -986,6 +1016,7 @@ bool PlatformsConfigDoc::Writer::writePlatform(const Platform& platform)

// Write known tags
writeCleanTextElement(Xml::Element_Platform::ELEMENT_NAME, platform.name());
writeCleanTextElement(Xml::Element_Platform::ELEMENT_CATEGORY, platform.category());

// Write other tags
writeOtherFields(platform.otherFields());
Expand All @@ -997,15 +1028,15 @@ bool PlatformsConfigDoc::Writer::writePlatform(const Platform& platform)
return !mStreamWriter.hasError();
}

bool PlatformsConfigDoc::Writer::writePlatformFolder(const QString& platform, const QString& mediaType, const QString& folderPath)
bool PlatformsConfigDoc::Writer::writePlatformFolder(const PlatformFolder& platformFoler)
{
// Write opening tag
mStreamWriter.writeStartElement(Xml::Element_PlatformFolder::NAME);

// Write known tags
writeCleanTextElement(Xml::Element_PlatformFolder::ELEMENT_MEDIA_TYPE, mediaType);
writeCleanTextElement(Xml::Element_PlatformFolder::ELEMENT_FOLDER_PATH, folderPath);
writeCleanTextElement(Xml::Element_PlatformFolder::ELEMENT_PLATFORM, platform);
writeCleanTextElement(Xml::Element_PlatformFolder::ELEMENT_MEDIA_TYPE, platformFoler.mediaType());
writeCleanTextElement(Xml::Element_PlatformFolder::ELEMENT_FOLDER_PATH, platformFoler.folderPath());
writeCleanTextElement(Xml::Element_PlatformFolder::ELEMENT_PLATFORM, platformFoler.platform());

// Close game tag
mStreamWriter.writeEndElement();
Expand All @@ -1020,9 +1051,8 @@ bool PlatformsConfigDoc::Writer::writePlatformCategory(const PlatformCategory& p
mStreamWriter.writeStartElement(Xml::Element_PlatformCategory::NAME);

// Write known tags
// None for now...
if(mStreamWriter.hasError())
return false;
writeCleanTextElement(Xml::Element_PlatformCategory::ELEMENT_NAME, platformCategory.name());
writeCleanTextElement(Xml::Element_PlatformCategory::ELEMENT_NESTED_NAME, platformCategory.nestedName());

// Write other tags
writeOtherFields(platformCategory.otherFields());
Expand Down
Loading

0 comments on commit 13993eb

Please sign in to comment.