diff --git a/CMakeLists.txt b/CMakeLists.txt index 01f34ac..956a194 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,7 +74,7 @@ endif() include(OB/FetchQx) ob_fetch_qx( - REF "0572d288936afd63ff6f5b6ce4b1fbfc0f03b0eb" + REF "98088b18de1ed812f55adaff3b030f273c42a6c2" COMPONENTS ${FIL_QX_COMPONENTS} ) @@ -104,7 +104,7 @@ ob_fetch_clifp("7139ae998b292eb595e751ba4cb8599230435358") # Fetch Neargye's Magic Enum include(OB/FetchMagicEnum) -ob_fetch_magicenum("v0.9.3") +ob_fetch_magicenum("v0.9.7") # Process Targets set(APP_TARGET_NAME ${PROJECT_NAMESPACE_LC}_${PROJECT_NAMESPACE_LC}) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index d5e7e9b..5dc495c 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -23,48 +23,53 @@ add_custom_target(fil_copy_clifp # ------------------ Setup FIL -------------------------- set(FIL_SOURCE - frontend/fe-data.h - frontend/fe-data.cpp - frontend/fe-installfoundation.h - frontend/fe-installfoundation.cpp - frontend/fe-installfoundation_win.cpp - frontend/fe-installfoundation_linux.cpp - frontend/fe-install.h - frontend/fe-install.cpp - frontend/fe-items.h - frontend/fe-items.cpp - frontend/attractmode/am-data.h - frontend/attractmode/am-data.cpp - frontend/attractmode/am-install.h - frontend/attractmode/am-install.cpp - frontend/attractmode/am-install_win.cpp - frontend/attractmode/am-install_linux.cpp - frontend/attractmode/am-items.h - frontend/attractmode/am-items.cpp - frontend/attractmode/am-settings-data.h - frontend/attractmode/am-settings-data.cpp - frontend/attractmode/am-settings-items.h - frontend/attractmode/am-settings-items.cpp + import/properties.h + import/properties.cpp + import/settings.h + import/worker.h + import/worker.cpp + launcher/lr-data.h + launcher/lr-data.cpp + launcher/lr-installfoundation.h + launcher/lr-installfoundation.cpp + launcher/lr-installfoundation_win.cpp + launcher/lr-installfoundation_linux.cpp + launcher/lr-install.h + launcher/lr-install.cpp + launcher/lr-items.h + launcher/lr-items.cpp + launcher/attractmode/am-data.h + launcher/attractmode/am-data.cpp + launcher/attractmode/am-install.h + launcher/attractmode/am-install.cpp + launcher/attractmode/am-install_win.cpp + launcher/attractmode/am-install_linux.cpp + launcher/attractmode/am-items.h + launcher/attractmode/am-items.cpp + launcher/attractmode/am-settings-data.h + launcher/attractmode/am-settings-data.cpp + launcher/attractmode/am-settings-items.h + launcher/attractmode/am-settings-items.cpp ui/mainwindow.h ui/mainwindow.cpp ui/mainwindow.ui ui/progresspresenter.h ui/progresspresenter.cpp - clifp.h - clifp.cpp - import-worker.h - import-worker.cpp + kernel/controller.h + kernel/controller.cpp + kernel/clifp.h + kernel/clifp.cpp main.cpp ) if(CMAKE_SYSTEM_NAME STREQUAL Windows) list(APPEND FIL_SOURCE - frontend/launchbox/lb-data.h - frontend/launchbox/lb-data.cpp - frontend/launchbox/lb-install.h - frontend/launchbox/lb-install.cpp - frontend/launchbox/lb-items.h - frontend/launchbox/lb-items.cpp + launcher/launchbox/lb-data.h + launcher/launchbox/lb-data.cpp + launcher/launchbox/lb-install.h + launcher/launchbox/lb-install.cpp + launcher/launchbox/lb-items.h + launcher/launchbox/lb-items.cpp ) endif() @@ -104,6 +109,11 @@ ob_add_standard_executable(${APP_TARGET_NAME} WIN32 ) +target_sources(fil_fil + PRIVATE + +) + # Note that the executable depends on CLIFp being copied into its build dir add_dependencies(${APP_TARGET_NAME} fil_copy_clifp) diff --git a/app/res/frontend/AttractMode/icon.png b/app/res/launcher/AttractMode/icon.png similarity index 100% rename from app/res/frontend/AttractMode/icon.png rename to app/res/launcher/AttractMode/icon.png diff --git a/app/res/frontend/AttractMode/marquee.png b/app/res/launcher/AttractMode/marquee.png similarity index 100% rename from app/res/frontend/AttractMode/marquee.png rename to app/res/launcher/AttractMode/marquee.png diff --git a/app/res/frontend/LaunchBox/icon.svg b/app/res/launcher/LaunchBox/icon.svg similarity index 100% rename from app/res/frontend/LaunchBox/icon.svg rename to app/res/launcher/LaunchBox/icon.svg diff --git a/app/res/resources.qrc b/app/res/resources.qrc index badfcbb..0ba26f3 100644 --- a/app/res/resources.qrc +++ b/app/res/resources.qrc @@ -8,9 +8,9 @@ ui/CLIFp.png ui/Exit.png ui/GitHub.png - frontend/LaunchBox/icon.svg - frontend/AttractMode/icon.png - frontend/AttractMode/marquee.png + launcher/LaunchBox/icon.svg + launcher/AttractMode/icon.png + launcher/AttractMode/marquee.png flashpoint/icon.png ui/About.png diff --git a/app/src/import/properties.cpp b/app/src/import/properties.cpp new file mode 100644 index 0000000..b77d3e8 --- /dev/null +++ b/app/src/import/properties.cpp @@ -0,0 +1,65 @@ +// Unit Include +#include "properties.h" + +namespace Import +{ + +//=============================================================================================================== +// Properties +//=============================================================================================================== + +//-Constructor------------------------------------------------------------- +//Public: +Properties::Properties() : + mHasLinkPerms(false) +{} + +//-Instance Functions------------------------------------------------------------- +//Public: +void Properties::setHasLinkPermissions(bool perms) { mHasLinkPerms = perms; } +bool Properties::hasLinkPermissions() const { return mHasLinkPerms; } + +Qx::Bindable Properties::bindableIsLauncherPresent() { return &mLauncherPresent; } +bool Properties::isLauncherPresent() const { return mLauncherPresent; } + +Qx::Bindable Properties::bindableIsLauncherReady() { return &mLauncherReady; } +bool Properties::isLauncherReady() const { return mLauncherReady; } + +Qx::Bindable Properties::bindableIsFlashpointPresent() { return &mFlashpointPresent; } +bool Properties::isFlashpointPresent() const { return mFlashpointPresent; } + +Qx::Bindable Properties::bindableIsFlashpointReady() { return &mFlashpointReady; } +bool Properties::isFlashpointReady() const { return mFlashpointReady; } + +Qx::Bindable Properties::bindableIsBothTargetsReady() { return &mBothTargetsReady; } +bool Properties::isBothTargetsReady() const { return mBothTargetsReady; } + +Qx::Bindable Properties::bindableIsFlashpointTargetSeries() { return &mFlashpointTargetSeries; } +bool Properties::isFlashpointTargetSeries() const { return mFlashpointTargetSeries; } + +Qx::Bindable> Properties::bindableImageModeOrder() { return &mImageModeOrder; } +const Qx::Bindable> Properties::bindableImageModeOrder() const { return &mImageModeOrder; } +QList Properties::imageModeOrder() const { return mImageModeOrder; } + +Qx::Bindable Properties::bindableIsImageDownloadable() { return &mImageDownloadable; } +bool Properties::isImageDownloadable() const { return mImageDownloadable; } + +Qx::Bindable Properties::bindableLauncherInfo() { return &mLauncherInfo; } +QString Properties::launcherInfo() const { return mLauncherInfo; } + +Qx::Bindable Properties::bindableFlashpointInfo() { return &mFlashpointInfo; } +QString Properties::flashpointInfo() const { return mFlashpointInfo; } + +Qx::Bindable> Properties::bindableTagMap() { return &mTagMap; } +const Qx::Bindable> Properties::bindableTagMap() const { return &mTagMap; } +QMap Properties::tagMap() const { return mTagMap; } + +Qx::Bindable> Properties::bindablePlatforms() { return &mPlatforms; } +const Qx::Bindable> Properties::bindablePlatforms() const { return &mPlatforms; } +QList Properties::platforms() const { return mPlatforms; } + +Qx::Bindable> Properties::bindablePlaylists() { return &mPlaylists; } +const Qx::Bindable> Properties::bindablePlaylists() const { return &mPlaylists; } +QList Properties::playlists() const { return mPlaylists; } + +} diff --git a/app/src/import/properties.h b/app/src/import/properties.h new file mode 100644 index 0000000..ff01d42 --- /dev/null +++ b/app/src/import/properties.h @@ -0,0 +1,101 @@ +#ifndef IMPORT_PROPERTIES_H +#define IMPORT_PROPERTIES_H + +// Qt Includes +#include + +// Qx Includes +#include + +// libfp Includes +#include + +// Project Includes +#include "import/settings.h" + +/* TODO: The number of properties here has gotten somewhat out of hand. + * Mainwindow should probably just be given access to something like + * const QBindable> (and same for + * launcher) so that it can setup its own bindings/properties directly + * off of that. Since it would be read only, that still lets Controller + * have control of the instances. + */ + +namespace Import +{ + +class Properties +{ +//-Instance Variables------------------------------------------------------------- +private: + bool mHasLinkPerms; + Qx::Property mLauncherPresent; + Qx::Property mLauncherReady; + Qx::Property mFlashpointPresent; + Qx::Property mFlashpointReady; + Qx::Property mBothTargetsReady; + Qx::Property mFlashpointTargetSeries; + Qx::Property> mImageModeOrder; + Qx::Property mImageDownloadable; + Qx::Property mLauncherInfo; + Qx::Property mFlashpointInfo; + Qx::Property> mTagMap; + Qx::Property> mPlatforms; + Qx::Property> mPlaylists; + +//-Constructor------------------------------------------------------------- +public: + Properties(); + +//-Instance Functions------------------------------------------------------------- +public: + void setHasLinkPermissions(bool perms); + bool hasLinkPermissions() const; + + Qx::Bindable bindableIsLauncherPresent(); + bool isLauncherPresent() const; + + Qx::Bindable bindableIsLauncherReady(); + bool isLauncherReady() const; + + Qx::Bindable bindableIsFlashpointPresent(); + bool isFlashpointPresent() const; + + Qx::Bindable bindableIsFlashpointReady(); + bool isFlashpointReady() const; + + Qx::Bindable bindableIsBothTargetsReady(); + bool isBothTargetsReady() const; + + Qx::Bindable bindableIsFlashpointTargetSeries(); + bool isFlashpointTargetSeries() const; + + Qx::Bindable> bindableImageModeOrder(); + const Qx::Bindable> bindableImageModeOrder() const; + QList imageModeOrder() const; + + Qx::Bindable bindableIsImageDownloadable(); + bool isImageDownloadable() const; + + Qx::Bindable bindableLauncherInfo(); + QString launcherInfo() const; + + Qx::Bindable bindableFlashpointInfo(); + QString flashpointInfo() const; + + Qx::Bindable> bindableTagMap(); + const Qx::Bindable> bindableTagMap() const; + QMap tagMap() const; + + Qx::Bindable> bindablePlatforms(); + const Qx::Bindable> bindablePlatforms() const; + QList platforms() const; + + Qx::Bindable> bindablePlaylists(); + const Qx::Bindable> bindablePlaylists() const; + QList playlists() const; +}; + +} + +#endif // IMPORT_PROPERTIES_H diff --git a/app/src/import/settings.h b/app/src/import/settings.h new file mode 100644 index 0000000..40c990c --- /dev/null +++ b/app/src/import/settings.h @@ -0,0 +1,50 @@ +#ifndef IMPORT_SETTINGS_H +#define IMPORT_SETTINGS_H + +// Qt Includes +#include +#include + +// libfp Includes +#include + +namespace Import +{ + +// Enums +enum class Install{ Launcher, Flashpoint }; +enum class UpdateMode {OnlyNew, NewAndExisting}; +enum class ImageMode {Copy, Reference, Link}; +enum class PlaylistGameMode {SelectedPlatform, ForceAll}; + +// Structs +struct Importee +{ + QString name; + bool existing = false; +}; + +struct Selections +{ + QStringList platforms; + QStringList playlists; +}; + +struct UpdateOptions +{ + UpdateMode importMode; + bool removeObsolete; +}; + +struct OptionSet +{ + UpdateOptions updateOptions; + ImageMode imageMode; + bool downloadImages; + PlaylistGameMode playlistMode; + Fp::Db::InclusionOptions inclusionOptions; +}; + +} + +#endif // IMPORT_SETTINGS_H diff --git a/app/src/import-worker.cpp b/app/src/import/worker.cpp similarity index 85% rename from app/src/import-worker.cpp rename to app/src/import/worker.cpp index 8207e55..0829681 100644 --- a/app/src/import-worker.cpp +++ b/app/src/import/worker.cpp @@ -1,5 +1,5 @@ // Unit Include -#include "import-worker.h" +#include "worker.h" // Standard Library Includes #include @@ -12,7 +12,10 @@ #include // Project Includes -#include "clifp.h" +#include "kernel/clifp.h" + +namespace Import +{ //=============================================================================================================== // ImageTransferError @@ -55,16 +58,16 @@ QString ImageTransferError::deriveDetails() const QString ImageTransferError::deriveCaption() const { return CAPTION_IMAGE_ERR; } //=============================================================================================================== -// IMPORT WORKER +// Worker //=============================================================================================================== //-Constructor--------------------------------------------------------------------------------------------------- -ImportWorker::ImportWorker(std::shared_ptr fpInstallForWork, - std::shared_ptr feInstallForWork, - ImportSelections importSelections, +Worker::Worker(std::shared_ptr fpInstallForWork, + std::shared_ptr lrInstallForWork, + Selections importSelections, OptionSet optionSet) : mFlashpointInstall(fpInstallForWork), - mFrontendInstall(feInstallForWork), + mLauncherInstall(lrInstallForWork), mImportSelections(importSelections), mOptionSet(optionSet), mCurrentProgress(0), @@ -73,7 +76,7 @@ ImportWorker::ImportWorker(std::shared_ptr fpInstallForWork, //-Instance Functions-------------------------------------------------------------------------------------------- //Private: -Qx::ProgressGroup* ImportWorker::initializeProgressGroup(const QString& groupName, quint64 weight) +Qx::ProgressGroup* Worker::initializeProgressGroup(const QString& groupName, quint64 weight) { Qx::ProgressGroup* pg = mProgressManager.addGroup(groupName); pg->setWeight(weight); @@ -82,7 +85,7 @@ Qx::ProgressGroup* ImportWorker::initializeProgressGroup(const QString& groupNam return pg; } -Qx::Error ImportWorker::preloadPlaylists(QList& targetPlaylists) +Qx::Error Worker::preloadPlaylists(QList& targetPlaylists) { // Reset playlists targetPlaylists.clear(); @@ -103,7 +106,7 @@ Qx::Error ImportWorker::preloadPlaylists(QList& targetPlaylists) return Qx::Error(); } -QList ImportWorker::getPlaylistSpecificGameIds(const QList& playlists) +QList Worker::getPlaylistSpecificGameIds(const QList& playlists) { QList playlistSpecGameIds; @@ -114,7 +117,7 @@ QList ImportWorker::getPlaylistSpecificGameIds(const QList& return playlistSpecGameIds; } -ImageTransferError ImportWorker::transferImage(bool symlink, QString sourcePath, QString destinationPath) +ImageTransferError Worker::transferImage(bool symlink, QString sourcePath, QString destinationPath) { /* TODO: Ideally the error handlers here don't need to include "Retry?" text and therefore need less use of QString::arg(); however, this largely * would require use of a button labeled "Ignore All" so that the errors could presented as is without a prompt, with the prompt being inferred @@ -156,7 +159,7 @@ ImageTransferError ImportWorker::transferImage(bool symlink, QString sourcePath, return ImageTransferError(ImageTransferError::CantCreateDirectory, QString(), destinationDir.absolutePath()); // Determine backup path - QString backupPath = Fe::Install::filePathToBackupPath(destinationInfo.absoluteFilePath()); + QString backupPath = Lr::Install::filePathToBackupPath(destinationInfo.absoluteFilePath()); // Temporarily backup image if it already exists (also acts as deletion marking in case images for the title were removed in an update) if(destinationOccupied) @@ -178,7 +181,7 @@ ImageTransferError ImportWorker::transferImage(bool symlink, QString sourcePath, else if(QFile::exists(backupPath)) QFile::remove(backupPath); else - mFrontendInstall->addRevertableFile(destinationPath); // Only queue image to be removed on failure if its new, so existing images aren't deleted on revert + mLauncherInstall->addRevertableFile(destinationPath); // Only queue image to be removed on failure if its new, so existing images aren't deleted on revert } else { @@ -190,21 +193,21 @@ ImageTransferError ImportWorker::transferImage(bool symlink, QString sourcePath, else if(QFile::exists(backupPath)) QFile::remove(backupPath); else - mFrontendInstall->addRevertableFile(destinationPath); // Only queue image to be removed on failure if its new, so existing images aren't deleted on revert + mLauncherInstall->addRevertableFile(destinationPath); // Only queue image to be removed on failure if its new, so existing images aren't deleted on revert } // Return null error on success return ImageTransferError(); } -bool ImportWorker::performImageJobs(const QList& jobs, bool symlink, Qx::ProgressGroup* pg) +bool Worker::performImageJobs(const QList& jobs, bool symlink, Qx::ProgressGroup* pg) { // Setup for image transfers ImageTransferError imageTransferError; // Error return reference *mBlockingErrorResponse = QMessageBox::NoToAll; // Default to choice "NoToAll" in case the signal is not correctly connected using Qt::BlockingQueuedConnection bool ignoreAllTransferErrors = false; // NoToAll response tracker - for(const Fe::Install::ImageMap& imageJob : jobs) + for(const Lr::Install::ImageMap& imageJob : jobs) { while((imageTransferError = transferImage(symlink, imageJob.sourcePath, imageJob.destPath)).isValid() && !ignoreAllTransferErrors) { @@ -229,7 +232,7 @@ bool ImportWorker::performImageJobs(const QList& jobs, bo return true; } -ImportWorker::ImportResult ImportWorker::processPlatformGames(Qx::Error& errorReport, std::unique_ptr& platformDoc, Fp::Db::QueryBuffer& gameQueryResult) +Worker::Result Worker::processPlatformGames(Qx::Error& errorReport, std::unique_ptr& platformDoc, Fp::Db::QueryBuffer& gameQueryResult) { const Fp::Toolkit* tk = mFlashpointInstall->toolkit(); @@ -280,7 +283,7 @@ ImportWorker::ImportResult ImportWorker::processPlatformGames(Qx::Error& errorRe // Add set to doc QString checkedLogoPath = (logoLocalInfo.exists() || mOptionSet.downloadImages) ? logoLocalInfo.absoluteFilePath() : QString(); QString checkedScreenshotPath = (ssLocalInfo.exists() || mOptionSet.downloadImages) ? ssLocalInfo.absoluteFilePath() : QString(); - platformDoc->addSet(builtSet, Fe::ImageSources(checkedLogoPath, checkedScreenshotPath)); + platformDoc->addSet(builtSet, Lr::ImageSources(checkedLogoPath, checkedScreenshotPath)); // Add ID to imported game cache mImportedGameIdsCache.insert(builtGame.id()); @@ -306,7 +309,7 @@ ImportWorker::ImportResult ImportWorker::processPlatformGames(Qx::Error& errorRe } // Handle image transfer progress - if(mOptionSet.imageMode == Fe::ImageMode::Copy || mOptionSet.imageMode == Fe::ImageMode::Link) + if(mOptionSet.imageMode == ImageMode::Copy || mOptionSet.imageMode == ImageMode::Link) { // Adjust progress if images aren't available if(checkedLogoPath.isEmpty()) @@ -330,7 +333,7 @@ ImportWorker::ImportResult ImportWorker::processPlatformGames(Qx::Error& errorRe return Successful; } -void ImportWorker::cullUnimportedPlaylistGames(QList& playlists) +void Worker::cullUnimportedPlaylistGames(QList& playlists) { const auto& idCache = mImportedGameIdsCache; for(auto& pl : playlists) @@ -341,7 +344,7 @@ void ImportWorker::cullUnimportedPlaylistGames(QList& playlists) } } -ImportWorker::ImportResult ImportWorker::preloadAddApps(Qx::Error& errorReport, Fp::Db::QueryBuffer& addAppQuery) +Worker::Result Worker::preloadAddApps(Qx::Error& errorReport, Fp::Db::QueryBuffer& addAppQuery) { mAddAppsCache.reserve(addAppQuery.size); for(int i = 0; i < addAppQuery.size; i++) @@ -380,25 +383,25 @@ ImportWorker::ImportResult ImportWorker::preloadAddApps(Qx::Error& errorReport, return Successful; } -ImportWorker::ImportResult ImportWorker::processGames(Qx::Error& errorReport, QList& primary, QList& playlistSpecific) +Worker::Result Worker::processGames(Qx::Error& errorReport, QList& primary, QList& playlistSpecific) { // Status tracking - ImportResult platformImportStatus; + Result platformImportStatus; // Track total platforms that have been handled qsizetype remainingPlatforms = primary.size() + playlistSpecific.size(); // Use lambda to handle both lists due to major overlap - auto platformsHandler = [&remainingPlatforms, &errorReport, this](QList& platformQueryResults, QString label) -> ImportResult { - ImportResult result; + auto platformsHandler = [&remainingPlatforms, &errorReport, this](QList& platformQueryResults, QString label) -> Result { + Result result; for(int i = 0; i < platformQueryResults.size(); i++) { Fp::Db::QueryBuffer& currentQueryResult = platformQueryResults[i]; - // Open frontend platform doc - std::unique_ptr currentPlatformDoc; - Fe::DocHandlingError platformReadError = mFrontendInstall->checkoutPlatformDoc(currentPlatformDoc, currentQueryResult.source); + // Open launcher platform doc + std::unique_ptr currentPlatformDoc; + Lr::DocHandlingError platformReadError = mLauncherInstall->checkoutPlatformDoc(currentPlatformDoc, currentQueryResult.source); // Stop import if error occurred if(platformReadError.isValid()) @@ -424,8 +427,8 @@ ImportWorker::ImportResult ImportWorker::processGames(Qx::Error& errorReport, QL } // Forfeit document lease and save it - Fe::DocHandlingError saveError; - if((saveError = mFrontendInstall->commitPlatformDoc(std::move(currentPlatformDoc))).isValid()) + Lr::DocHandlingError saveError; + if((saveError = mLauncherInstall->commitPlatformDoc(std::move(currentPlatformDoc))).isValid()) { errorReport = saveError; return Failed; @@ -453,16 +456,16 @@ ImportWorker::ImportResult ImportWorker::processGames(Qx::Error& errorReport, QL return Successful; } -ImportWorker::ImportResult ImportWorker::processPlaylists(Qx::Error& errorReport, const QList& playlists) +Worker::Result Worker::processPlaylists(Qx::Error& errorReport, const QList& playlists) { for(const auto& currentPlaylist : playlists) { // Update progress dialog label emit progressStepChanged(STEP_IMPORTING_PLAYLISTS.arg(currentPlaylist.title())); - // Open frontend playlist doc - std::unique_ptr currentPlaylistDoc; - Fe::DocHandlingError playlistReadError = mFrontendInstall->checkoutPlaylistDoc(currentPlaylistDoc, currentPlaylist.title()); + // Open launcher playlist doc + std::unique_ptr currentPlaylistDoc; + Lr::DocHandlingError playlistReadError = mLauncherInstall->checkoutPlaylistDoc(currentPlaylistDoc, currentPlaylist.title()); // Stop import if error occurred if(playlistReadError.isValid()) @@ -485,8 +488,8 @@ ImportWorker::ImportResult ImportWorker::processPlaylists(Qx::Error& errorReport } // Forfeit document lease and save it - Fe::DocHandlingError saveError; - if((saveError = mFrontendInstall->commitPlaylistDoc(std::move(currentPlaylistDoc))).isValid()) + Lr::DocHandlingError saveError; + if((saveError = mLauncherInstall->commitPlaylistDoc(std::move(currentPlaylistDoc))).isValid()) { errorReport = saveError; return Failed; @@ -507,7 +510,7 @@ ImportWorker::ImportResult ImportWorker::processPlaylists(Qx::Error& errorReport return Successful; } -ImportWorker::ImportResult ImportWorker::processImages(Qx::Error& errorReport) +Worker::Result Worker::processImages(Qx::Error& errorReport) { //-Image Download--------------------------------------------------------------------------------- if(mOptionSet.downloadImages && mImageDownloadManager.hasTasks()) @@ -528,8 +531,8 @@ ImportWorker::ImportResult ImportWorker::processImages(Qx::Error& errorReport) *ignore = *mBlockingErrorResponse == QMessageBox::Yes; }); - connect(&mImageDownloadManager, &Qx::SyncDownloadManager::authenticationRequired, this, &ImportWorker::authenticationRequired); - connect(&mImageDownloadManager, &Qx::SyncDownloadManager::proxyAuthenticationRequired, this, &ImportWorker::authenticationRequired); + connect(&mImageDownloadManager, &Qx::SyncDownloadManager::authenticationRequired, this, &Worker::authenticationRequired); + connect(&mImageDownloadManager, &Qx::SyncDownloadManager::proxyAuthenticationRequired, this, &Worker::authenticationRequired); connect(&mImageDownloadManager, &Qx::SyncDownloadManager::downloadFinished, this, [this]() { // clazy:exclude=lambda-in-connect mProgressManager.group(Pg::ImageDownload)->incrementValue(); @@ -558,16 +561,16 @@ ImportWorker::ImportResult ImportWorker::processImages(Qx::Error& errorReport) // Update progress dialog label emit progressStepChanged(STEP_IMPORTING_IMAGES); - // Provide frontend with bulk reference locations and acquire any transfer tasks - QList imageTransferJobs; - Fe::ImageSources bulkSources; - if(mOptionSet.imageMode == Fe::ImageMode::Reference) + // Provide launcher with bulk reference locations and acquire any transfer tasks + QList imageTransferJobs; + Lr::ImageSources bulkSources; + if(mOptionSet.imageMode == ImageMode::Reference) { bulkSources.setLogoPath(QDir::toNativeSeparators(mFlashpointInstall->entryLogosDirectory().absolutePath())); bulkSources.setScreenshotPath(QDir::toNativeSeparators(mFlashpointInstall->entryScreenshotsDirectory().absolutePath())); } - Qx::Error imageExchangeError = mFrontendInstall->preImageProcessing(imageTransferJobs, bulkSources); + Qx::Error imageExchangeError = mLauncherInstall->preImageProcessing(imageTransferJobs, bulkSources); if(imageExchangeError.isValid()) { @@ -577,7 +580,7 @@ ImportWorker::ImportResult ImportWorker::processImages(Qx::Error& errorReport) } // Perform transfers if required - if(mOptionSet.imageMode == Fe::ImageMode::Copy || mOptionSet.imageMode == Fe::ImageMode::Link) + if(mOptionSet.imageMode == ImageMode::Copy || mOptionSet.imageMode == ImageMode::Link) { /* * Account for potential mismatch between assumed and actual job count. @@ -587,32 +590,32 @@ ImportWorker::ImportResult ImportWorker::processImages(Qx::Error& errorReport) if(static_cast(imageTransferJobs.size()) != mProgressManager.group(Pg::ImageTransfer)->maximum()) mProgressManager.group(Pg::ImageTransfer)->setMaximum(imageTransferJobs.size()); - if(!performImageJobs(imageTransferJobs, mOptionSet.imageMode == Fe::ImageMode::Link, mProgressManager.group(Pg::ImageTransfer))) + if(!performImageJobs(imageTransferJobs, mOptionSet.imageMode == ImageMode::Link, mProgressManager.group(Pg::ImageTransfer))) return Canceled; } else if(!imageTransferJobs.isEmpty()) - qWarning("the frontend provided image transfers when the mode wasn't link/copy"); + qWarning("the launcher provided image transfers when the mode wasn't link/copy"); - // Handle frontend specific actions - mFrontendInstall->postImageProcessing(); + // Handle launcher specific actions + mLauncherInstall->postImageProcessing(); // Report successful step completion errorReport = Qx::Error(); return Successful; } -ImportWorker::ImportResult ImportWorker::processIcons(Qx::Error& errorReport, const QStringList& platforms, const QList& playlists) +Worker::Result Worker::processIcons(Qx::Error& errorReport, const QStringList& platforms, const QList& playlists) { - QList jobs; - QString mainDest = mFrontendInstall->platformCategoryIconPath(); - std::optional platformDestDir = mFrontendInstall->platformIconsDirectory(); - std::optional playlistDestDir = mFrontendInstall->playlistIconsDirectory(); + QList jobs; + QString mainDest = mLauncherInstall->platformCategoryIconPath(); + std::optional platformDestDir = mLauncherInstall->platformIconsDirectory(); + std::optional playlistDestDir = mLauncherInstall->playlistIconsDirectory(); const Fp::Toolkit* tk = mFlashpointInstall->toolkit(); // Main Job if(!mainDest.isEmpty()) - jobs.emplace_back(Fe::Install::ImageMap{.sourcePath = u":/flashpoint/icon.png"_s, .destPath = mainDest}); + jobs.emplace_back(Lr::Install::ImageMap{.sourcePath = u":/flashpoint/icon.png"_s, .destPath = mainDest}); // Platform jobs if(platformDestDir) @@ -622,7 +625,7 @@ ImportWorker::ImportResult ImportWorker::processIcons(Qx::Error& errorReport, co { QString src = tk->platformLogoPath(p); if(QFile::exists(src)) - jobs.emplace_back(Fe::Install::ImageMap{.sourcePath = src, + jobs.emplace_back(Lr::Install::ImageMap{.sourcePath = src, .destPath = pdd.absoluteFilePath(p + ".png")}); } } @@ -650,19 +653,19 @@ ImportWorker::ImportResult ImportWorker::processIcons(Qx::Error& errorReport, co continue; /* NOTE: This is LaunchBox specific since it's currently the only FE to support icons. If this changes a general solution is needed - * Like allowing the frontend to filter out specific icons + * Like allowing the launcher to filter out specific icons * * Don't copy the favorites icon as LB already has its own. */ if(p.title().trimmed() == u"Favorites"_s) continue; - /* NOTE: This may not work for all frontends + /* NOTE: This may not work for all launchers * - * Use translated name for destination since that's what the frontend is expecting + * Use translated name for destination since that's what the launcher is expecting */ QString sFilename = p.title() + ".png"; - QString dFilename = mFrontendInstall->translateDocName(p.title(), Fe::DataDoc::Type::Playlist) + ".png";; + QString dFilename = mLauncherInstall->translateDocName(p.title(), Lr::DataDoc::Type::Playlist) + ".png";; QString source = iconInflateDir.filePath(sFilename); QString dest = pdd.absoluteFilePath(dFilename); @@ -673,7 +676,7 @@ ImportWorker::ImportResult ImportWorker::processIcons(Qx::Error& errorReport, co return Failed; } - jobs.emplace_back(Fe::Install::ImageMap{.sourcePath = source, .destPath = dest}); + jobs.emplace_back(Lr::Install::ImageMap{.sourcePath = source, .destPath = dest}); } } @@ -691,12 +694,12 @@ ImportWorker::ImportResult ImportWorker::processIcons(Qx::Error& errorReport, co } //Public -ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) +Worker::Result Worker::doImport(Qx::Error& errorReport) { //-Setup---------------------------------------------------------------- // Import step status - ImportResult importStepStatus; + Result importStepStatus; // Process query status Fp::DbError queryError; @@ -798,7 +801,7 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) } // Screenshot and Logo transfer - if(mOptionSet.imageMode != Fe::ImageMode::Reference) + if(mOptionSet.imageMode != ImageMode::Reference) { Qx::ProgressGroup* pgImageTransfer = initializeProgressGroup(Pg::ImageTransfer, 3); pgImageTransfer->setMaximum(totalGameCount * 2); @@ -807,11 +810,11 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) // Icon transfers // TOD: Somewhat wasteful because these create a temporary copy, but not a big deal for now quint64 iconCount = 0; - if(!mFrontendInstall->platformCategoryIconPath().isEmpty()) + if(!mLauncherInstall->platformCategoryIconPath().isEmpty()) iconCount++; - if(mFrontendInstall->platformIconsDirectory()) + if(mLauncherInstall->platformIconsDirectory()) iconCount += involvedPlatforms.size(); - if(mFrontendInstall->playlistIconsDirectory()) + if(mLauncherInstall->playlistIconsDirectory()) iconCount += targetPlaylists.size(); if(iconCount > 0) @@ -828,10 +831,10 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) } // Connect progress manager signal - connect(&mProgressManager, &Qx::GroupedProgressManager::progressUpdated, this, &ImportWorker::pmProgressUpdated); + connect(&mProgressManager, &Qx::GroupedProgressManager::progressUpdated, this, &Worker::pmProgressUpdated); - //-Handle Frontend Specific Import Setup------------------------------ - Fe::Install::ImportDetails details{ + //-Handle Launcher Specific Import Setup------------------------------ + Lr::Install::ImportDetails details{ .updateOptions = mOptionSet.updateOptions, .imageMode = mOptionSet.imageMode, .clifpPath = CLIFp::standardCLIFpPath(*mFlashpointInstall), @@ -839,7 +842,7 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) .involvedPlaylists = mImportSelections.playlists }; - errorReport = mFrontendInstall->preImport(details); + errorReport = mLauncherInstall->preImport(details); if(errorReport.isValid()) return Failed; @@ -853,8 +856,8 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) if((importStepStatus = preloadAddApps(errorReport, addAppQuery)) != Successful) return importStepStatus; - // Handle Frontend specific pre-platform tasks - errorReport = mFrontendInstall->prePlatformsImport(); + // Handle Launcher specific pre-platform tasks + errorReport = mLauncherInstall->prePlatformsImport(); if(errorReport.isValid()) return Failed; @@ -862,8 +865,8 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) if((importStepStatus = processGames(errorReport, gameQueries, playlistSpecGameQueries)) != Successful) return importStepStatus; - // Handle Frontend specific post-platform tasks - errorReport = mFrontendInstall->postPlatformsImport(); + // Handle Launcher specific post-platform tasks + errorReport = mLauncherInstall->postPlatformsImport(); if(errorReport.isValid()) return Failed; @@ -881,23 +884,23 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) // Remove un-imported games from playlists cullUnimportedPlaylistGames(targetPlaylists); - // Handle Frontend specific pre-playlist tasks - errorReport = mFrontendInstall->prePlaylistsImport(); + // Handle Launcher specific pre-playlist tasks + errorReport = mLauncherInstall->prePlaylistsImport(); if(errorReport.isValid()) return Failed; if((importStepStatus = processPlaylists(errorReport, targetPlaylists)) != Successful) return importStepStatus; - // Handle Frontend specific pre-playlist tasks - errorReport = mFrontendInstall->postPlaylistsImport(); + // Handle Launcher specific pre-playlist tasks + errorReport = mLauncherInstall->postPlaylistsImport(); if(errorReport.isValid()) return Failed; } - // Handle Frontend specific cleanup + // Handle Launcher specific cleanup emit progressStepChanged(STEP_FINALIZING); - errorReport = mFrontendInstall->postImport(); + errorReport = mLauncherInstall->postImport(); if(errorReport.isValid()) return Failed; @@ -909,7 +912,7 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) } // Reset install - mFrontendInstall->softReset(); + mLauncherInstall->softReset(); // Report successful import completion errorReport = Qx::Error(); @@ -918,7 +921,7 @@ ImportWorker::ImportResult ImportWorker::doImport(Qx::Error& errorReport) //-Slots--------------------------------------------------------------------------------------------------------- //Private Slots: -void ImportWorker::pmProgressUpdated(quint64 currentProgress) +void Worker::pmProgressUpdated(quint64 currentProgress) { /* NOTE: This is required because if the value isn't actually different than the current when * the connected QProgressDialog::setValue() is triggered then processEvents() won't be called. @@ -940,4 +943,6 @@ void ImportWorker::pmProgressUpdated(quint64 currentProgress) } //Public Slots: -void ImportWorker::notifyCanceled() { mCanceled = true; } +void Worker::notifyCanceled() { mCanceled = true; } + +} diff --git a/app/src/import-worker.h b/app/src/import/worker.h similarity index 76% rename from app/src/import-worker.h rename to app/src/import/worker.h index 7f0a9a9..57182a2 100644 --- a/app/src/import-worker.h +++ b/app/src/import/worker.h @@ -1,5 +1,5 @@ -#ifndef COREIMPORTWORKER_H -#define COREIMPORTWORKER_H +#ifndef IMPORT_WORKER_H +#define IMPORT_WORKER_H // Qt Includes #include @@ -13,7 +13,10 @@ #include // Project Includes -#include "frontend/fe-install.h" +#include "launcher/lr-install.h" + +namespace Import +{ class QX_ERROR_TYPE(ImageTransferError, "ImageTransferError", 1351) { @@ -69,14 +72,13 @@ class QX_ERROR_TYPE(ImageTransferError, "ImageTransferError", 1351) QString deriveCaption() const override; }; -class ImportWorker : public QObject +class Worker : public QObject { Q_OBJECT // Required for classes that use Qt elements //-Class Enums--------------------------------------------------------------------------------------------------- public: - enum ImportResult {Failed, Canceled, Taskless, Successful}; - enum PlaylistGameMode {SelectedPlatform, ForceAll}; + enum Result {Failed, Canceled, Taskless, Successful}; //-Inner Classes------------------------------------------------------------------------------------------------ private: @@ -91,23 +93,6 @@ class ImportWorker : public QObject static inline const QString PlaylistImport = u"PlaylistImport"_s; }; -//-Class Structs------------------------------------------------------------------------------------------------- -public: - struct ImportSelections - { - QStringList platforms; - QStringList playlists; - }; - - struct OptionSet - { - Fe::UpdateOptions updateOptions; - Fe::ImageMode imageMode; - bool downloadImages; - PlaylistGameMode playlistMode; - Fp::Db::InclusionOptions inclusionOptions; - }; - //-Class Variables----------------------------------------------------------------------------------------------- public: // Import Steps @@ -123,13 +108,13 @@ class ImportWorker : public QObject private: // Install links std::shared_ptr mFlashpointInstall; - std::shared_ptr mFrontendInstall; + std::shared_ptr mLauncherInstall; // Image processing Qx::SyncDownloadManager mImageDownloadManager; // Job details - ImportSelections mImportSelections; + Selections mImportSelections; OptionSet mOptionSet; // Job Caches @@ -148,9 +133,9 @@ class ImportWorker : public QObject //-Constructor--------------------------------------------------------------------------------------------------- public: - ImportWorker(std::shared_ptr fpInstallForWork, - std::shared_ptr feInstallForWork, - ImportSelections importSelections, + Worker(std::shared_ptr fpInstallForWork, + std::shared_ptr feInstallForWork, + Selections importSelections, OptionSet optionSet); //-Instance Functions--------------------------------------------------------------------------------------------------------- @@ -159,18 +144,18 @@ class ImportWorker : public QObject Qx::Error preloadPlaylists(QList& targetPlaylists); QList getPlaylistSpecificGameIds(const QList& playlists); ImageTransferError transferImage(bool symlink, QString sourcePath, QString destPath); - bool performImageJobs(const QList& jobs, bool symlink, Qx::ProgressGroup* pg = nullptr); - ImportResult processPlatformGames(Qx::Error& errorReport, std::unique_ptr& platformDoc, Fp::Db::QueryBuffer& gameQueryResult); + bool performImageJobs(const QList& jobs, bool symlink, Qx::ProgressGroup* pg = nullptr); + Result processPlatformGames(Qx::Error& errorReport, std::unique_ptr& platformDoc, Fp::Db::QueryBuffer& gameQueryResult); void cullUnimportedPlaylistGames(QList& playlists); - ImportResult preloadAddApps(Qx::Error& errorReport, Fp::Db::QueryBuffer& addAppQuery); - ImportResult processGames(Qx::Error& errorReport, QList& primary, QList& playlistSpecific); - ImportResult processPlaylists(Qx::Error& errorReport, const QList& playlists); - ImportResult processImages(Qx::Error& errorReport); - ImportResult processIcons(Qx::Error& errorReport, const QStringList& platforms, const QList& playlists); + Result preloadAddApps(Qx::Error& errorReport, Fp::Db::QueryBuffer& addAppQuery); + Result processGames(Qx::Error& errorReport, QList& primary, QList& playlistSpecific); + Result processPlaylists(Qx::Error& errorReport, const QList& playlists); + Result processImages(Qx::Error& errorReport); + Result processIcons(Qx::Error& errorReport, const QStringList& platforms, const QList& playlists); public: - ImportResult doImport(Qx::Error& errorReport); + Result doImport(Qx::Error& errorReport); //-Slots---------------------------------------------------------------------------------------------------------- private slots: @@ -191,10 +176,12 @@ public slots: void authenticationRequired(const QString& prompt, QAuthenticator* authenticator); // Finished - void importCompleted(ImportWorker::ImportResult importResult, const Qx::Error& errorReport); + void importCompleted(Worker::Result importResult, const Qx::Error& errorReport); }; +} + //-Metatype declarations------------------------------------------------------------------------------------------- -Q_DECLARE_METATYPE(ImportWorker::ImportResult); +Q_DECLARE_METATYPE(Import::Worker::Result); -#endif // COREIMPORTWORKER_H +#endif // IMPORT_WORKER_H diff --git a/app/src/clifp.cpp b/app/src/kernel/clifp.cpp similarity index 100% rename from app/src/clifp.cpp rename to app/src/kernel/clifp.cpp diff --git a/app/src/clifp.h b/app/src/kernel/clifp.h similarity index 100% rename from app/src/clifp.h rename to app/src/kernel/clifp.h diff --git a/app/src/kernel/controller.cpp b/app/src/kernel/controller.cpp new file mode 100644 index 0000000..f435b79 --- /dev/null +++ b/app/src/kernel/controller.cpp @@ -0,0 +1,418 @@ +// Unit Include +#include "controller.h" + +// Qt Includes +#include +#include + +// Qx Includes +#include +#include +#include + +// libfp Includes +#include + +// Project Includes +#include "launcher/lr-install.h" + +/* TODO: Consider having this tool deploy a .ini file (or the like) into the target launcher install + * (with the exact location probably being guided by the specific Install child) that saves the settings + * used for the import, so that they can be loaded again when that install is targeted by future versions + * of the tool. Would have to account for an initial import vs update (likely just leaving the update settings + * blank). Wouldn't be a huge difference but could be a nice little time saver. + */ + +//=============================================================================================================== +// Controller +//=============================================================================================================== + +//-Constructor------------------------------------------------------------- +//Public: +Controller::Controller() : + mMainWindow(mImportProperties), // Shared by reference, so OK that it's not setup yet + mProgressPresenter(&mMainWindow) +{ + QApplication::setApplicationName(PROJECT_FULL_NAME); + + /*Register metatypes + * NOTE: Qt docs note these should be needed, as always, but since Qt6 signals/slots with these types seem to + * work fine without the following calls. + * See https://forum.qt.io/topic/136627/undocumented-automatic-metatype-registration-in-qt6 + */ + //qRegisterMetaType(); + //qRegisterMetaType(); + //qRegisterMetaType>(); + + // Ensure built-in CLIFp version is valid + if(CLIFp::internalVersion().isNull()) + { + QMessageBox::critical(&mMainWindow, CAPTION_GENERAL_FATAL_ERROR, MSG_FATAL_NO_INTERNAL_CLIFP_VER); + QApplication::exit(1); + return; + } + + // Check if Flashpoint is running + if(Qx::processIsRunning(Fp::Install::LAUNCHER_NAME)) + QMessageBox::warning(&mMainWindow, QApplication::applicationName(), MSG_FP_CLOSE_PROMPT); + + // Setup properties + mImportProperties.setHasLinkPermissions(testForLinkPermissions()); + mImportProperties.bindableIsLauncherPresent().setBinding([this]{ return *mLauncher != nullptr; }); + mImportProperties.bindableIsLauncherReady().setBinding([this]{ return mImportProperties.isLauncherPresent() && mLauncher->isValid() && !mLauncherError; }); + mImportProperties.bindableIsFlashpointPresent().setBinding([this]{ return *mFlashpoint != nullptr; }); + mImportProperties.bindableIsFlashpointReady().setBinding([this]{ return mImportProperties.isFlashpointPresent() && mFlashpoint->isValid(); }); + mImportProperties.bindableIsBothTargetsReady().setBinding([this]{ return mImportProperties.isLauncherReady() && mImportProperties.isFlashpointPresent(); }); + mImportProperties.bindableIsBothTargetsReady().lifetimeOnValueChanged([this]{ if(mImportProperties.isBothTargetsReady()) gatherTargetData(); }); + mImportProperties.bindableIsFlashpointTargetSeries().setBinding([this]{ return mImportProperties.isFlashpointReady() && installMatchesTargetSeries(*mFlashpoint.value()); }); + mImportProperties.bindableImageModeOrder().setBinding([this]{ + /* Even though technically we only need the launcher, check for both installs to prevent the selection + * from moving until its section is available + */ + static QList defOrder{Import::ImageMode::Link, Import::ImageMode::Reference, Import::ImageMode::Copy}; + bool def = !mImportProperties.isBothTargetsReady(); + auto order = def ? defOrder : mLauncher->preferredImageModeOrder(); + if(!mImportProperties.hasLinkPermissions()) + order.removeAll(Import::ImageMode::Link); + + return order; + }); + mImportProperties.bindableIsImageDownloadable().setBinding([this]{ + return mImportProperties.isFlashpointReady() && mFlashpoint->preferences().onDemandImages; + } ); + mImportProperties.bindableLauncherInfo().setBinding([this]{ return mImportProperties.isLauncherReady() ? mLauncher->name() + ' ' + mLauncher->versionString() : QString(); }); + mImportProperties.bindableLauncherInfo().setBinding([this]{ return mImportProperties.isFlashpointReady() ? mFlashpoint->versionInfo()->fullString() : QString(); }); + mImportProperties.bindableTagMap().setBinding([this]{ return mImportProperties.isFlashpointReady() ? mFlashpoint->database()->tags() : QMap(); }); + + // Connect main window + connect(&mMainWindow, &MainWindow::installPathChanged, this, &Controller::updateInstallPath); + connect(&mMainWindow, &MainWindow::importTriggered, this, &Controller::startImport); + connect(&mMainWindow, &MainWindow::standaloneDeployTriggered, this, &Controller::standaloneCLIFpDeploy); + + // Spawn main window + mMainWindow.show(); + mProgressPresenter.attachWindow(mMainWindow.windowHandle()); // Must be after show() for handle to be valid +} + +//-Class Functions------------------------------------------------------------- +//Private: +bool Controller::testForLinkPermissions() +{ + QTemporaryDir testLinkDir; + if(testLinkDir.isValid()) + { + QFile testLinkTarget(testLinkDir.filePath(u"linktarget.tmp"_s)); + + if(testLinkTarget.open(QIODevice::WriteOnly)) + { + testLinkTarget.close(); + std::error_code symlinkError; + std::filesystem::create_symlink(testLinkTarget.fileName().toStdString(), testLinkDir.filePath(u"testlink.tmp"_s).toStdString(), symlinkError); + + if(!symlinkError) + return true; + } + } + + // Default + return false; +} + +bool Controller::installMatchesTargetSeries(const Fp::Install& fpInstall) +{ + Qx::VersionNumber fpVersion = fpInstall.versionInfo()->version(); + return TARGET_FP_VERSION_PREFIX.isPrefixOf(fpVersion) || + TARGET_FP_VERSION_PREFIX.normalized() == fpVersion; // Accounts for if FP doesn't use a trailing zero for major releases +} + + +//-Instance Functions------------------------------------------------------------- +//Private: +void Controller::gatherTargetData() +{ + // IO Error check instance + Qx::Error existingCheck; + + // Get list of existing platforms and playlists + existingCheck = mLauncher->refreshExistingDocs(); + + // IO Error Check + if(existingCheck.isValid()) + { + Qx::postBlockingError(existingCheck); + mLauncherError = true; + return; + } + + /* We set the platform/playlist properties here instead of using a binding because gatherLauncherData() + * might need to be called in contexts where there is no trivail way to cause the binding to re-evaluate + * without adding a hacky bool property specifically for that purpose. + */ + QList plats; + for(const QString& p : mFlashpoint->database()->platformNames()) + plats.append({.name = p, .existing = mLauncher->containsPlatform(p)}); + + QList plays; + for(const QString& p : mFlashpoint->playlistManager()->playlistTitles()) + plays.append({.name = p, .existing = mLauncher->containsPlaylist(p)}); + + mImportProperties.bindablePlatforms().setValue(plats); //clazy:exclude=writing-to-temporary + mImportProperties.bindablePlaylists().setValue(plays); //clazy:exclude=writing-to-temporary +} + +void Controller::processImportResult(Import::Worker::Result importResult, const Qx::Error& errorReport) +{ + // Reset progress presenter + mProgressPresenter.reset(); + + // Post error report if present + if(errorReport.isValid()) + Qx::postBlockingError(errorReport, QMessageBox::Ok); + + if(importResult == Import::Worker::Successful) + { + deployCLIFp(**mFlashpoint, QMessageBox::Ignore); + + // Post-import message + QMessageBox::information(&mMainWindow, QApplication::applicationName(), MSG_POST_IMPORT); + + // Update selection lists to reflect newly existing platforms + gatherTargetData(); + } + else if(importResult == Import::Worker::Taskless) + { + QMessageBox::warning(&mMainWindow, CAPTION_TASKLESS_IMPORT, MSG_NO_WORK); + } + else if(importResult == Import::Worker::Canceled) + { + QMessageBox::critical(&mMainWindow, CAPTION_REVERT, MSG_USER_CANCELED); + revertAllLauncherChanges(); + } + else if(importResult == Import::Worker::Failed) + { + // Show general next steps message + QMessageBox::warning(&mMainWindow, CAPTION_REVERT, MSG_HAVE_TO_REVERT); + revertAllLauncherChanges(); + } + else + qCritical("unhandled import worker result type."); +} + +void Controller::revertAllLauncherChanges() +{ + // Trackers + bool tempSkip = false; + bool alwaysSkip = false; + Lr::RevertError currentError; + int retryChoice; + + // Progress + mProgressPresenter.setMinimum(0); + mProgressPresenter.setMaximum(mLauncher->revertQueueCount()); + mProgressPresenter.setCaption(CAPTION_REVERT); + while(mLauncher->revertNextChange(currentError, alwaysSkip || tempSkip) != 0) + { + // Check for error + if(!currentError.isValid()) + { + tempSkip = false; + mProgressPresenter.setValue(mProgressPresenter.value() + 1); + } + else + { + retryChoice = Qx::postBlockingError(currentError, QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Abort, QMessageBox::Retry); + + if(retryChoice == QMessageBox::Ignore) + tempSkip = true; + else if(retryChoice == QMessageBox::Abort) + alwaysSkip = true; + } + } + + // Ensure progress dialog is closed + mProgressPresenter.reset(); + + // Reset instance + mLauncher->softReset(); +} + +void Controller::deployCLIFp(const Fp::Install& fp, QMessageBox::Button abandonButton) +{ + bool willDeploy = true; + + // Check for existing CLIFp + if(CLIFp::hasCLIFp(fp)) + { + // Notify user if this will be a downgrade + if(CLIFp::internalVersion() < CLIFp::installedVersion(fp)) + willDeploy = (QMessageBox::warning(&mMainWindow, CAPTION_CLIFP_DOWNGRADE, MSG_FP_CLFIP_WILL_DOWNGRADE, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes); + } + + // Deploy CLIFp if applicable + if(willDeploy) + { + // Deploy exe + QString deployError; + while(!CLIFp::deployCLIFp(deployError, fp)) + if(QMessageBox::critical(&mMainWindow, CAPTION_CLIFP_ERR, MSG_FP_CANT_DEPLOY_CLIFP.arg(deployError), QMessageBox::Retry | abandonButton, QMessageBox::Retry) == abandonButton) + break; + } +} + +//-Signals & Slots------------------------------------------------------------- +//Private Slots: +void Controller::handleBlockingError(std::shared_ptr response, const Qx::Error& blockingError, QMessageBox::StandardButtons choices) +{ + mProgressPresenter.setErrorState(); + + // Post error and get response + int userChoice = Qx::postBlockingError(blockingError, choices); + + // If applicable return selection + if(response) + *response = userChoice; + + mProgressPresenter.resetState(); +} + +void Controller::handleAuthRequest(const QString& prompt, QAuthenticator* authenticator) +{ + Qx::LoginDialog ld; + ld.setPrompt(prompt); + + int choice = ld.exec(); + + if(choice == QDialog::Accepted) + { + authenticator->setUser(ld.username()); + authenticator->setPassword(ld.password()); + } +} + +//Public Slots: +void Controller::updateInstallPath(const QString& installPath, Import::Install type) +{ + QString cleanPath = QDir::cleanPath(installPath); + + using enum Import::Install; + switch(type) + { + case Launcher: + { + // Reset any past operational error state + mLauncherError = false; + std::shared_ptr launcher; + if(cleanPath.isEmpty()) + launcher = nullptr; + else + { + launcher = Lr::Install::acquireMatch(cleanPath); + if(!launcher->isValid()) + QMessageBox::critical(&mMainWindow, QApplication::applicationName(), MSG_LR_INSTALL_INVALID); + } + + mLauncher = launcher; + break; + } + case Flashpoint: + { + std::shared_ptr flashpoint; + if(cleanPath.isEmpty()) + flashpoint = nullptr; + else + { + flashpoint = std::make_shared(cleanPath, true); + if(!flashpoint->isValid()) + Qx::postBlockingError(mFlashpoint->error(), QMessageBox::Ok); + } + + mFlashpoint = flashpoint; // Updates target series property + if(!mImportProperties.isFlashpointTargetSeries()) + QMessageBox::warning(&mMainWindow, QApplication::applicationName(), MSG_FP_VER_NOT_TARGET); + break; + } + } +} + +void Controller::startImport(Import::Selections sel, Import::OptionSet opt, bool mayModify) +{ + // Ensure launcher hasn't changed + bool changed = true; // Assume true for if error occurs + mLauncher->refreshExistingDocs(&changed); + if(changed) + { + QMessageBox::warning(&mMainWindow, QApplication::applicationName(), MSG_INSTALL_CONTENTS_CHANGED); + updateInstallPath(mLauncher->path(), Import::Install::Launcher); // Reprocess launcher to make sure it's the same install + return; + } + + // Warn user if they are changing existing files + if(mayModify) + if(QMessageBox::warning(&mMainWindow, QApplication::applicationName(), MSG_PRE_EXISTING_IMPORT, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Cancel) + return; + + // Warn user if Flashpoint is running + // Check if Flashpoint is running + if(Qx::processIsRunning(Fp::Install::LAUNCHER_NAME)) + QMessageBox::warning(&mMainWindow, QApplication::applicationName(), MSG_FP_CLOSE_PROMPT); + + // Only allow proceeding if launcher isn't running + bool lrRunning; + while((lrRunning = mLauncher->isRunning())) + if(QMessageBox::critical(&mMainWindow, QApplication::applicationName(), MSG_LAUNCHER_CLOSE_PROMPT, QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Retry) == QMessageBox::Cancel) + break; + + if(lrRunning) + return; + + // Start progress presentation + mProgressPresenter.setCaption(CAPTION_IMPORTING); + mProgressPresenter.setMinimum(0); + mProgressPresenter.setMaximum(0); + mProgressPresenter.setValue(0); + mProgressPresenter.setBusyState(); + mProgressPresenter.setLabelText(STEP_FP_DB_INITIAL_QUERY); + QApplication::processEvents(); // Force show progress immediately + + // Setup import worker + Import::Worker importWorker(*mFlashpoint, *mLauncher, sel, opt); + + // Setup blocking error connection + connect(&importWorker, &Import::Worker::blockingErrorOccured, this, &Controller::handleBlockingError); + + // Setup auth handler + connect(&importWorker, &Import::Worker::authenticationRequired, this, &Controller::handleAuthRequest); + + // Create process update connections + connect(&importWorker, &Import::Worker::progressStepChanged, &mProgressPresenter, &ProgressPresenter::setLabelText); + connect(&importWorker, &Import::Worker::progressValueChanged, &mProgressPresenter, &ProgressPresenter::setValue); + connect(&importWorker, &Import::Worker::progressMaximumChanged, &mProgressPresenter, &ProgressPresenter::setMaximum); + connect(&mProgressPresenter, &ProgressPresenter::canceled, &importWorker, &Import::Worker::notifyCanceled); + + // Import error tracker + Qx::Error importError; + + // Start import and forward result to handler + Import::Worker::Result importResult = importWorker.doImport(importError); + processImportResult(importResult, importError); +} + +void Controller::standaloneCLIFpDeploy() +{ + // Browse for install + QString selectedDir = QFileDialog::getExistingDirectory(&mMainWindow, CAPTION_FLASHPOINT_BROWSE, QDir::currentPath()); + + if(!selectedDir.isEmpty()) + { + Fp::Install tempFlashpointInstall(selectedDir); + if(tempFlashpointInstall.isValid()) + { + if(!installMatchesTargetSeries(tempFlashpointInstall)) + QMessageBox::warning(&mMainWindow, QApplication::applicationName(), MSG_FP_VER_NOT_TARGET); + + deployCLIFp(tempFlashpointInstall, QMessageBox::Cancel); + } + else + Qx::postBlockingError(tempFlashpointInstall.error(), QMessageBox::Ok); + } +} diff --git a/app/src/kernel/controller.h b/app/src/kernel/controller.h new file mode 100644 index 0000000..4735a5f --- /dev/null +++ b/app/src/kernel/controller.h @@ -0,0 +1,129 @@ +#ifndef CONTROLLER_H +#define CONTROLLER_H + +// Qt Includes +#include +#include + +// Qx Includes +#include + +// Project Includes +#include "import/properties.h" +#include "import/worker.h" +#include "kernel/clifp.h" +#include "ui/mainwindow.h" +#include "ui/progresspresenter.h" +#include "project_vars.h" + +namespace Lr { class Install; } +namespace Fp { class Install; } + +class Controller : public QObject +{ + Q_OBJECT + Q_OBJECT_BINDABLE_PROPERTY(Controller, std::shared_ptr, mLauncher); + Q_OBJECT_BINDABLE_PROPERTY(Controller, std::shared_ptr, mFlashpoint); + + // This is a bodge for before this is handled as part of the launcher's regular validity + Q_OBJECT_BINDABLE_PROPERTY(Controller, bool, mLauncherError); + +//-Class Variables--------------------------------------------------------------- +private: + // Flashpoint version check + static inline const Qx::VersionNumber TARGET_FP_VERSION_PREFIX = Qx::VersionNumber::fromString(PROJECT_TARGET_FP_VER_PFX_STR); + + // Messages - General + static inline const QString MSG_FATAL_NO_INTERNAL_CLIFP_VER = u"Failed to get version information from the internal copy of CLIFp.exe!\n" + "\n" + "Execution cannot continue."_s; + + // Messages - FP General + static inline const QString MSG_FP_CLOSE_PROMPT = u"It is strongly recommended to close Flashpoint before proceeding as it can severely slow or interfere with the import process"_s; + + // Messages - Input + static inline const QString MSG_LR_INSTALL_INVALID = u"The specified directory either doesn't contain a valid launcher install, or it contains a version that is incompatible with this tool."_s; + static inline const QString MSG_FP_INSTALL_INVALID = u"The specified directory either doesn't contain a valid Flashpoint install, or it contains a version that is incompatible with this tool."_s; + static inline const QString MSG_FP_VER_NOT_TARGET = u"The selected Flashpoint install contains a version of Flashpoint that is different from the target version series (" PROJECT_TARGET_FP_VER_PFX_STR "), but appears to have a compatible structure. " + "You may proceed at your own risk as the tool is not guaranteed to work correctly in this circumstance. Please use a newer version of " PROJECT_SHORT_NAME " if available."_s; + + static inline const QString MSG_INSTALL_CONTENTS_CHANGED = u"The contents of your installs have been changed since the initial scan and therefore must be re-evaluated. You will need to make your selections again."_s; + + // Messages - General import procedure + static inline const QString MSG_PRE_EXISTING_IMPORT = u"One or more existing Platforms/Playlists may be affected by this import. These will be altered even if they did not originate from this program (i.e. if you " + "already happened to have a Platform/Playlist with the same name as one present in Flashpoint).\n" + "\n" + "Are you sure you want to proceed?"_s; + static inline const QString MSG_LAUNCHER_CLOSE_PROMPT = u"The importer has detected that the selected launcher is running. It must be closed in order to continue. If recently closed, wait a few moments before trying to proceed again as it performs significant cleanup in the background."_s; + + // Initial import status + static inline const QString STEP_FP_DB_INITIAL_QUERY = u"Making initial Flashpoint database queries..."_s; + + // Messages - Import Result + static inline const QString MSG_POST_IMPORT = u"The Flashpoint import has completed successfully. Next time you start the launcher it may take longer than usual as it may have to fill in some default fields for the imported Platforms/Playlists.\n" + "\n" + "If you wish to import further selections or update to a newer version of Flashpoint, simply re-run this procedure after pointing it to the desired Flashpoint installation."_s; + static inline const QString MSG_NO_WORK = u"The provided import selections/options resulted in no tasks to perform. Double-check your settings."_s; + static inline const QString MSG_USER_CANCELED = u"Import canceled by user, all changes that occurred during import will now be reverted (other than existing images that were replaced with newer versions)."_s; + static inline const QString MSG_HAVE_TO_REVERT = u"Due to previous unrecoverable errors, all changes that occurred during import will now be reverted (other than existing images that were replaced with newer versions).\n" + "\n" + "Afterwards, check to see if there is a newer version of " PROJECT_SHORT_NAME " and try again using that version. If not ask for help on the relevant forums where this tool was released (see Help).\n" + "\n" + "If you believe this to be due to a bug with this software, please submit an issue to its GitHub page (listed under help)"_s; + + // Messages - FP CLIFp + static inline const QString MSG_FP_CLFIP_WILL_DOWNGRADE = u"The existing version of "_s + CLIFp::EXE_NAME + u" in your Flashpoint install is newer than the version package with this tool.\n" + "\n" + "Replacing it with the packaged Version (downgrade) will likely cause compatibility issues unless you are specifically re-importing after downgrading your Flashpoint install to a previous version.\n" + "\n" + "Do you wish to downgrade "_s + CLIFp::EXE_NAME + u"?"_s; + + static inline const QString MSG_FP_CANT_DEPLOY_CLIFP = u"Failed to deploy "_s + CLIFp::EXE_NAME + u" to the selected Flashpoint install.\n" + "\n" + "%1\n" + "\n" + "If you choose to ignore this you will have to place CLIFp in your Flashpoint install directory manually."_s; + // Dialog captions + static inline const QString CAPTION_GENERAL_FATAL_ERROR = u"Fatal Error!"_s; + static inline const QString CAPTION_TASKLESS_IMPORT = u"Nothing to do"_s; + static inline const QString CAPTION_IMPORTING = u"FP Import"_s; + static inline const QString CAPTION_REVERT = u"Reverting changes..."_s; + static inline const QString CAPTION_FLASHPOINT_BROWSE = u"Select the root directory of your Flashpoint install..."_s; + static inline const QString CAPTION_CLIFP_DOWNGRADE = u"Downgrade CLIFp?"_s; + static inline const QString CAPTION_CLIFP_ERR = u"Error deploying CLIFp"_s; + +//-Instance Variables------------------------------------------------------------- +private: + Import::Properties mImportProperties; + MainWindow mMainWindow; + ProgressPresenter mProgressPresenter; + +//-Constructor------------------------------------------------------------- +public: + Controller(); + +//-Class Functions------------------------------------------------------------- +private: + bool testForLinkPermissions(); + bool installMatchesTargetSeries(const Fp::Install& fpInstall); + +//-Instance Functions------------------------------------------------------------- +private: + void gatherTargetData(); + void processImportResult(Import::Worker::Result importResult, const Qx::Error& errorReport); + void revertAllLauncherChanges(); + void deployCLIFp(const Fp::Install& fp, QMessageBox::Button abandonButton); + +//-Signals & Slots------------------------------------------------------------- +private slots: + // Import Handlers + void handleBlockingError(std::shared_ptr response, const Qx::Error& blockingError, QMessageBox::StandardButtons choices); + void handleAuthRequest(const QString& prompt, QAuthenticator* authenticator); + +public slots: + void updateInstallPath(const QString& installPath, Import::Install type); + void startImport(Import::Selections sel, Import::OptionSet opt, bool mayModify); + void standaloneCLIFpDeploy(); +}; + +#endif // CONTROLLER_H diff --git a/app/src/frontend/attractmode/am-data.cpp b/app/src/launcher/attractmode/am-data.cpp similarity index 93% rename from app/src/frontend/attractmode/am-data.cpp rename to app/src/launcher/attractmode/am-data.cpp index f0f4e68..794d843 100644 --- a/app/src/frontend/attractmode/am-data.cpp +++ b/app/src/launcher/attractmode/am-data.cpp @@ -8,7 +8,7 @@ #include // Project Includes -#include "am-install.h" +#include "launcher/attractmode/am-install.h" namespace Am { @@ -18,8 +18,8 @@ namespace Am //-Constructor-------------------------------------------------------------------------------------------------------- //Protected: -CommonDocReader::CommonDocReader(Fe::DataDoc* targetDoc) : - Fe::DataDoc::Reader(targetDoc), +CommonDocReader::CommonDocReader(Lr::DataDoc* targetDoc) : + Lr::DataDoc::Reader(targetDoc), mStreamReader(targetDoc->path()) {} @@ -39,22 +39,22 @@ QString CommonDocReader::readLineIgnoringComments(qint64 maxlen) } //Public: -Fe::DocHandlingError CommonDocReader::readInto() +Lr::DocHandlingError CommonDocReader::readInto() { // Open file Qx::IoOpReport openError = mStreamReader.openFile(); if(openError.isFailure()) - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocCantOpen, openError.outcomeInfo()); + return Lr::DocHandlingError(*mTargetDocument, Lr::DocHandlingError::DocCantOpen, openError.outcomeInfo()); // Check that doc is valid bool isValid = false; if(!checkDocValidity(isValid)) - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocWriteFailed, mStreamReader.status().outcomeInfo()); + return Lr::DocHandlingError(*mTargetDocument, Lr::DocHandlingError::DocWriteFailed, mStreamReader.status().outcomeInfo()); else if(!isValid) - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocInvalidType); + return Lr::DocHandlingError(*mTargetDocument, Lr::DocHandlingError::DocInvalidType); // Read doc - Fe::DocHandlingError parseError = readTargetDoc(); + Lr::DocHandlingError parseError = readTargetDoc(); // Close file mStreamReader.closeFile(); @@ -63,9 +63,9 @@ Fe::DocHandlingError CommonDocReader::readInto() if(parseError.isValid()) return parseError; else if(mStreamReader.hasError()) - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocWriteFailed, mStreamReader.status().outcomeInfo()); + return Lr::DocHandlingError(*mTargetDocument, Lr::DocHandlingError::DocWriteFailed, mStreamReader.status().outcomeInfo()); else - return Fe::DocHandlingError(); + return Lr::DocHandlingError(); } //=============================================================================================================== @@ -74,19 +74,19 @@ Fe::DocHandlingError CommonDocReader::readInto() //-Constructor-------------------------------------------------------------------------------------------------------- //Protected: -CommonDocWriter::CommonDocWriter(Fe::DataDoc* sourceDoc) : - Fe::DataDoc::Writer(sourceDoc), +CommonDocWriter::CommonDocWriter(Lr::DataDoc* sourceDoc) : + Lr::DataDoc::Writer(sourceDoc), mStreamWriter(sourceDoc->path(), Qx::WriteMode::Truncate) {} //-Instance Functions------------------------------------------------------------------------------------------------- //Public: -Fe::DocHandlingError CommonDocWriter::writeOutOf() +Lr::DocHandlingError CommonDocWriter::writeOutOf() { // Open file Qx::IoOpReport openError = mStreamWriter.openFile(); if(openError.isFailure()) - return Fe::DocHandlingError(*mSourceDocument, Fe::DocHandlingError::DocCantOpen, openError.outcomeInfo()); + return Lr::DocHandlingError(*mSourceDocument, Lr::DocHandlingError::DocCantOpen, openError.outcomeInfo()); // Write doc bool writeSuccess = writeSourceDoc(); @@ -95,8 +95,8 @@ Fe::DocHandlingError CommonDocWriter::writeOutOf() mStreamWriter.closeFile(); // Return outcome - return writeSuccess ? Fe::DocHandlingError() : - Fe::DocHandlingError(*mSourceDocument, Fe::DocHandlingError::DocWriteFailed, mStreamWriter.status().outcomeInfo()); + return writeSuccess ? Lr::DocHandlingError() : + Lr::DocHandlingError(*mSourceDocument, Lr::DocHandlingError::DocWriteFailed, mStreamWriter.status().outcomeInfo()); } //=============================================================================================================== @@ -106,7 +106,7 @@ Fe::DocHandlingError CommonDocWriter::writeOutOf() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: ConfigDoc::ConfigDoc(Install* const parent, const QString& filePath, QString docName) : - Fe::DataDoc(parent, filePath, docName) + Lr::DataDoc(parent, filePath, docName) {} //-Instance Functions-------------------------------------------------------------------------------------------------- @@ -204,7 +204,7 @@ bool ConfigDoc::Writer::writeSourceDoc() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: Taglist::Taglist(Install* const parent, const QString& listPath, QString docName) : - Fe::DataDoc(parent, listPath, docName) + Lr::DataDoc(parent, listPath, docName) {} //-Instance Functions-------------------------------------------------------------------------------------------------- @@ -254,7 +254,7 @@ PlatformTaglist::PlatformTaglist(Install* const parent, const QString& listPath, //-Instance Functions-------------------------------------------------------------------------------------------------- //Public: -Fe::DataDoc::Type PlatformTaglist::type() const { return Fe::DataDoc::Type::Platform; } +Lr::DataDoc::Type PlatformTaglist::type() const { return Lr::DataDoc::Type::Platform; } //=============================================================================================================== // PlaylistTaglist @@ -268,7 +268,7 @@ PlaylistTaglist::PlaylistTaglist(Install* const parent, const QString& listPath, //-Instance Functions-------------------------------------------------------------------------------------------------- //Public: -Fe::DataDoc::Type PlaylistTaglist::type() const { return Fe::DataDoc::Type::Playlist; } +Lr::DataDoc::Type PlaylistTaglist::type() const { return Lr::DataDoc::Type::Playlist; } //=============================================================================================================== // Romlist @@ -276,14 +276,14 @@ Fe::DataDoc::Type PlaylistTaglist::type() const { return Fe::DataDoc::Type::Play //-Constructor-------------------------------------------------------------------------------------------------------- //Public: -Romlist::Romlist(Install* const parent, const QString& listPath, QString docName, const Fe::UpdateOptions& updateOptions, +Romlist::Romlist(Install* const parent, const QString& listPath, QString docName, const Import::UpdateOptions& updateOptions, const DocKey&) : - Fe::UpdateableDoc(parent, listPath, docName, updateOptions) + Lr::UpdateableDoc(parent, listPath, docName, updateOptions) {} //-Instance Functions-------------------------------------------------------------------------------------------------- //Public: -Fe::DataDoc::Type Romlist::type() const { return Fe::DataDoc::Type::Config; } +Lr::DataDoc::Type Romlist::type() const { return Lr::DataDoc::Type::Config; } bool Romlist::isEmpty() const { @@ -295,7 +295,7 @@ const QHash>& Romlist::finalEntries() const { r bool Romlist::containsGame(QUuid gameId) const { return mEntriesExisting.contains(gameId) || mEntriesFinal.contains(gameId); } bool Romlist::containsAddApp(QUuid addAppId) const { return mEntriesExisting.contains(addAppId) || mEntriesFinal.contains(addAppId); } -void Romlist::addSet(const Fp::Set& set, const Fe::ImageSources& images) +void Romlist::addSet(const Fp::Set& set, const Lr::ImageSources& images) { // Convert to romlist entry std::shared_ptr mainRomEntry = std::make_shared(set.game()); @@ -325,7 +325,7 @@ void Romlist::addSet(const Fp::Set& set, const Fe::ImageSources& images) void Romlist::finalize() { finalizeUpdateableItems(mEntriesExisting, mEntriesFinal); - Fe::UpdateableDoc::finalize(); + Lr::UpdateableDoc::finalize(); } //=============================================================================================================== @@ -355,7 +355,7 @@ bool Romlist::Reader::checkDocValidity(bool& isValid) return !mStreamReader.hasError(); } -Fe::DocHandlingError Romlist::Reader::readTargetDoc() +Lr::DocHandlingError Romlist::Reader::readTargetDoc() { // Read all romlist entries while(!mStreamReader.atEnd()) @@ -365,7 +365,7 @@ Fe::DocHandlingError Romlist::Reader::readTargetDoc() } // Only can have stream errors - return Fe::DocHandlingError(); + return Lr::DocHandlingError(); } void Romlist::Reader::parseRomEntry(const QString& rawEntry) @@ -586,7 +586,7 @@ bool BulkOverviewWriter::writeOverview(const QUuid& gameId, const QString& overv //Public: PlatformInterface::PlatformInterface(Install* const parent, const QString& platformTaglistPath, QString platformName, const QDir& overviewDir,const DocKey&) : - Fe::PlatformDoc(parent, {}, platformName, {}), + Lr::PlatformDoc(parent, {}, platformName, {}), mPlatformTaglist(parent, platformTaglistPath, platformName), mOverviewWriter(overviewDir) {} @@ -613,7 +613,7 @@ bool PlatformInterface::containsAddApp(QUuid addAppId) const return static_cast(parent())->mRomlist->containsAddApp(addAppId); }; -void PlatformInterface::addSet(const Fp::Set& set, const Fe::ImageSources& images) +void PlatformInterface::addSet(const Fp::Set& set, const Lr::ImageSources& images) { if(!hasError()) { @@ -633,7 +633,7 @@ void PlatformInterface::addSet(const Fp::Set& set, const Fe::ImageSources& image if(written) parent()->addRevertableFile(mOverviewWriter.currentFilePath()); else - mError = Fe::DocHandlingError(*this, Fe::DocHandlingError::DocWriteFailed, mOverviewWriter.fileErrorString()); + mError = Lr::DocHandlingError(*this, Lr::DocHandlingError::DocWriteFailed, mOverviewWriter.fileErrorString()); } //-Handle add apps------------------------------------------------------- @@ -662,14 +662,14 @@ void PlatformInterface::addSet(const Fp::Set& set, const Fe::ImageSources& image //-Constructor-------------------------------------------------------------------------------------------------------- //Public: PlatformInterface::Writer::Writer(PlatformInterface* sourceDoc) : - Fe::DataDoc::Writer(sourceDoc), - Fe::PlatformDoc::Writer(sourceDoc), + Lr::DataDoc::Writer(sourceDoc), + Lr::PlatformDoc::Writer(sourceDoc), mTaglistWriter(&sourceDoc->mPlatformTaglist) {} //-Instance Functions-------------------------------------------------------------------------------------------------- //Public: -Fe::DocHandlingError PlatformInterface::Writer::writeOutOf() { return mTaglistWriter.writeOutOf(); } +Lr::DocHandlingError PlatformInterface::Writer::writeOutOf() { return mTaglistWriter.writeOutOf(); } //=============================================================================================================== // PlaylistInterface @@ -679,7 +679,7 @@ Fe::DocHandlingError PlatformInterface::Writer::writeOutOf() { return mTaglistWr //Public: PlaylistInterface::PlaylistInterface(Install* const parent, const QString& playlistTaglistPath, QString playlistName, const DocKey&) : - Fe::PlaylistDoc(parent, {}, playlistName, {}), + Lr::PlaylistDoc(parent, {}, playlistName, {}), mPlaylistTaglist(parent, playlistTaglistPath, playlistName) {} @@ -705,14 +705,14 @@ void PlaylistInterface::setPlaylistData(const Fp::Playlist& playlist) //-Constructor-------------------------------------------------------------------------------------------------------- //Public: PlaylistInterface::Writer::Writer(PlaylistInterface* sourceDoc) : - Fe::DataDoc::Writer(sourceDoc), - Fe::PlaylistDoc::Writer(sourceDoc), + Lr::DataDoc::Writer(sourceDoc), + Lr::PlaylistDoc::Writer(sourceDoc), mTaglistWriter(&sourceDoc->mPlaylistTaglist) {} //-Instance Functions-------------------------------------------------------------------------------------------------- //Public: -Fe::DocHandlingError PlaylistInterface::Writer::writeOutOf() { return mTaglistWriter.writeOutOf(); } +Lr::DocHandlingError PlaylistInterface::Writer::writeOutOf() { return mTaglistWriter.writeOutOf(); } //=============================================================================================================== // Emulator @@ -727,7 +727,7 @@ Emulator::Emulator(Install* const parent, const QString& filePath, const DocKey& //-Instance Functions-------------------------------------------------------------------------------------------------- //Public: bool Emulator::isEmpty() const { return false; } // Can have blank fields, but always has field keys -Fe::DataDoc::Type Emulator::type() const { return Fe::DataDoc::Type::Config; } +Lr::DataDoc::Type Emulator::type() const { return Lr::DataDoc::Type::Config; } QString Emulator::executable() const { return mExecutable; } QString Emulator::args() const { return mArgs; } @@ -762,7 +762,7 @@ EmulatorReader::EmulatorReader(Emulator* targetDoc) : //-Instance Functions-------------------------------------------------------------------------------------------------- //Private: -Fe::DocHandlingError EmulatorReader::readTargetDoc() +Lr::DocHandlingError EmulatorReader::readTargetDoc() { while(!mStreamReader.atEnd()) { @@ -774,7 +774,7 @@ Fe::DocHandlingError EmulatorReader::readTargetDoc() } // Only can have stream related errors - return Fe::DocHandlingError(); + return Lr::DocHandlingError(); } void EmulatorReader::parseKeyValue(const QString& key, const QString& value) diff --git a/app/src/frontend/attractmode/am-data.h b/app/src/launcher/attractmode/am-data.h similarity index 93% rename from app/src/frontend/attractmode/am-data.h rename to app/src/launcher/attractmode/am-data.h index be477a9..c891c28 100644 --- a/app/src/frontend/attractmode/am-data.h +++ b/app/src/launcher/attractmode/am-data.h @@ -9,8 +9,8 @@ #include // Project Includes -#include "am-items.h" -#include "frontend/fe-data.h" +#include "launcher/lr-data.h" +#include "launcher/attractmode/am-items.h" namespace Am { @@ -25,7 +25,7 @@ class DocKey DocKey(const DocKey&) = default; }; -class CommonDocReader : public Fe::DataDoc::Reader +class CommonDocReader : public Lr::DataDoc::Reader { //-Instance Variables-------------------------------------------------------------------------------------------------- protected: @@ -33,20 +33,20 @@ class CommonDocReader : public Fe::DataDoc::Reader //-Constructor-------------------------------------------------------------------------------------------------------- protected: - CommonDocReader(Fe::DataDoc* targetDoc); + CommonDocReader(Lr::DataDoc* targetDoc); //-Instance Functions------------------------------------------------------------------------------------------------- protected: bool lineIsComment(const QString& line); QString readLineIgnoringComments(qint64 maxlen = 0); virtual bool checkDocValidity(bool& isValid) = 0; - virtual Fe::DocHandlingError readTargetDoc() = 0; + virtual Lr::DocHandlingError readTargetDoc() = 0; public: - Fe::DocHandlingError readInto() override; + Lr::DocHandlingError readInto() override; }; -class CommonDocWriter : public Fe::DataDoc::Writer +class CommonDocWriter : public Lr::DataDoc::Writer { //-Instance Variables-------------------------------------------------------------------------------------------------- protected: @@ -54,17 +54,17 @@ class CommonDocWriter : public Fe::DataDoc::Writer //-Constructor-------------------------------------------------------------------------------------------------------- protected: - CommonDocWriter(Fe::DataDoc* sourceDoc); + CommonDocWriter(Lr::DataDoc* sourceDoc); //-Instance Functions------------------------------------------------------------------------------------------------- protected: virtual bool writeSourceDoc() = 0; public: - Fe::DocHandlingError writeOutOf() override; + Lr::DocHandlingError writeOutOf() override; }; -class ConfigDoc : public Fe::DataDoc +class ConfigDoc : public Lr::DataDoc { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -117,7 +117,7 @@ class ConfigDoc::Writer : public CommonDocWriter bool writeSourceDoc() override; }; -class Taglist : public Fe::DataDoc +class Taglist : public Lr::DataDoc { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -174,7 +174,7 @@ class PlaylistTaglist : public Taglist Type type() const override; }; -class Romlist : public Fe::UpdateableDoc +class Romlist : public Lr::UpdateableDoc { /* This class looks like it should inherit PlatformDoc, but it isn't truly one in the context of an Am install * since those are represented by tag lists, and if it did there would be the issue that once modified it would @@ -199,7 +199,7 @@ class Romlist : public Fe::UpdateableDoc //-Constructor-------------------------------------------------------------------------------------------------------- public: - explicit Romlist(Install* const parent, const QString& listPath, QString docName, const Fe::UpdateOptions& updateOptions, + explicit Romlist(Install* const parent, const QString& listPath, QString docName, const Import::UpdateOptions& updateOptions, const DocKey&); //-Instance Functions-------------------------------------------------------------------------------------------------- @@ -212,7 +212,7 @@ class Romlist : public Fe::UpdateableDoc bool containsGame(QUuid gameId) const; bool containsAddApp(QUuid addAppId) const; - void addSet(const Fp::Set& set, const Fe::ImageSources& images); + void addSet(const Fp::Set& set, const Lr::ImageSources& images); void finalize() override; }; @@ -227,7 +227,7 @@ class Romlist::Reader : public CommonDocReader private: QHash>& targetDocExistingRomEntries(); bool checkDocValidity(bool& isValid) override; - Fe::DocHandlingError readTargetDoc() override; + Lr::DocHandlingError readTargetDoc() override; void parseRomEntry(const QString& rawEntry); void addFieldToBuilder(RomEntry::Builder& builder, QString field, quint8 index); }; @@ -263,7 +263,7 @@ class BulkOverviewWriter bool writeOverview(const QUuid& gameId, const QString& overview); }; -class PlatformInterface : public Fe::PlatformDoc +class PlatformInterface : public Lr::PlatformDoc { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -289,10 +289,10 @@ class PlatformInterface : public Fe::PlatformDoc bool containsGame(QUuid gameId) const override; bool containsAddApp(QUuid addAppId) const override; - void addSet(const Fp::Set& set, const Fe::ImageSources& images) override; + void addSet(const Fp::Set& set, const Lr::ImageSources& images) override; }; -class PlatformInterface::Writer : public Fe::PlatformDoc::Writer +class PlatformInterface::Writer : public Lr::PlatformDoc::Writer { // Shell for writing the taglist of the interface @@ -306,10 +306,10 @@ class PlatformInterface::Writer : public Fe::PlatformDoc::Writer //-Instance Functions------------------------------------------------------------------------------------------------- public: - Fe::DocHandlingError writeOutOf() override; + Lr::DocHandlingError writeOutOf() override; }; -class PlaylistInterface : public Fe::PlaylistDoc +class PlaylistInterface : public Lr::PlaylistDoc { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -333,7 +333,7 @@ class PlaylistInterface : public Fe::PlaylistDoc void setPlaylistData(const Fp::Playlist& playlist) override; }; -class PlaylistInterface::Writer : public Fe::PlaylistDoc::Writer +class PlaylistInterface::Writer : public Lr::PlaylistDoc::Writer { // Shell for writing the taglist of the interface @@ -347,7 +347,7 @@ class PlaylistInterface::Writer : public Fe::PlaylistDoc::Writer //-Instance Functions------------------------------------------------------------------------------------------------- private: - Fe::DocHandlingError writeOutOf() override; + Lr::DocHandlingError writeOutOf() override; }; class Emulator : public ConfigDoc @@ -437,7 +437,7 @@ class EmulatorReader : public ConfigDoc::Reader //-Instance Functions------------------------------------------------------------------------------------------------- private: - Fe::DocHandlingError readTargetDoc() override; + Lr::DocHandlingError readTargetDoc() override; void parseKeyValue(const QString& key, const QString& value); void parseExecutable(const QString& value); void parseArgs(const QString& value); diff --git a/app/src/frontend/attractmode/am-install.cpp b/app/src/launcher/attractmode/am-install.cpp similarity index 84% rename from app/src/frontend/attractmode/am-install.cpp rename to app/src/launcher/attractmode/am-install.cpp index adc9a82..636175d 100644 --- a/app/src/frontend/attractmode/am-install.cpp +++ b/app/src/launcher/attractmode/am-install.cpp @@ -9,7 +9,7 @@ #include // Project Includes -#include "clifp.h" +#include "kernel/clifp.h" namespace Am { @@ -20,7 +20,7 @@ namespace Am //-Constructor------------------------------------------------------------------------------------------------ //Public: Install::Install(const QString& installPath) : - Fe::Install(installPath), + Lr::Install(installPath), mEmulatorsDirectory(installPath + '/' + EMULATORS_PATH), mRomlistsDirectory(installPath + '/' + ROMLISTS_PATH), mMainConfigFile(installPath + '/' + MAIN_CFG_PATH), @@ -55,7 +55,7 @@ Install::Install(const QString& installPath) : //Private: void Install::nullify() { - Fe::Install::nullify(); + Lr::Install::nullify(); mEmulatorsDirectory = QDir(); mRomlistsDirectory = QDir(); @@ -89,7 +89,7 @@ Qx::Error Install::populateExistingDocs() return existingCheck; for(const QFileInfo& platformFile : qAsConst(existingList)) - catalogueExistingDoc(Fe::DataDoc::Identifier(Fe::DataDoc::Type::Platform, platformFile.baseName())); + catalogueExistingDoc(Lr::DataDoc::Identifier(Lr::DataDoc::Type::Platform, platformFile.baseName())); // Check for playlists existingCheck = Qx::dirContentInfoList(existingList, mFpTagDirectory, {u"[[]Playlist[]] *."_s + TAG_EXT}, @@ -98,27 +98,27 @@ Qx::Error Install::populateExistingDocs() return existingCheck; for(const QFileInfo& playlistFile : qAsConst(existingList)) - catalogueExistingDoc(Fe::DataDoc::Identifier(Fe::DataDoc::Type::Playlist, playlistFile.baseName())); + catalogueExistingDoc(Lr::DataDoc::Identifier(Lr::DataDoc::Type::Playlist, playlistFile.baseName())); // Check for special "Flashpoint" platform (more like a config doc but OK for now) QFileInfo mainRomlistInfo(mFpRomlist); if(mainRomlistInfo.exists()) - catalogueExistingDoc(Fe::DataDoc::Identifier(Fe::DataDoc::Type::Platform, mainRomlistInfo.baseName())); + catalogueExistingDoc(Lr::DataDoc::Identifier(Lr::DataDoc::Type::Platform, mainRomlistInfo.baseName())); } // Check for config docs QFileInfo mainCfgInfo(mMainConfigFile); - catalogueExistingDoc(Fe::DataDoc::Identifier(Fe::DataDoc::Type::Config, mainCfgInfo.baseName())); // Must exist + catalogueExistingDoc(Lr::DataDoc::Identifier(Lr::DataDoc::Type::Config, mainCfgInfo.baseName())); // Must exist QFileInfo emulatorCfgInfo(mEmulatorConfigFile); if(emulatorCfgInfo.exists()) - catalogueExistingDoc(Fe::DataDoc::Identifier(Fe::DataDoc::Type::Config, emulatorCfgInfo.baseName())); + catalogueExistingDoc(Lr::DataDoc::Identifier(Lr::DataDoc::Type::Config, emulatorCfgInfo.baseName())); // Return success return Qx::Error(); } -QString Install::imageDestinationPath(Fp::ImageType imageType, const Fe::Game* game) const +QString Install::imageDestinationPath(Fp::ImageType imageType, const Lr::Game* game) const { return mFpScraperDirectory.absolutePath() + '/' + (imageType == Fp::ImageType::Logo ? LOGO_FOLDER_NAME : SCREENSHOT_FOLDER_NAME) + '/' + @@ -126,7 +126,7 @@ QString Install::imageDestinationPath(Fp::ImageType imageType, const Fe::Game* g '.' + IMAGE_EXT; } -std::shared_ptr Install::preparePlatformDocCheckout(std::unique_ptr& platformDoc, const QString& translatedName) +std::shared_ptr Install::preparePlatformDocCheckout(std::unique_ptr& platformDoc, const QString& translatedName) { // Determine path to the taglist that corresponds with the interface QString taglistPath = mFpTagDirectory.absoluteFilePath(translatedName + u"."_s + TAG_EXT) ; @@ -138,10 +138,10 @@ std::shared_ptr Install::preparePlatformDocCheckout(std platformDoc = std::make_unique(this, taglistPath, translatedName, overviewDir, DocKey{}); // No reading to be done for this interface (tag lists are always overwritten) - return std::shared_ptr(); + return std::shared_ptr(); } -std::shared_ptr Install::preparePlaylistDocCheckout(std::unique_ptr& playlistDoc, const QString& translatedName) +std::shared_ptr Install::preparePlaylistDocCheckout(std::unique_ptr& playlistDoc, const QString& translatedName) { // Determine path to the taglist that corresponds with the interface QString taglistPath = mFpTagDirectory.absoluteFilePath(translatedName + u"."_s + TAG_EXT) ; @@ -150,28 +150,28 @@ std::shared_ptr Install::preparePlaylistDocCheckout(std playlistDoc = std::make_unique(this, taglistPath, translatedName, DocKey{}); // No reading to be done for this interface (tag lists are always overwritten) - return std::shared_ptr(); + return std::shared_ptr(); } -std::shared_ptr Install::preparePlatformDocCommit(const std::unique_ptr& platformDoc) +std::shared_ptr Install::preparePlatformDocCommit(const std::unique_ptr& platformDoc) { // Construct doc writer - std::shared_ptr docWriter = std::make_shared(static_cast(platformDoc.get())); + std::shared_ptr docWriter = std::make_shared(static_cast(platformDoc.get())); // Return writer return docWriter; } -std::shared_ptr Install::preparePlaylistDocCommit(const std::unique_ptr& playlistDoc) +std::shared_ptr Install::preparePlaylistDocCommit(const std::unique_ptr& playlistDoc) { // Construct doc writer - std::shared_ptr docWriter = std::make_shared(static_cast(playlistDoc.get())); + std::shared_ptr docWriter = std::make_shared(static_cast(playlistDoc.get())); // Return writer return docWriter; } -Fe::DocHandlingError Install::checkoutMainConfig(std::unique_ptr& returnBuffer) +Lr::DocHandlingError Install::checkoutMainConfig(std::unique_ptr& returnBuffer) { // Construct unopened document returnBuffer = std::make_unique(this, mMainConfigFile.fileName(), DocKey{}); @@ -180,7 +180,7 @@ Fe::DocHandlingError Install::checkoutMainConfig(std::unique_ptr& std::shared_ptr docReader = std::make_shared(returnBuffer.get()); // Open document - Fe::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); + Lr::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); // Set return null on failure if(readErrorStatus.isValid()) @@ -190,7 +190,7 @@ Fe::DocHandlingError Install::checkoutMainConfig(std::unique_ptr& return readErrorStatus; } -Fe::DocHandlingError Install::checkoutFlashpointRomlist(std::unique_ptr& returnBuffer) +Lr::DocHandlingError Install::checkoutFlashpointRomlist(std::unique_ptr& returnBuffer) { // Construct unopened document returnBuffer = std::make_unique(this, mFpRomlist.fileName(), Fp::NAME, mImportDetails->updateOptions, DocKey{}); @@ -199,7 +199,7 @@ Fe::DocHandlingError Install::checkoutFlashpointRomlist(std::unique_ptr std::shared_ptr docReader = std::make_shared(returnBuffer.get()); // Open document - Fe::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); + Lr::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); // Set return null on failure if(readErrorStatus.isValid()) @@ -209,7 +209,7 @@ Fe::DocHandlingError Install::checkoutFlashpointRomlist(std::unique_ptr return readErrorStatus; } -Fe::DocHandlingError Install::checkoutClifpEmulatorConfig(std::unique_ptr& returnBuffer) +Lr::DocHandlingError Install::checkoutClifpEmulatorConfig(std::unique_ptr& returnBuffer) { // Construct unopened document returnBuffer = std::make_unique(this, mEmulatorConfigFile.fileName(), DocKey{}); @@ -218,7 +218,7 @@ Fe::DocHandlingError Install::checkoutClifpEmulatorConfig(std::unique_ptr docReader = std::make_shared(returnBuffer.get()); // Open document - Fe::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); + Lr::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); // Set return null on failure if(readErrorStatus.isValid()) @@ -228,7 +228,7 @@ Fe::DocHandlingError Install::checkoutClifpEmulatorConfig(std::unique_ptr document) +Lr::DocHandlingError Install::commitMainConfig(std::unique_ptr document) { assert(document->parent() == this); @@ -236,7 +236,7 @@ Fe::DocHandlingError Install::commitMainConfig(std::unique_ptr do std::shared_ptr docWriter = std::make_shared(document.get()); // Write - Fe::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); + Lr::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); // Ensure document is cleared document.reset(); @@ -245,7 +245,7 @@ Fe::DocHandlingError Install::commitMainConfig(std::unique_ptr do return writeErrorStatus; } -Fe::DocHandlingError Install::commitFlashpointRomlist(std::unique_ptr document) +Lr::DocHandlingError Install::commitFlashpointRomlist(std::unique_ptr document) { assert(document->parent() == this); @@ -253,7 +253,7 @@ Fe::DocHandlingError Install::commitFlashpointRomlist(std::unique_ptr d std::shared_ptr docWriter = std::make_shared(document.get()); // Write - Fe::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); + Lr::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); // Ensure document is cleared document.reset(); @@ -263,7 +263,7 @@ Fe::DocHandlingError Install::commitFlashpointRomlist(std::unique_ptr d } -Fe::DocHandlingError Install::commitClifpEmulatorConfig(std::unique_ptr document) +Lr::DocHandlingError Install::commitClifpEmulatorConfig(std::unique_ptr document) { assert(document->parent() == this); @@ -271,7 +271,7 @@ Fe::DocHandlingError Install::commitClifpEmulatorConfig(std::unique_ptr docWriter = std::make_shared(document.get()); // Write - Fe::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); + Lr::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); // Ensure document is cleared document.reset(); @@ -283,7 +283,7 @@ Fe::DocHandlingError Install::commitClifpEmulatorConfig(std::unique_ptr Install::preferredImageModeOrder() const { return IMAGE_MODE_ORDER; } +QList Install::preferredImageModeOrder() const { return IMAGE_MODE_ORDER; } bool Install::isRunning() const { @@ -324,18 +324,18 @@ QString Install::versionString() const } // Can't determine version - return Fe::Install::versionString(); + return Lr::Install::versionString(); } -QString Install::translateDocName(const QString& originalName, Fe::DataDoc::Type type) const +QString Install::translateDocName(const QString& originalName, Lr::DataDoc::Type type) const { // Perform general kosherization QString translatedName = Qx::kosherizeFileName(originalName); // Prefix platforms/playlists - if(type == Fe::DataDoc::Type::Platform) + if(type == Lr::DataDoc::Type::Platform) translatedName.prepend(PLATFORM_TAG_PREFIX); - else if(type == Fe::DataDoc::Type::Playlist) + else if(type == Lr::DataDoc::Type::Playlist) translatedName.prepend(PLAYLIST_TAG_PREFIX); return translatedName; @@ -357,7 +357,7 @@ Qx::Error Install::preImport(const ImportDetails& details) return Qx::IoOpReport(Qx::IO_OP_WRITE, Qx::IO_ERR_CANT_CREATE, overviewDir); // Logo and screenshot dir - if(details.imageMode == Fe::ImageMode::Copy || details.imageMode == Fe::ImageMode::Link) + if(details.imageMode == Import::ImageMode::Copy || details.imageMode == Import::ImageMode::Link) { QDir logoDir(mFpScraperDirectory.absoluteFilePath(LOGO_FOLDER_NAME)); if(!logoDir.exists()) @@ -371,7 +371,7 @@ Qx::Error Install::preImport(const ImportDetails& details) } // Perform base tasks - return Fe::Install::preImport(details); + return Lr::Install::preImport(details); } Qx::Error Install::prePlatformsImport() @@ -389,17 +389,17 @@ Qx::Error Install::postPlatformsImport() return commitFlashpointRomlist(std::move(mRomlist)); } -Qx::Error Install::preImageProcessing(QList& workerTransfers, const Fe::ImageSources& bulkSources) +Qx::Error Install::preImageProcessing(QList& workerTransfers, const Lr::ImageSources& bulkSources) { Q_UNUSED(bulkSources); switch(mImportDetails->imageMode) { - case Fe::ImageMode::Link: - case Fe::ImageMode::Copy: + case Import::ImageMode::Link: + case Import::ImageMode::Copy: workerTransfers.swap(mWorkerImageJobs); return Qx::Error(); - case Fe::ImageMode::Reference: + case Import::ImageMode::Reference: qWarning("unsupported image mode"); return Qx::Error(); default: @@ -414,7 +414,7 @@ Qx::Error Install::postImport() // Checkout emulator config std::unique_ptr emulatorConfig; - Fe::DocHandlingError emulatorConfigReadError = checkoutClifpEmulatorConfig(emulatorConfig); + Lr::DocHandlingError emulatorConfigReadError = checkoutClifpEmulatorConfig(emulatorConfig); // Stop import if error occurred if(emulatorConfigReadError.isValid()) @@ -445,7 +445,7 @@ Qx::Error Install::postImport() emulatorConfig->setArtworkEntry(aeb.build()); // Commit emulator config - Fe::DocHandlingError emulatorConfigWriteError = commitClifpEmulatorConfig(std::move(emulatorConfig)); + Lr::DocHandlingError emulatorConfigWriteError = commitClifpEmulatorConfig(std::move(emulatorConfig)); // Stop import if error occurred if(emulatorConfigWriteError.isValid()) @@ -455,7 +455,7 @@ Qx::Error Install::postImport() // Checkout main config std::unique_ptr mainConfig; - Fe::DocHandlingError mainConfigReadError = checkoutMainConfig(mainConfig); + Lr::DocHandlingError mainConfigReadError = checkoutMainConfig(mainConfig); // Stop import if error occurred if(mainConfigReadError.isValid()) @@ -511,6 +511,7 @@ Qx::Error Install::postImport() for(const QString& tagFile : tagFiles) { // Escape brackets in name since AM uses regex for value + // TODO: Use Qx for this QString escaped = tagFile; escaped.replace(u"["_s, u"\\["_s).replace(u"]"_s, u"\\]"_s); @@ -524,7 +525,7 @@ Qx::Error Install::postImport() } // Commit main config - Fe::DocHandlingError configCommitError = commitMainConfig(std::move(mainConfig)); + Lr::DocHandlingError configCommitError = commitMainConfig(std::move(mainConfig)); // Stop import if error occurred if(configCommitError.isValid()) @@ -540,10 +541,10 @@ Qx::Error Install::postImport() return Qx::Error(); } -void Install::processDirectGameImages(const Fe::Game* game, const Fe::ImageSources& imageSources) +void Install::processDirectGameImages(const Lr::Game* game, const Lr::ImageSources& imageSources) { - Fe::ImageMode mode = mImportDetails->imageMode; - if(mode == Fe::ImageMode::Link || mode == Fe::ImageMode::Copy) + Import::ImageMode mode = mImportDetails->imageMode; + if(mode == Import::ImageMode::Link || mode == Import::ImageMode::Copy) { if(!imageSources.logoPath().isEmpty()) { diff --git a/app/src/frontend/attractmode/am-install.h b/app/src/launcher/attractmode/am-install.h similarity index 70% rename from app/src/frontend/attractmode/am-install.h rename to app/src/launcher/attractmode/am-install.h index 81b7540..a9c4058 100644 --- a/app/src/frontend/attractmode/am-install.h +++ b/app/src/launcher/attractmode/am-install.h @@ -5,14 +5,14 @@ #include // Project Includes -#include "frontend/fe-install.h" -#include "am-data.h" -#include "am-settings-data.h" +#include "launcher/lr-install.h" +#include "launcher/attractmode/am-data.h" +#include "launcher/attractmode/am-settings-data.h" namespace Am { -class Install : public Fe::Install +class Install : public Lr::Install { friend class PlatformInterface; friend class PlaylistInterface; @@ -21,7 +21,7 @@ class Install : public Fe::Install public: // Identity static inline const QString NAME = u"AttractMode"_s; - static inline const QString ICON_PATH = u":/frontend/AttractMode/icon.png"_s; + static inline const QString ICON_PATH = u":/launcher/AttractMode/icon.png"_s; static inline const QUrl HELP_URL = QUrl(u""_s); // Naming @@ -52,9 +52,9 @@ class Install : public Fe::Install static inline const QString CFG_EXT = u"cfg"_s; // Support - static inline const QList IMAGE_MODE_ORDER { - Fe::ImageMode::Link, - Fe::ImageMode::Copy + static inline const QList IMAGE_MODE_ORDER { + Import::ImageMode::Link, + Import::ImageMode::Copy }; /* * NOTE: In order to support reference, thousands of folders would have to be added to the image search list which is likely impractical. @@ -62,7 +62,7 @@ class Install : public Fe::Install */ // Extra - static inline const QString MARQUEE_PATH = u":/frontend/AttractMode/marquee.png"_s; + static inline const QString MARQUEE_PATH = u":/launcher/AttractMode/marquee.png"_s; //-Instance Variables----------------------------------------------------------------------------------------------- private: @@ -97,20 +97,20 @@ class Install : public Fe::Install QString versionFromExecutable() const; // Image Processing - QString imageDestinationPath(Fp::ImageType imageType, const Fe::Game* game) const; + QString imageDestinationPath(Fp::ImageType imageType, const Lr::Game* game) const; // Doc handling - std::shared_ptr preparePlatformDocCheckout(std::unique_ptr& platformDoc, const QString& translatedName) override; - std::shared_ptr preparePlaylistDocCheckout(std::unique_ptr& playlistDoc, const QString& translatedName) override; - std::shared_ptr preparePlatformDocCommit(const std::unique_ptr& platformDoc) override; - std::shared_ptr preparePlaylistDocCommit(const std::unique_ptr& playlistDoc) override; - - Fe::DocHandlingError checkoutMainConfig(std::unique_ptr& returnBuffer); - Fe::DocHandlingError checkoutFlashpointRomlist(std::unique_ptr& returnBuffer); - Fe::DocHandlingError checkoutClifpEmulatorConfig(std::unique_ptr& returnBuffer); - Fe::DocHandlingError commitMainConfig(std::unique_ptr document); - Fe::DocHandlingError commitFlashpointRomlist(std::unique_ptr document); - Fe::DocHandlingError commitClifpEmulatorConfig(std::unique_ptr document); + std::shared_ptr preparePlatformDocCheckout(std::unique_ptr& platformDoc, const QString& translatedName) override; + std::shared_ptr preparePlaylistDocCheckout(std::unique_ptr& playlistDoc, const QString& translatedName) override; + std::shared_ptr preparePlatformDocCommit(const std::unique_ptr& platformDoc) override; + std::shared_ptr preparePlaylistDocCommit(const std::unique_ptr& playlistDoc) override; + + Lr::DocHandlingError checkoutMainConfig(std::unique_ptr& returnBuffer); + Lr::DocHandlingError checkoutFlashpointRomlist(std::unique_ptr& returnBuffer); + Lr::DocHandlingError checkoutClifpEmulatorConfig(std::unique_ptr& returnBuffer); + Lr::DocHandlingError commitMainConfig(std::unique_ptr document); + Lr::DocHandlingError commitFlashpointRomlist(std::unique_ptr document); + Lr::DocHandlingError commitClifpEmulatorConfig(std::unique_ptr document); public: // Install management @@ -118,22 +118,22 @@ class Install : public Fe::Install // Info QString name() const override; - QList preferredImageModeOrder() const override; + QList preferredImageModeOrder() const override; bool isRunning() const override; QString versionString() const override; - QString translateDocName(const QString& originalName, Fe::DataDoc::Type type) const override; + QString translateDocName(const QString& originalName, Lr::DataDoc::Type type) const override; // Import stage notifier hooks Qx::Error preImport(const ImportDetails& details) override; Qx::Error prePlatformsImport() override; Qx::Error postPlatformsImport() override; - Qx::Error preImageProcessing(QList& workerTransfers, const Fe::ImageSources& bulkSources) override; + Qx::Error preImageProcessing(QList& workerTransfers, const Lr::ImageSources& bulkSources) override; Qx::Error postImport() override; // Image handling - void processDirectGameImages(const Fe::Game* game, const Fe::ImageSources& imageSources) override; + void processDirectGameImages(const Lr::Game* game, const Lr::ImageSources& imageSources) override; }; -REGISTER_FRONTEND(Install::NAME, Install, &Install::ICON_PATH, &Install::HELP_URL); +REGISTER_LAUNCHER(Install::NAME, Install, &Install::ICON_PATH, &Install::HELP_URL); } #endif // ATTRACTMODE_INSTALL_H diff --git a/app/src/frontend/attractmode/am-install_linux.cpp b/app/src/launcher/attractmode/am-install_linux.cpp similarity index 100% rename from app/src/frontend/attractmode/am-install_linux.cpp rename to app/src/launcher/attractmode/am-install_linux.cpp diff --git a/app/src/frontend/attractmode/am-install_win.cpp b/app/src/launcher/attractmode/am-install_win.cpp similarity index 100% rename from app/src/frontend/attractmode/am-install_win.cpp rename to app/src/launcher/attractmode/am-install_win.cpp diff --git a/app/src/frontend/attractmode/am-items.cpp b/app/src/launcher/attractmode/am-items.cpp similarity index 97% rename from app/src/frontend/attractmode/am-items.cpp rename to app/src/launcher/attractmode/am-items.cpp index ca0e391..2eeeaa7 100644 --- a/app/src/frontend/attractmode/am-items.cpp +++ b/app/src/launcher/attractmode/am-items.cpp @@ -18,7 +18,7 @@ namespace Am RomEntry::RomEntry() {} RomEntry::RomEntry(const Fp::Game& flashpointGame) : - Fe::Game(flashpointGame.id(), ESCAPE(flashpointGame.title()), flashpointGame.platformName()), + Lr::Game(flashpointGame.id(), ESCAPE(flashpointGame.title()), flashpointGame.platformName()), mEmulator(Fp::NAME), mCloneOf(), mYear(flashpointGame.releaseDate().date()), @@ -42,7 +42,7 @@ RomEntry::RomEntry(const Fp::Game& flashpointGame) : {} RomEntry::RomEntry(const Fp::AddApp& flashpointAddApp, const Fp::Game& parentGame) : - Fe::Game(flashpointAddApp.id(), ESCAPE(addAppTitle(parentGame.title(), flashpointAddApp.name())), parentGame.platformName()), + Lr::Game(flashpointAddApp.id(), ESCAPE(addAppTitle(parentGame.title(), flashpointAddApp.name())), parentGame.platformName()), mEmulator(Fp::NAME), mCloneOf(flashpointAddApp.parentId().toString(QUuid::WithoutBraces)), mYear(parentGame.releaseDate().date()), @@ -83,13 +83,13 @@ QString RomEntry::addAppSortTitle(const QString& parentTitle, const QString& ori //-Instance Functions------------------------------------------------------------------------------------------------ //Public: -QUuid RomEntry::name() const { return mId; } // Alias for Fe::Game::Id -QString RomEntry::title() const { return mName; }; // Alias for Fe::Game::name +QUuid RomEntry::name() const { return mId; } // Alias for Lr::Game::Id +QString RomEntry::title() const { return mName; }; // Alias for Lr::Game::name QString RomEntry::emulator() const { return mEmulator; } QString RomEntry::cloneOf() const { return mCloneOf; } QDate RomEntry::year() const{ return mYear; } QString RomEntry::manufacturer() const { return mManufacturer; } -QString RomEntry::category() const { return mPlatform; } // Alias for Fe::Game::platform +QString RomEntry::category() const { return mPlatform; } // Alias for Lr::Game::platform QString RomEntry::players() const { return mPlayers; } quint8 RomEntry::rotation() const { return mRotation; } QString RomEntry::control() const { return mControl; } diff --git a/app/src/frontend/attractmode/am-items.h b/app/src/launcher/attractmode/am-items.h similarity index 88% rename from app/src/frontend/attractmode/am-items.h rename to app/src/launcher/attractmode/am-items.h index 8fb4029..1663ee6 100644 --- a/app/src/frontend/attractmode/am-items.h +++ b/app/src/launcher/attractmode/am-items.h @@ -9,12 +9,12 @@ #include // Project Includes -#include "frontend/fe-items.h" +#include "launcher/lr-items.h" namespace Am { -class RomEntry : public Fe::Game +class RomEntry : public Lr::Game { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -22,13 +22,13 @@ class RomEntry : public Fe::Game //-Instance Variables----------------------------------------------------------------------------------------------- private: - //mName - Handled as alias for Fe::Game::mId - //mTitle - Handled as alias for Fe::Game::mName + //mName - Handled as alias for Lr::Game::mId + //mTitle - Handled as alias for Lr::Game::mName QString mEmulator; QString mCloneOf; QDate mYear; QString mManufacturer; - //mCategory - Handled as alias for Fe::Game::mPlatform + //mCategory - Handled as alias for Lr::Game::mPlatform QString mPlayers; quint8 mRotation; QString mControl; @@ -57,13 +57,13 @@ class RomEntry : public Fe::Game //-Instance Functions------------------------------------------------------------------------------------------------------ public: - QUuid name() const; // Alias for Fe::Game::Id - QString title() const; // Alias for Fe::Game::name + QUuid name() const; // Alias for Lr::Game::Id + QString title() const; // Alias for Lr::Game::name QString emulator() const; QString cloneOf() const; QDate year() const; QString manufacturer() const; - QString category() const; // Alias for Fe::Game::platform + QString category() const; // Alias for Lr::Game::platform QString players() const; quint8 rotation() const; QString control() const; @@ -80,7 +80,7 @@ class RomEntry : public Fe::Game QString rating() const; }; -class RomEntry::Builder : public Fe::Game::Builder +class RomEntry::Builder : public Lr::Game::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -111,7 +111,7 @@ class RomEntry::Builder : public Fe::Game::Builder Builder& wRating(const QString& rating); }; -class EmulatorArtworkEntry : public Fe::Item +class EmulatorArtworkEntry : public Lr::Item { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -132,7 +132,7 @@ class EmulatorArtworkEntry : public Fe::Item QStringList paths() const; }; -class EmulatorArtworkEntry::Builder : public Fe::Item::Builder +class EmulatorArtworkEntry::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: diff --git a/app/src/frontend/attractmode/am-settings-data.cpp b/app/src/launcher/attractmode/am-settings-data.cpp similarity index 98% rename from app/src/frontend/attractmode/am-settings-data.cpp rename to app/src/launcher/attractmode/am-settings-data.cpp index 7d1a73e..53c119c 100644 --- a/app/src/frontend/attractmode/am-settings-data.cpp +++ b/app/src/launcher/attractmode/am-settings-data.cpp @@ -168,7 +168,7 @@ CrudeSettings::CrudeSettings(Install* const parent, const QString& filePath, con //-Instance Functions-------------------------------------------------------------------------------------------------- //Public: bool CrudeSettings::isEmpty() const { return mDisplays.isEmpty() && mOtherSettings.isEmpty(); } -Fe::DataDoc::Type CrudeSettings::type() const { return Type::Config; } +Lr::DataDoc::Type CrudeSettings::type() const { return Type::Config; } bool CrudeSettings::containsDisplay(const QString& name) { return mDisplays.contains(name); } void CrudeSettings::addDisplay(const Display& display) { mDisplays.insert(display.name(), display); } @@ -217,9 +217,9 @@ CrudeSettings* CrudeSettingsReader::targetCrudeSettings() const return static_cast(mTargetDocument); } -Fe::DocHandlingError CrudeSettingsReader::readTargetDoc() +Lr::DocHandlingError CrudeSettingsReader::readTargetDoc() { - Fe::DocHandlingError errorStatus; + Lr::DocHandlingError errorStatus; // Got through all entries while(!mStreamReader.atEnd()) @@ -253,7 +253,7 @@ Fe::DocHandlingError CrudeSettingsReader::readTargetDoc() if(!mCurrentSubSettingParser->parse(key, value, depth)) { QString setting = mCurrentSubSettingParser->settingName(); - errorStatus = Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocReadFailed, UNKNOWN_KEY_ERROR.arg(key, setting)); + errorStatus = Lr::DocHandlingError(*mTargetDocument, Lr::DocHandlingError::DocReadFailed, UNKNOWN_KEY_ERROR.arg(key, setting)); break; } } diff --git a/app/src/frontend/attractmode/am-settings-data.h b/app/src/launcher/attractmode/am-settings-data.h similarity index 96% rename from app/src/frontend/attractmode/am-settings-data.h rename to app/src/launcher/attractmode/am-settings-data.h index 67c580f..5d59430 100644 --- a/app/src/frontend/attractmode/am-settings-data.h +++ b/app/src/launcher/attractmode/am-settings-data.h @@ -2,13 +2,13 @@ #define AM_SETTINGS_DATA_H // Project Includes -#include "am-data.h" -#include "am-settings-items.h" +#include "launcher/attractmode/am-data.h" +#include "launcher/attractmode/am-settings-items.h" namespace Am { -/* This setup of AM config parsing basically inverts the approach of the standard Fe items in that instead of using a builder +/* This setup of AM config parsing basically inverts the approach of the standard Lr items in that instead of using a builder * to work on an in-progress item and then building the item when done and finally adding it to its destination, instead the * item is constructed in a default state first and immediately added to its destination and then what parses the item works * on it in-place. This is basically required due to the need for polymorphism, itself needed due to how the AM config is @@ -18,7 +18,7 @@ namespace Am * are really hacky/jank and should be avoided. * * In the end this approach is similar to how AM itself reads attract.cfg and so it is for the best despite the slight consistency - * break with the Fe defaults. It's probably a good thing that different frontend implementations can be this flexible anyway. + * break with the Lr defaults. It's probably a good thing that different launcher implementations can be this flexible anyway. */ class ISettingParser @@ -198,7 +198,7 @@ class CrudeSettingsReader : public ConfigDoc::Reader //-Instance Functions------------------------------------------------------------------------------------------------- private: CrudeSettings* targetCrudeSettings() const; - Fe::DocHandlingError readTargetDoc() override; + Lr::DocHandlingError readTargetDoc() override; void initializeGenericSubSetting(const QString& key, const QString& value); }; diff --git a/app/src/frontend/attractmode/am-settings-items.cpp b/app/src/launcher/attractmode/am-settings-items.cpp similarity index 100% rename from app/src/frontend/attractmode/am-settings-items.cpp rename to app/src/launcher/attractmode/am-settings-items.cpp diff --git a/app/src/frontend/attractmode/am-settings-items.h b/app/src/launcher/attractmode/am-settings-items.h similarity index 96% rename from app/src/frontend/attractmode/am-settings-items.h rename to app/src/launcher/attractmode/am-settings-items.h index 106d046..f1eff33 100644 --- a/app/src/frontend/attractmode/am-settings-items.h +++ b/app/src/launcher/attractmode/am-settings-items.h @@ -2,14 +2,14 @@ #define AM_SETTINGS_ITEMS_H // Project Includes -#include "frontend/fe-items.h" +#include "launcher/lr-items.h" using namespace Qt::Literals::StringLiterals; namespace Am { -class SettingsItem : public Fe::Item +class SettingsItem : public Lr::Item { //-Instance Variables----------------------------------------------------------------------------------------------- private: @@ -50,7 +50,7 @@ class DisplayGlobalFilter : public SettingsItem QStringList exceptions() const; }; -class DisplayGlobalFilter::Builder : public Fe::Item::Builder +class DisplayGlobalFilter::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -129,7 +129,7 @@ class DisplayFilter : public SettingsItem int listLimit() const; }; -class DisplayFilter::Builder : public Fe::Item::Builder +class DisplayFilter::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -183,7 +183,7 @@ class Display : public SettingsItem const QList& filters() const; }; -class Display::Builder : public Fe::Item::Builder +class Display::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -243,7 +243,7 @@ class OtherSetting : public SettingsItem QList contents() const; }; -class OtherSetting::Builder : public Fe::Item::Builder +class OtherSetting::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: diff --git a/app/src/frontend/launchbox/lb-data.cpp b/app/src/launcher/launchbox/lb-data.cpp similarity index 94% rename from app/src/frontend/launchbox/lb-data.cpp rename to app/src/launcher/launchbox/lb-data.cpp index e065744..980fa30 100644 --- a/app/src/frontend/launchbox/lb-data.cpp +++ b/app/src/launcher/launchbox/lb-data.cpp @@ -5,7 +5,7 @@ #include // Project Includes -#include "lb-install.h" +#include "launcher/launchbox/lb-install.h" namespace Xml { @@ -126,14 +126,14 @@ namespace Lb //-Constructor-------------------------------------------------------------------------------------------------------- //Public: -PlatformDoc::PlatformDoc(Install* const parent, const QString& xmlPath, QString docName, const Fe::UpdateOptions& updateOptions, +PlatformDoc::PlatformDoc(Install* const parent, const QString& xmlPath, QString docName, const Import::UpdateOptions& updateOptions, const DocKey&) : - Fe::BasicPlatformDoc(parent, xmlPath, docName, updateOptions) + Lr::BasicPlatformDoc(parent, xmlPath, docName, updateOptions) {} //-Instance Functions-------------------------------------------------------------------------------------------------- //Private: -std::shared_ptr PlatformDoc::prepareGame(const Fp::Game& game, const Fe::ImageSources& images) +std::shared_ptr PlatformDoc::prepareGame(const Fp::Game& game, const Lr::ImageSources& images) { Q_UNUSED(images); // LaunchBox doesn't store image info in its platform doc directly @@ -155,7 +155,7 @@ std::shared_ptr PlatformDoc::prepareGame(const Fp::Game& game, const F return lbGame; } -std::shared_ptr PlatformDoc::prepareAddApp(const Fp::AddApp& addApp) +std::shared_ptr PlatformDoc::prepareAddApp(const Fp::AddApp& addApp) { // Convert to LaunchBox add app const QString& clifpPath = static_cast(parent())->mImportDetails->clifpPath; @@ -174,7 +174,7 @@ void PlatformDoc::addCustomField(std::shared_ptr customField) //Public: bool PlatformDoc::isEmpty() const { - return mCustomFieldsFinal.isEmpty() && mCustomFieldsExisting.isEmpty() && Fe::BasicPlatformDoc::isEmpty(); + return mCustomFieldsFinal.isEmpty() && mCustomFieldsExisting.isEmpty() && Lr::BasicPlatformDoc::isEmpty(); } void PlatformDoc::finalize() @@ -192,7 +192,7 @@ void PlatformDoc::finalize() ++i; } - Fe::BasicPlatformDoc::finalize(); + Lr::BasicPlatformDoc::finalize(); } //=============================================================================================================== @@ -202,14 +202,14 @@ void PlatformDoc::finalize() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: PlatformDoc::Reader::Reader(PlatformDoc* targetDoc) : - Fe::DataDoc::Reader(targetDoc), - Fe::BasicPlatformDoc::Reader(targetDoc), - Fe::XmlDocReader(targetDoc, Xml::ROOT_ELEMENT) + Lr::DataDoc::Reader(targetDoc), + Lr::BasicPlatformDoc::Reader(targetDoc), + Lr::XmlDocReader(targetDoc, Xml::ROOT_ELEMENT) {} //-Instance Functions------------------------------------------------------------------------------------------------- //Private: -Fe::DocHandlingError PlatformDoc::Reader::readTargetDoc() +Lr::DocHandlingError PlatformDoc::Reader::readTargetDoc() { while(mStreamReader.readNextStartElement()) { @@ -346,9 +346,9 @@ void PlatformDoc::Reader::parseCustomField() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: PlatformDoc::Writer::Writer(PlatformDoc* sourceDoc) : - Fe::DataDoc::Writer(sourceDoc), - Fe::BasicPlatformDoc::Writer(sourceDoc), - Fe::XmlDocWriter(sourceDoc, Xml::ROOT_ELEMENT) + Lr::DataDoc::Writer(sourceDoc), + Lr::BasicPlatformDoc::Writer(sourceDoc), + Lr::XmlDocWriter(sourceDoc, Xml::ROOT_ELEMENT) {} //-Instance Functions------------------------------------------------------------------------------------------------- @@ -356,14 +356,14 @@ PlatformDoc::Writer::Writer(PlatformDoc* sourceDoc) : bool PlatformDoc::Writer::writeSourceDoc() { // Write all games - for(const std::shared_ptr& game : static_cast(mSourceDocument)->finalGames()) + for(const std::shared_ptr& game : static_cast(mSourceDocument)->finalGames()) { if(!writeGame(*std::static_pointer_cast(game))) return false; } // Write all additional apps - for(const std::shared_ptr& addApp : static_cast(mSourceDocument)->finalAddApps()) + for(const std::shared_ptr& addApp : static_cast(mSourceDocument)->finalAddApps()) { if(!writeAddApp(*std::static_pointer_cast(addApp))) return false; @@ -475,15 +475,15 @@ bool PlatformDoc::Writer::writeCustomField(const CustomField& customField) //-Constructor-------------------------------------------------------------------------------------------------------- //Public: -PlaylistDoc::PlaylistDoc(Install* const parent, const QString& xmlPath, QString docName, const Fe::UpdateOptions& updateOptions, +PlaylistDoc::PlaylistDoc(Install* const parent, const QString& xmlPath, QString docName, const Import::UpdateOptions& updateOptions, const DocKey&) : - Fe::BasicPlaylistDoc(parent, xmlPath, docName, updateOptions), + Lr::BasicPlaylistDoc(parent, xmlPath, docName, updateOptions), mLaunchBoxDatabaseIdTracker(&parent->mLbDatabaseIdTracker) {} //-Instance Functions-------------------------------------------------------------------------------------------------- //Private: -std::shared_ptr PlaylistDoc::preparePlaylistHeader(const Fp::Playlist& playlist) +std::shared_ptr PlaylistDoc::preparePlaylistHeader(const Fp::Playlist& playlist) { // Convert to LaunchBox playlist header std::shared_ptr lbPlaylist = std::make_shared(playlist); @@ -492,7 +492,7 @@ std::shared_ptr PlaylistDoc::preparePlaylistHeader(const Fp: return lbPlaylist; } -std::shared_ptr PlaylistDoc::preparePlaylistGame(const Fp::PlaylistGame& game) +std::shared_ptr PlaylistDoc::preparePlaylistGame(const Fp::PlaylistGame& game) { // Convert to LaunchBox playlist game std::shared_ptr lbPlaylistGame = std::make_shared(game, static_cast(parent())->mPlaylistGameDetailsCache); @@ -502,7 +502,7 @@ std::shared_ptr PlaylistDoc::preparePlaylistGame(const Fp::Pla if(mPlaylistGamesExisting.contains(key)) { // Move LB playlist ID if applicable - if(mUpdateOptions.importMode == Fe::ImportMode::NewAndExisting) + if(mUpdateOptions.importMode == Import::UpdateMode::NewAndExisting) lbPlaylistGame->setLBDatabaseId(std::static_pointer_cast(mPlaylistGamesExisting[key])->lbDatabaseId()); } else @@ -522,14 +522,14 @@ std::shared_ptr PlaylistDoc::preparePlaylistGame(const Fp::Pla //-Constructor-------------------------------------------------------------------------------------------------------- //Public: PlaylistDoc::Reader::Reader(PlaylistDoc* targetDoc) : - Fe::DataDoc::Reader(targetDoc), - Fe::BasicPlaylistDoc::Reader(targetDoc), - Fe::XmlDocReader(targetDoc, Xml::ROOT_ELEMENT) + Lr::DataDoc::Reader(targetDoc), + Lr::BasicPlaylistDoc::Reader(targetDoc), + Lr::XmlDocReader(targetDoc, Xml::ROOT_ELEMENT) {} //-Instance Functions------------------------------------------------------------------------------------------------- //Private: -Fe::DocHandlingError PlaylistDoc::Reader::readTargetDoc() +Lr::DocHandlingError PlaylistDoc::Reader::readTargetDoc() { while(mStreamReader.readNextStartElement()) { @@ -616,9 +616,9 @@ void PlaylistDoc::Reader::parsePlaylistGame() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: PlaylistDoc::Writer::Writer(PlaylistDoc* sourceDoc) : - Fe::DataDoc::Writer(sourceDoc), - Fe::BasicPlaylistDoc::Writer(sourceDoc), - Fe::XmlDocWriter(sourceDoc, Xml::ROOT_ELEMENT) + Lr::DataDoc::Writer(sourceDoc), + Lr::BasicPlaylistDoc::Writer(sourceDoc), + Lr::XmlDocWriter(sourceDoc, Xml::ROOT_ELEMENT) {} //-Instance Functions------------------------------------------------------------------------------------------------- @@ -626,12 +626,12 @@ PlaylistDoc::Writer::Writer(PlaylistDoc* sourceDoc) : bool PlaylistDoc::Writer::writeSourceDoc() { // Write playlist header - std::shared_ptr playlistHeader = static_cast(mSourceDocument)->playlistHeader(); + std::shared_ptr playlistHeader = static_cast(mSourceDocument)->playlistHeader(); if(!writePlaylistHeader(*std::static_pointer_cast(playlistHeader))) return false; // Write all playlist games - for(const std::shared_ptr& playlistGame : static_cast(mSourceDocument)->finalPlaylistGames()) + for(const std::shared_ptr& playlistGame : static_cast(mSourceDocument)->finalPlaylistGames()) { if(!writePlaylistGame(*std::static_pointer_cast(playlistGame))) return false; @@ -690,14 +690,14 @@ bool PlaylistDoc::Writer::writePlaylistGame(const PlaylistGame& playlistGame) //-Constructor-------------------------------------------------------------------------------------------------------- //Public: -PlatformsConfigDoc::PlatformsConfigDoc(Install* const parent, const QString& xmlPath, const Fe::UpdateOptions& updateOptions, +PlatformsConfigDoc::PlatformsConfigDoc(Install* const parent, const QString& xmlPath, const Import::UpdateOptions& updateOptions, const DocKey&) : - Fe::UpdateableDoc(parent, xmlPath, STD_NAME, updateOptions) + Lr::UpdateableDoc(parent, xmlPath, STD_NAME, updateOptions) {} //-Instance Functions-------------------------------------------------------------------------------------------------- //Private: -Fe::DataDoc::Type PlatformsConfigDoc::type() const { return Fe::DataDoc::Type::Config; } +Lr::DataDoc::Type PlatformsConfigDoc::type() const { return Lr::DataDoc::Type::Config; } //Public: bool PlatformsConfigDoc::isEmpty() const @@ -758,7 +758,7 @@ void PlatformsConfigDoc::finalize() finalizeUpdateableItems(mPlatformCategoriesExisting, mPlatformCategoriesFinal); // Finalize base - Fe::UpdateableDoc::finalize(); + Lr::UpdateableDoc::finalize(); } //=============================================================================================================== @@ -768,13 +768,13 @@ void PlatformsConfigDoc::finalize() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: PlatformsConfigDoc::Reader::Reader(PlatformsConfigDoc* targetDoc) : - Fe::DataDoc::Reader(targetDoc), - Fe::XmlDocReader(targetDoc, Xml::ROOT_ELEMENT) + Lr::DataDoc::Reader(targetDoc), + Lr::XmlDocReader(targetDoc, Xml::ROOT_ELEMENT) {} //-Instance Functions------------------------------------------------------------------------------------------------- //Private: -Fe::DocHandlingError PlatformsConfigDoc::Reader::readTargetDoc() +Lr::DocHandlingError PlatformsConfigDoc::Reader::readTargetDoc() { while(mStreamReader.readNextStartElement()) { @@ -782,7 +782,7 @@ Fe::DocHandlingError PlatformsConfigDoc::Reader::readTargetDoc() parsePlatform(); else if(mStreamReader.name() == Xml::Element_PlatformFolder::NAME) { - if(Fe::DocHandlingError dhe = parsePlatformFolder(); dhe.isValid()) + if(Lr::DocHandlingError dhe = parsePlatformFolder(); dhe.isValid()) return dhe; } else if (mStreamReader.name() == Xml::Element_PlatformCategory::NAME) @@ -816,7 +816,7 @@ void PlatformsConfigDoc::Reader::parsePlatform() static_cast(mTargetDocument)->mPlatformsExisting[existingPlatform.name()] = existingPlatform; } -Fe::DocHandlingError PlatformsConfigDoc::Reader::parsePlatformFolder() +Lr::DocHandlingError PlatformsConfigDoc::Reader::parsePlatformFolder() { // Platform Folder to Build PlatformFolder::Builder pfb; @@ -831,14 +831,14 @@ Fe::DocHandlingError PlatformsConfigDoc::Reader::parsePlatformFolder() else if(mStreamReader.name() == Xml::Element_PlatformFolder::ELEMENT_PLATFORM) pfb.wPlatform(mStreamReader.readElementText()); else - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocInvalidType); + return Lr::DocHandlingError(*mTargetDocument, Lr::DocHandlingError::DocInvalidType); } // Build PlatformFolder and add to document PlatformFolder existingPlatformFolder = pfb.build(); static_cast(mTargetDocument)->mPlatformFoldersExisting[existingPlatformFolder.identifier()] = existingPlatformFolder; - return Fe::DocHandlingError(); + return Lr::DocHandlingError(); } void PlatformsConfigDoc::Reader::parsePlatformCategory() @@ -871,8 +871,8 @@ void PlatformsConfigDoc::Reader::parsePlatformCategory() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: PlatformsConfigDoc::Writer::Writer(PlatformsConfigDoc* sourceDoc) : - Fe::DataDoc::Writer(sourceDoc), - Fe::XmlDocWriter(sourceDoc, Xml::ROOT_ELEMENT) + Lr::DataDoc::Writer(sourceDoc), + Lr::XmlDocWriter(sourceDoc, Xml::ROOT_ELEMENT) {} //-Instance Functions------------------------------------------------------------------------------------------------- @@ -966,12 +966,12 @@ bool PlatformsConfigDoc::Writer::writePlatformCategory(const PlatformCategory& p //-Constructor-------------------------------------------------------------------------------------------------------- //Public: ParentsDoc::ParentsDoc(Install* const parent, const QString& xmlPath, const DocKey&) : - Fe::DataDoc(parent, xmlPath, STD_NAME) + Lr::DataDoc(parent, xmlPath, STD_NAME) {} //-Instance Functions-------------------------------------------------------------------------------------------------- //Private: -Fe::DataDoc::Type ParentsDoc::type() const { return Fe::DataDoc::Type::Config; } +Lr::DataDoc::Type ParentsDoc::type() const { return Lr::DataDoc::Type::Config; } bool ParentsDoc::removeIfPresent(qsizetype idx) { @@ -1042,13 +1042,13 @@ void ParentsDoc::addParent(const Parent& parent) { mParents.append(parent); } //-Constructor-------------------------------------------------------------------------------------------------------- //Public: ParentsDoc::Reader::Reader(ParentsDoc* targetDoc) : - Fe::DataDoc::Reader(targetDoc), - Fe::XmlDocReader(targetDoc, Xml::ROOT_ELEMENT) + Lr::DataDoc::Reader(targetDoc), + Lr::XmlDocReader(targetDoc, Xml::ROOT_ELEMENT) {} //-Instance Functions------------------------------------------------------------------------------------------------- //Private: -Fe::DocHandlingError ParentsDoc::Reader::readTargetDoc() +Lr::DocHandlingError ParentsDoc::Reader::readTargetDoc() { while(mStreamReader.readNextStartElement()) { @@ -1094,8 +1094,8 @@ void ParentsDoc::Reader::parseParent() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: ParentsDoc::Writer::Writer(ParentsDoc* sourceDoc) : - Fe::DataDoc::Writer(sourceDoc), - Fe::XmlDocWriter(sourceDoc, Xml::ROOT_ELEMENT) + Lr::DataDoc::Writer(sourceDoc), + Lr::XmlDocWriter(sourceDoc, Xml::ROOT_ELEMENT) {} //-Instance Functions------------------------------------------------------------------------------------------------- diff --git a/app/src/frontend/launchbox/lb-data.h b/app/src/launcher/launchbox/lb-data.h similarity index 85% rename from app/src/frontend/launchbox/lb-data.h rename to app/src/launcher/launchbox/lb-data.h index 835073b..652cfcb 100644 --- a/app/src/frontend/launchbox/lb-data.h +++ b/app/src/launcher/launchbox/lb-data.h @@ -1,5 +1,5 @@ -#ifndef LAUNCHBOX_XML_H -#define LAUNCHBOX_XML_H +#ifndef LAUNCHBOX_DATA_H +#define LAUNCHBOX_DATA_H #pragma warning( disable : 4250 ) @@ -12,8 +12,8 @@ #include // Project Includes -#include "lb-items.h" -#include "frontend/fe-data.h" +#include "launcher/lr-data.h" +#include "launcher/launchbox/lb-items.h" // Reminder for virtual inheritance constructor mechanics if needed, // since some classes here use multiple virtual inheritance: @@ -32,7 +32,7 @@ class DocKey DocKey(const DocKey&) = default; }; -class PlatformDoc : public Fe::BasicPlatformDoc +class PlatformDoc : public Lr::BasicPlatformDoc { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -46,13 +46,13 @@ class PlatformDoc : public Fe::BasicPlatformDoc //-Constructor-------------------------------------------------------------------------------------------------------- public: - explicit PlatformDoc(Install* const parent, const QString& xmlPath, QString docName, const Fe::UpdateOptions& updateOptions, + explicit PlatformDoc(Install* const parent, const QString& xmlPath, QString docName, const Import::UpdateOptions& updateOptions, const DocKey&); //-Instance Functions-------------------------------------------------------------------------------------------------- private: - std::shared_ptr prepareGame(const Fp::Game& game, const Fe::ImageSources& images) override; - std::shared_ptr prepareAddApp(const Fp::AddApp& addApp) override; + std::shared_ptr prepareGame(const Fp::Game& game, const Lr::ImageSources& images) override; + std::shared_ptr prepareAddApp(const Fp::AddApp& addApp) override; void addCustomField(std::shared_ptr customField); @@ -62,7 +62,7 @@ class PlatformDoc : public Fe::BasicPlatformDoc void finalize() override; }; -class PlatformDoc::Reader : public Fe::BasicPlatformDoc::Reader, public Fe::XmlDocReader +class PlatformDoc::Reader : public Lr::BasicPlatformDoc::Reader, public Lr::XmlDocReader { //-Constructor-------------------------------------------------------------------------------------------------------- public: @@ -70,13 +70,13 @@ class PlatformDoc::Reader : public Fe::BasicPlatformDoc::Reader, public Fe::XmlD //-Instance Functions------------------------------------------------------------------------------------------------- private: - Fe::DocHandlingError readTargetDoc() override; + Lr::DocHandlingError readTargetDoc() override; void parseGame(); void parseAddApp(); void parseCustomField(); }; -class PlatformDoc::Writer : public Fe::BasicPlatformDoc::Writer, public Fe::XmlDocWriter +class PlatformDoc::Writer : public Lr::BasicPlatformDoc::Writer, public Lr::XmlDocWriter { //-Constructor-------------------------------------------------------------------------------------------------------- public: @@ -90,7 +90,7 @@ class PlatformDoc::Writer : public Fe::BasicPlatformDoc::Writer, public Fe::XmlD bool writeCustomField(const CustomField& customField); }; -class PlaylistDoc : public Fe::BasicPlaylistDoc +class PlaylistDoc : public Lr::BasicPlaylistDoc { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -103,16 +103,16 @@ class PlaylistDoc : public Fe::BasicPlaylistDoc //-Constructor-------------------------------------------------------------------------------------------------------- public: - explicit PlaylistDoc(Install* const parent, const QString& xmlPath, QString docName, const Fe::UpdateOptions& updateOptions, + explicit PlaylistDoc(Install* const parent, const QString& xmlPath, QString docName, const Import::UpdateOptions& updateOptions, const DocKey&); //-Instance Functions-------------------------------------------------------------------------------------------------- private: - std::shared_ptr preparePlaylistHeader(const Fp::Playlist& playlist) override; - std::shared_ptr preparePlaylistGame(const Fp::PlaylistGame& game) override; + std::shared_ptr preparePlaylistHeader(const Fp::Playlist& playlist) override; + std::shared_ptr preparePlaylistGame(const Fp::PlaylistGame& game) override; }; -class PlaylistDoc::Reader : public Fe::BasicPlaylistDoc::Reader, public Fe::XmlDocReader +class PlaylistDoc::Reader : public Lr::BasicPlaylistDoc::Reader, public Lr::XmlDocReader { //-Constructor-------------------------------------------------------------------------------------------------------- public: @@ -120,12 +120,12 @@ class PlaylistDoc::Reader : public Fe::BasicPlaylistDoc::Reader, public Fe::XmlD //-Instance Functions------------------------------------------------------------------------------------------------- private: - Fe::DocHandlingError readTargetDoc() override; + Lr::DocHandlingError readTargetDoc() override; void parsePlaylistHeader(); void parsePlaylistGame(); }; -class PlaylistDoc::Writer : public Fe::BasicPlaylistDoc::Writer, Fe::XmlDocWriter +class PlaylistDoc::Writer : public Lr::BasicPlaylistDoc::Writer, Lr::XmlDocWriter { //-Constructor-------------------------------------------------------------------------------------------------------- public: @@ -138,7 +138,7 @@ class PlaylistDoc::Writer : public Fe::BasicPlaylistDoc::Writer, Fe::XmlDocWrite bool writePlaylistGame(const PlaylistGame& playlistGame); }; -class PlatformsConfigDoc : public Fe::UpdateableDoc +class PlatformsConfigDoc : public Lr::UpdateableDoc { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -160,7 +160,7 @@ class PlatformsConfigDoc : public Fe::UpdateableDoc //-Constructor-------------------------------------------------------------------------------------------------------- public: - explicit PlatformsConfigDoc(Install* const parent, const QString& xmlPath, const Fe::UpdateOptions& updateOptions, + explicit PlatformsConfigDoc(Install* const parent, const QString& xmlPath, const Import::UpdateOptions& updateOptions, const DocKey&); //-Instance Functions-------------------------------------------------------------------------------------------------- @@ -186,7 +186,7 @@ class PlatformsConfigDoc : public Fe::UpdateableDoc void finalize() override; }; -class PlatformsConfigDoc::Reader : public Fe::XmlDocReader +class PlatformsConfigDoc::Reader : public Lr::XmlDocReader { //-Constructor-------------------------------------------------------------------------------------------------------- public: @@ -194,13 +194,13 @@ class PlatformsConfigDoc::Reader : public Fe::XmlDocReader //-Instance Functions------------------------------------------------------------------------------------------------- private: - Fe::DocHandlingError readTargetDoc() override; + Lr::DocHandlingError readTargetDoc() override; void parsePlatform(); - Fe::DocHandlingError parsePlatformFolder(); + Lr::DocHandlingError parsePlatformFolder(); void parsePlatformCategory(); }; -class PlatformsConfigDoc::Writer : public Fe::XmlDocWriter +class PlatformsConfigDoc::Writer : public Lr::XmlDocWriter { //-Constructor-------------------------------------------------------------------------------------------------------- public: @@ -214,7 +214,7 @@ class PlatformsConfigDoc::Writer : public Fe::XmlDocWriter bool writePlatformCategory(const PlatformCategory& platformCategory); }; -class ParentsDoc : public Fe::DataDoc +class ParentsDoc : public Lr::DataDoc { //-Inner Classes---------------------------------------------------------------------------------------------------- public: @@ -263,7 +263,7 @@ class ParentsDoc : public Fe::DataDoc void addParent(const Parent& parent); }; -class ParentsDoc::Reader : public Fe::XmlDocReader +class ParentsDoc::Reader : public Lr::XmlDocReader { //-Constructor-------------------------------------------------------------------------------------------------------- public: @@ -271,11 +271,11 @@ class ParentsDoc::Reader : public Fe::XmlDocReader //-Instance Functions------------------------------------------------------------------------------------------------- private: - Fe::DocHandlingError readTargetDoc() override; + Lr::DocHandlingError readTargetDoc() override; void parseParent(); }; -class ParentsDoc::Writer : public Fe::XmlDocWriter +class ParentsDoc::Writer : public Lr::XmlDocWriter { //-Constructor-------------------------------------------------------------------------------------------------------- public: @@ -288,4 +288,4 @@ class ParentsDoc::Writer : public Fe::XmlDocWriter }; } -#endif // LAUNCHBOX_XML_H +#endif // LAUNCHBOX_DATA_H diff --git a/app/src/frontend/launchbox/lb-install.cpp b/app/src/launcher/launchbox/lb-install.cpp similarity index 81% rename from app/src/frontend/launchbox/lb-install.cpp rename to app/src/launcher/launchbox/lb-install.cpp index a4769d7..ac50333 100644 --- a/app/src/frontend/launchbox/lb-install.cpp +++ b/app/src/launcher/launchbox/lb-install.cpp @@ -24,7 +24,7 @@ namespace Lb //-Constructor------------------------------------------------------------------------------------------------ //Public: Install::Install(const QString& installPath) : - Fe::Install(installPath), + Lr::Install(installPath), mDataDirectory(installPath + '/' + DATA_PATH), mPlatformsDirectory(installPath + '/' + PLATFORMS_PATH), mPlaylistsDirectory(installPath + '/' + PLAYLISTS_PATH), @@ -51,7 +51,7 @@ Install::Install(const QString& installPath) : //Private: void Install::nullify() { - Fe::Install::nullify(); + Lr::Install::nullify(); mDataDirectory = QDir(); mPlatformsDirectory = QDir(); @@ -73,7 +73,7 @@ Qx::Error Install::populateExistingDocs() return existingCheck; for(const QFileInfo& platformFile : qAsConst(existingList)) - catalogueExistingDoc(Fe::DataDoc::Identifier(Fe::DataDoc::Type::Platform, platformFile.baseName())); + catalogueExistingDoc(Lr::DataDoc::Identifier(Lr::DataDoc::Type::Platform, platformFile.baseName())); // Check for playlists existingCheck = Qx::dirContentInfoList(existingList, mPlaylistsDirectory, {u"*."_s + XML_EXT}, QDir::NoFilter, QDirIterator::Subdirectories); @@ -81,7 +81,7 @@ Qx::Error Install::populateExistingDocs() return existingCheck; for(const QFileInfo& playlistFile : qAsConst(existingList)) - catalogueExistingDoc(Fe::DataDoc::Identifier(Fe::DataDoc::Type::Playlist, playlistFile.baseName())); + catalogueExistingDoc(Lr::DataDoc::Identifier(Lr::DataDoc::Type::Playlist, playlistFile.baseName())); // Check for config docs existingCheck = Qx::dirContentInfoList(existingList, mDataDirectory, {u"*."_s + XML_EXT}); @@ -89,13 +89,13 @@ Qx::Error Install::populateExistingDocs() return existingCheck; for(const QFileInfo& configDocFile : qAsConst(existingList)) - catalogueExistingDoc(Fe::DataDoc::Identifier(Fe::DataDoc::Type::Config, configDocFile.baseName())); + catalogueExistingDoc(Lr::DataDoc::Identifier(Lr::DataDoc::Type::Config, configDocFile.baseName())); // Return success return Qx::Error(); } -QString Install::imageDestinationPath(Fp::ImageType imageType, const Fe::Game* game) const +QString Install::imageDestinationPath(Fp::ImageType imageType, const Lr::Game* game) const { return mPlatformImagesDirectory.absolutePath() + '/' + game->platform() + '/' + @@ -104,7 +104,7 @@ QString Install::imageDestinationPath(Fp::ImageType imageType, const Fe::Game* g '.' + IMAGE_EXT; } -void Install::editBulkImageReferences(const Fe::ImageSources& imageSources) +void Install::editBulkImageReferences(const Lr::ImageSources& imageSources) { // Set media folder paths const QList affectedPlatforms = modifiedPlatforms(); @@ -130,68 +130,68 @@ void Install::editBulkImageReferences(const Fe::ImageSources& imageSources) } } -QString Install::dataDocPath(Fe::DataDoc::Identifier identifier) const +QString Install::dataDocPath(Lr::DataDoc::Identifier identifier) const { QString fileName = identifier.docName() + u"."_s + XML_EXT; switch(identifier.docType()) { - case Fe::DataDoc::Type::Platform: + case Lr::DataDoc::Type::Platform: return mPlatformsDirectory.absoluteFilePath(fileName); break; - case Fe::DataDoc::Type::Playlist: + case Lr::DataDoc::Type::Playlist: return mPlaylistsDirectory.absoluteFilePath(fileName); break; - case Fe::DataDoc::Type::Config: + case Lr::DataDoc::Type::Config: return mDataDirectory.absoluteFilePath(fileName); break; default: - throw new std::invalid_argument("Function argument was not of type Fe::DataDoc::Identifier"); + throw new std::invalid_argument("Function argument was not of type Lr::DataDoc::Identifier"); } } -std::shared_ptr Install::preparePlatformDocCheckout(std::unique_ptr& platformDoc, const QString& translatedName) +std::shared_ptr Install::preparePlatformDocCheckout(std::unique_ptr& platformDoc, const QString& translatedName) { // Create doc file reference - Fe::DataDoc::Identifier docId(Fe::DataDoc::Type::Platform, translatedName); + Lr::DataDoc::Identifier docId(Lr::DataDoc::Type::Platform, translatedName); // Construct unopened document platformDoc = std::make_unique(this, dataDocPath(docId), translatedName, mImportDetails->updateOptions, DocKey{}); // Construct doc reader (need to downcast pointer since doc pointer is upcasted after construction above) - std::shared_ptr docReader = std::make_shared(static_cast(platformDoc.get())); + std::shared_ptr docReader = std::make_shared(static_cast(platformDoc.get())); // Return reader and doc return docReader; } -std::shared_ptr Install::preparePlaylistDocCheckout(std::unique_ptr& playlistDoc, const QString& translatedName) +std::shared_ptr Install::preparePlaylistDocCheckout(std::unique_ptr& playlistDoc, const QString& translatedName) { // Create doc file reference - Fe::DataDoc::Identifier docId(Fe::DataDoc::Type::Playlist, translatedName); + Lr::DataDoc::Identifier docId(Lr::DataDoc::Type::Playlist, translatedName); // Construct unopened document playlistDoc = std::make_unique(this, dataDocPath(docId), translatedName, mImportDetails->updateOptions, DocKey{}); // Construct doc reader (need to downcast pointer since doc pointer is upcasted after construction above) - std::shared_ptr docReader = std::make_shared(static_cast(playlistDoc.get())); + std::shared_ptr docReader = std::make_shared(static_cast(playlistDoc.get())); // Return reader and doc return docReader; } -std::shared_ptr Install::preparePlatformDocCommit(const std::unique_ptr& platformDoc) +std::shared_ptr Install::preparePlatformDocCommit(const std::unique_ptr& platformDoc) { // Construct doc writer - std::shared_ptr docWriter = std::make_shared(static_cast(platformDoc.get())); + std::shared_ptr docWriter = std::make_shared(static_cast(platformDoc.get())); // Return writer return docWriter; } -std::shared_ptr Install::preparePlaylistDocCommit(const std::unique_ptr& playlistDoc) +std::shared_ptr Install::preparePlaylistDocCommit(const std::unique_ptr& playlistDoc) { // Work with native type auto lbPlaylistDoc = static_cast(playlistDoc.get()); @@ -201,26 +201,26 @@ std::shared_ptr Install::preparePlaylistDocCommit(const mModifiedPlaylistIds.insert(lbPlaylistDoc->playlistHeader()->id()); // Construct doc writer - std::shared_ptr docWriter = std::make_shared(lbPlaylistDoc); + std::shared_ptr docWriter = std::make_shared(lbPlaylistDoc); // Return writer return docWriter; } -Fe::DocHandlingError Install::checkoutPlatformsConfigDoc(std::unique_ptr& returnBuffer) +Lr::DocHandlingError Install::checkoutPlatformsConfigDoc(std::unique_ptr& returnBuffer) { // Create doc file reference - Fe::DataDoc::Identifier docId(Fe::DataDoc::Type::Config, PlatformsConfigDoc::STD_NAME); + Lr::DataDoc::Identifier docId(Lr::DataDoc::Type::Config, PlatformsConfigDoc::STD_NAME); // Construct unopened document - Fe::UpdateOptions uo{.importMode = Fe::ImportMode::NewAndExisting, .removeObsolete = false}; + Import::UpdateOptions uo{.importMode = Import::UpdateMode::NewAndExisting, .removeObsolete = false}; returnBuffer = std::make_unique(this, dataDocPath(docId), uo, DocKey{}); // Construct doc reader std::shared_ptr docReader = std::make_shared(returnBuffer.get()); // Open document - Fe::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); + Lr::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); // Set return null on failure if(readErrorStatus.isValid()) @@ -230,7 +230,7 @@ Fe::DocHandlingError Install::checkoutPlatformsConfigDoc(std::unique_ptr document) +Lr::DocHandlingError Install::commitPlatformsConfigDoc(std::unique_ptr document) { assert(document->parent() == this); @@ -238,7 +238,7 @@ Fe::DocHandlingError Install::commitPlatformsConfigDoc(std::unique_ptr docWriter = std::make_shared(document.get()); // Write - Fe::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); + Lr::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); // Ensure document is cleared document.reset(); @@ -247,10 +247,10 @@ Fe::DocHandlingError Install::commitPlatformsConfigDoc(std::unique_ptr& returnBuffer) +Lr::DocHandlingError Install::checkoutParentsDoc(std::unique_ptr& returnBuffer) { // Create doc file reference - Fe::DataDoc::Identifier docId(Fe::DataDoc::Type::Config, ParentsDoc::STD_NAME); + Lr::DataDoc::Identifier docId(Lr::DataDoc::Type::Config, ParentsDoc::STD_NAME); // Construct unopened document returnBuffer = std::make_unique(this, dataDocPath(docId), DocKey{}); @@ -259,7 +259,7 @@ Fe::DocHandlingError Install::checkoutParentsDoc(std::unique_ptr& re std::shared_ptr docReader = std::make_shared(returnBuffer.get()); // Open document - Fe::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); + Lr::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); // Set return null on failure if(readErrorStatus.isValid()) @@ -269,7 +269,7 @@ Fe::DocHandlingError Install::checkoutParentsDoc(std::unique_ptr& re return readErrorStatus; } -Fe::DocHandlingError Install::commitParentsDoc(std::unique_ptr document) +Lr::DocHandlingError Install::commitParentsDoc(std::unique_ptr document) { assert(document->parent() == this); @@ -277,7 +277,7 @@ Fe::DocHandlingError Install::commitParentsDoc(std::unique_ptr docum std::shared_ptr docWriter = std::make_shared(document.get()); // Write - Fe::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); + Lr::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); // Ensure document is cleared document.reset(); @@ -289,7 +289,7 @@ Fe::DocHandlingError Install::commitParentsDoc(std::unique_ptr docum //Public: void Install::softReset() { - Fe::Install::softReset(); + Lr::Install::softReset(); mLbDatabaseIdTracker = Qx::FreeIndexTracker(0, LB_DB_ID_TRACKER_MAX); mPlaylistGameDetailsCache.clear(); @@ -297,7 +297,7 @@ void Install::softReset() } QString Install::name() const { return NAME; } -QList Install::preferredImageModeOrder() const { return IMAGE_MODE_ORDER; } +QList Install::preferredImageModeOrder() const { return IMAGE_MODE_ORDER; } bool Install::isRunning() const { return Qx::processIsRunning(mExeFile.fileName()); } QString Install::versionString() const @@ -312,10 +312,10 @@ QString Install::versionString() const else if(!productVersionStr.isEmpty()) return productVersionStr; else - return Fe::Install::versionString(); + return Lr::Install::versionString(); } -QString Install::translateDocName(const QString& originalName, Fe::DataDoc::Type type) const +QString Install::translateDocName(const QString& originalName, Lr::DataDoc::Type type) const { Q_UNUSED(type); @@ -333,6 +333,7 @@ QString Install::translateDocName(const QString& originalName, Fe::DataDoc::Type QString translatedName = originalName; // LB matched changes (LB might replace all illegal characters with underscores, but these are is known for sure) + // TODO: Use Qx for this translatedName.replace(':','_'); translatedName.replace('#','_'); translatedName.replace('\'','_'); @@ -345,7 +346,7 @@ QString Install::translateDocName(const QString& originalName, Fe::DataDoc::Type Qx::Error Install::prePlatformsImport() { - if(Qx::Error superErr = Fe::Install::prePlatformsImport(); superErr.isValid()) + if(Qx::Error superErr = Lr::Install::prePlatformsImport(); superErr.isValid()) return superErr; // Open platforms document @@ -354,11 +355,11 @@ Qx::Error Install::prePlatformsImport() Qx::Error Install::postPlatformsImport() { - if(Qx::Error superErr = Fe::Install::postPlatformsImport(); superErr.isValid()) + if(Qx::Error superErr = Lr::Install::postPlatformsImport(); superErr.isValid()) return superErr; // Open Parents.xml - if(Fe::DocHandlingError dhe = checkoutParentsDoc(mParents); dhe.isValid()) + if(Lr::DocHandlingError dhe = checkoutParentsDoc(mParents); dhe.isValid()) return dhe; // Add PlatformCategories to Platforms.xml @@ -420,19 +421,19 @@ Qx::Error Install::postPlatformsImport() return Qx::Error(); } -Qx::Error Install::preImageProcessing(QList& workerTransfers, const Fe::ImageSources& bulkSources) +Qx::Error Install::preImageProcessing(QList& workerTransfers, const Lr::ImageSources& bulkSources) { - if(Qx::Error superErr = Fe::Install::preImageProcessing(workerTransfers, bulkSources); superErr.isValid()) + if(Qx::Error superErr = Lr::Install::preImageProcessing(workerTransfers, bulkSources); superErr.isValid()) return superErr; switch(mImportDetails->imageMode) { - case Fe::ImageMode::Link: - case Fe::ImageMode::Copy: + case Import::ImageMode::Link: + case Import::ImageMode::Copy: workerTransfers.swap(mWorkerImageJobs); editBulkImageReferences(bulkSources); break; - case Fe::ImageMode::Reference: + case Import::ImageMode::Reference: editBulkImageReferences(bulkSources); break; default: @@ -444,12 +445,12 @@ Qx::Error Install::preImageProcessing(QList& workerTransfers, const Fe Qx::Error Install::postImageProcessing() { - if(Qx::Error superErr = Fe::Install::postImageProcessing(); superErr.isValid()) + if(Qx::Error superErr = Lr::Install::postImageProcessing(); superErr.isValid()) return superErr; // Save platforms document since it's no longer needed at this point mPlatformsConfig->finalize(); - Fe::DocHandlingError saveError = commitPlatformsConfigDoc(std::move(mPlatformsConfig)); + Lr::DocHandlingError saveError = commitPlatformsConfigDoc(std::move(mPlatformsConfig)); return saveError; } @@ -475,10 +476,10 @@ Qx::Error Install::postPlaylistsImport() return commitParentsDoc(std::move(mParents)); } -void Install::processDirectGameImages(const Fe::Game* game, const Fe::ImageSources& imageSources) +void Install::processDirectGameImages(const Lr::Game* game, const Lr::ImageSources& imageSources) { - Fe::ImageMode mode = mImportDetails->imageMode; - if(mode == Fe::ImageMode::Link || mode == Fe::ImageMode::Copy) + Import::ImageMode mode = mImportDetails->imageMode; + if(mode == Import::ImageMode::Link || mode == Import::ImageMode::Copy) { if(!imageSources.logoPath().isEmpty()) { diff --git a/app/src/frontend/launchbox/lb-install.h b/app/src/launcher/launchbox/lb-install.h similarity index 74% rename from app/src/frontend/launchbox/lb-install.h rename to app/src/launcher/launchbox/lb-install.h index 48d7667..b662295 100644 --- a/app/src/frontend/launchbox/lb-install.h +++ b/app/src/launcher/launchbox/lb-install.h @@ -9,13 +9,13 @@ #include // Project Includes -#include "frontend/fe-install.h" -#include "lb-items.h" -#include "lb-data.h" +#include "launcher/lr-install.h" +#include "launcher/launchbox/lb-items.h" +#include "launcher/launchbox/lb-data.h" namespace Lb { -class Install : public Fe::Install +class Install : public Lr::Install { friend class PlatformDoc; // TODO: See about removing the need for these (CLIfp path would need public accessor here) friend class PlaylistDoc; @@ -23,7 +23,7 @@ class Install : public Fe::Install public: // Identity static inline const QString NAME = u"LaunchBox"_s; - static inline const QString ICON_PATH = u":/frontend/LaunchBox/icon.svg"_s; + static inline const QString ICON_PATH = u":/launcher/LaunchBox/icon.svg"_s; static inline const QUrl HELP_URL = QUrl(u"https://forums.launchbox-app.com/files/file/2652-obbys-flashpoint-importer-for-launchbox"_s); // Paths @@ -53,10 +53,10 @@ class Install : public Fe::Install static const quint64 LB_DB_ID_TRACKER_MAX = 100000; // Support - static inline const QList IMAGE_MODE_ORDER { - Fe::ImageMode::Link, - Fe::ImageMode::Copy, - Fe::ImageMode::Reference + static inline const QList IMAGE_MODE_ORDER { + Import::ImageMode::Link, + Import::ImageMode::Copy, + Import::ImageMode::Reference }; //-Instance Variables----------------------------------------------------------------------------------------------- @@ -97,20 +97,20 @@ class Install : public Fe::Install Qx::Error populateExistingDocs() override; // Image Processing - QString imageDestinationPath(Fp::ImageType imageType, const Fe::Game* game) const; - void editBulkImageReferences(const Fe::ImageSources& imageSources); + QString imageDestinationPath(Fp::ImageType imageType, const Lr::Game* game) const; + void editBulkImageReferences(const Lr::ImageSources& imageSources); // Doc handling - QString dataDocPath(Fe::DataDoc::Identifier identifier) const; - std::shared_ptr preparePlatformDocCheckout(std::unique_ptr& platformDoc, const QString& translatedName) override; - std::shared_ptr preparePlaylistDocCheckout(std::unique_ptr& playlistDoc, const QString& translatedName) override; - std::shared_ptr preparePlatformDocCommit(const std::unique_ptr& platformDoc) override; - std::shared_ptr preparePlaylistDocCommit(const std::unique_ptr& playlistDoc) override; + QString dataDocPath(Lr::DataDoc::Identifier identifier) const; + std::shared_ptr preparePlatformDocCheckout(std::unique_ptr& platformDoc, const QString& translatedName) override; + std::shared_ptr preparePlaylistDocCheckout(std::unique_ptr& playlistDoc, const QString& translatedName) override; + std::shared_ptr preparePlatformDocCommit(const std::unique_ptr& platformDoc) override; + std::shared_ptr preparePlaylistDocCommit(const std::unique_ptr& playlistDoc) override; - Fe::DocHandlingError checkoutPlatformsConfigDoc(std::unique_ptr& returnBuffer); - Fe::DocHandlingError commitPlatformsConfigDoc(std::unique_ptr document); - Fe::DocHandlingError checkoutParentsDoc(std::unique_ptr& returnBuffer); - Fe::DocHandlingError commitParentsDoc(std::unique_ptr document); + Lr::DocHandlingError checkoutPlatformsConfigDoc(std::unique_ptr& returnBuffer); + Lr::DocHandlingError commitPlatformsConfigDoc(std::unique_ptr document); + Lr::DocHandlingError checkoutParentsDoc(std::unique_ptr& returnBuffer); + Lr::DocHandlingError commitParentsDoc(std::unique_ptr document); public: // Install management @@ -118,26 +118,26 @@ class Install : public Fe::Install // Info QString name() const override; - QList preferredImageModeOrder() const override; + QList preferredImageModeOrder() const override; bool isRunning() const override; QString versionString() const override; - QString translateDocName(const QString& originalName, Fe::DataDoc::Type type) const override; + QString translateDocName(const QString& originalName, Lr::DataDoc::Type type) const override; // Import stage notifier hooks Qx::Error prePlatformsImport() override; Qx::Error postPlatformsImport() override; - Qx::Error preImageProcessing(QList& workerTransfers, const Fe::ImageSources& bulkSources) override; + Qx::Error preImageProcessing(QList& workerTransfers, const Lr::ImageSources& bulkSources) override; Qx::Error postImageProcessing() override; Qx::Error postPlaylistsImport() override; // Image handling - void processDirectGameImages(const Fe::Game* game, const Fe::ImageSources& imageSources) override; + void processDirectGameImages(const Lr::Game* game, const Lr::ImageSources& imageSources) override; QString platformCategoryIconPath() const override; std::optional platformIconsDirectory() const override; std::optional playlistIconsDirectory() const override; }; -REGISTER_FRONTEND(Install::NAME, Install, &Install::ICON_PATH, &Install::HELP_URL); +REGISTER_LAUNCHER(Install::NAME, Install, &Install::ICON_PATH, &Install::HELP_URL); } diff --git a/app/src/frontend/launchbox/lb-items.cpp b/app/src/launcher/launchbox/lb-items.cpp similarity index 98% rename from app/src/frontend/launchbox/lb-items.cpp rename to app/src/launcher/launchbox/lb-items.cpp index 205137c..2042a64 100644 --- a/app/src/frontend/launchbox/lb-items.cpp +++ b/app/src/launcher/launchbox/lb-items.cpp @@ -2,7 +2,7 @@ #include "lb-items.h" // Project Includes -#include "clifp.h" +#include "kernel/clifp.h" namespace Lb { @@ -15,7 +15,7 @@ namespace Lb Game::Game() {} Game::Game(const Fp::Game& flashpointGame, const QString& fullCLIFpPath) : - Fe::Game(flashpointGame.id(), flashpointGame.title(), flashpointGame.platformName()), + Lr::Game(flashpointGame.id(), flashpointGame.title(), flashpointGame.platformName()), mSeries(flashpointGame.series()), mDeveloper(flashpointGame.developer()), mPublisher(flashpointGame.publisher()), @@ -112,7 +112,7 @@ Game::Builder& Game::Builder::wReleaseType(const QString& releaseType) { mItemBl AddApp::AddApp() {} AddApp::AddApp(const Fp::AddApp& flashpointAddApp, const QString& fullCLIFpPath) : - Fe::AddApp(flashpointAddApp.id(), flashpointAddApp.name(), flashpointAddApp.parentId()), + Lr::AddApp(flashpointAddApp.id(), flashpointAddApp.name(), flashpointAddApp.parentId()), mAppPath(QDir::toNativeSeparators(fullCLIFpPath)), mCommandLine(flashpointAddApp.isPlayable() ? CLIFp::parametersFromStandard(mId) : CLIFp::parametersFromStandard(flashpointAddApp.appPath(), flashpointAddApp.launchCommand())), @@ -179,7 +179,7 @@ CustomField::Builder& CustomField::Builder::wValue(const QString& value) { mItem //Public: PlaylistHeader::PlaylistHeader() {} PlaylistHeader::PlaylistHeader(const Fp::Playlist& flashpointPlaylist) : - Fe::PlaylistHeader(flashpointPlaylist.id(), flashpointPlaylist.title()), + Lr::PlaylistHeader(flashpointPlaylist.id(), flashpointPlaylist.title()), mNestedName(flashpointPlaylist.title()), mNotes(flashpointPlaylist.description()) {} @@ -231,7 +231,7 @@ QString PlaylistGame::EntryDetails::platform() const { return mPlatform; } //-Constructor------------------------------------------------------------------------------------------------ //Public: PlaylistGame::PlaylistGame(const Fp::PlaylistGame& flashpointPlaylistGame, const QHash& playlistGameDetailsMap) : - Fe::PlaylistGame(flashpointPlaylistGame.gameId(), playlistGameDetailsMap.value(flashpointPlaylistGame.gameId()).title()), + Lr::PlaylistGame(flashpointPlaylistGame.gameId(), playlistGameDetailsMap.value(flashpointPlaylistGame.gameId()).title()), mLBDatabaseId(-1), mGameFilename(playlistGameDetailsMap.value(flashpointPlaylistGame.gameId()).filename()), mGamePlatform(playlistGameDetailsMap.value(flashpointPlaylistGame.gameId()).platform()), diff --git a/app/src/frontend/launchbox/lb-items.h b/app/src/launcher/launchbox/lb-items.h similarity index 93% rename from app/src/frontend/launchbox/lb-items.h rename to app/src/launcher/launchbox/lb-items.h index 685dd3e..cbea416 100644 --- a/app/src/frontend/launchbox/lb-items.h +++ b/app/src/launcher/launchbox/lb-items.h @@ -10,12 +10,12 @@ #include // Project Includes -#include "frontend/fe-items.h" +#include "launcher/lr-items.h" namespace Lb { -class Game : public Fe::Game +class Game : public Lr::Game { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -68,7 +68,7 @@ class Game : public Fe::Game QString releaseType() const; }; -class Game::Builder : public Fe::Game::Builder +class Game::Builder : public Lr::Game::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -96,7 +96,7 @@ class Game::Builder : public Fe::Game::Builder Builder& wReleaseType(const QString& releaseType); }; -class AddApp : public Fe::AddApp +class AddApp : public Lr::AddApp { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -122,7 +122,7 @@ class AddApp : public Fe::AddApp bool isWaitForExit() const; }; -class AddApp::Builder : public Fe::AddApp::Builder +class AddApp::Builder : public Lr::AddApp::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -136,7 +136,7 @@ class AddApp::Builder : public Fe::AddApp::Builder Builder& wWaitForExit(const QString& rawWaitForExit); }; -class CustomField : public Fe::Item +class CustomField : public Lr::Item { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -163,7 +163,7 @@ class CustomField : public Fe::Item QString value() const; }; -class CustomField::Builder : public Fe::Item::Builder +class CustomField::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -177,7 +177,7 @@ class CustomField::Builder : public Fe::Item::Builder Builder& wValue(const QString& value); }; -class PlaylistHeader : public Fe::PlaylistHeader +class PlaylistHeader : public Lr::PlaylistHeader { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -200,7 +200,7 @@ class PlaylistHeader : public Fe::PlaylistHeader QString notes() const; }; -class PlaylistHeader::Builder : public Fe::PlaylistHeader::Builder +class PlaylistHeader::Builder : public Lr::PlaylistHeader::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -213,7 +213,7 @@ class PlaylistHeader::Builder : public Fe::PlaylistHeader::Builder +class PlaylistGame::Builder : public Lr::PlaylistGame::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -275,7 +275,7 @@ class PlaylistGame::Builder : public Fe::PlaylistGame::Builder Builder& wManualOrder(const QString& rawManualOrder); }; -class Platform : public Fe::Item +class Platform : public Lr::Item { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -296,7 +296,7 @@ class Platform : public Fe::Item // QString category() const; }; -class Platform::Builder : public Fe::Item::Builder +class Platform::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -308,7 +308,7 @@ class Platform::Builder : public Fe::Item::Builder // Builder& wCategory(const QString& category); }; -class PlatformFolder : public Fe::Item +class PlatformFolder : public Lr::Item { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -332,7 +332,7 @@ class PlatformFolder : public Fe::Item QString identifier() const; }; -class PlatformFolder::Builder : public Fe::Item::Builder +class PlatformFolder::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -345,7 +345,7 @@ class PlatformFolder::Builder : public Fe::Item::Builder Builder& wPlatform(const QString& platform); }; -class PlatformCategory : public Fe::Item +class PlatformCategory : public Lr::Item { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -366,7 +366,7 @@ class PlatformCategory : public Fe::Item QString nestedName() const; }; -class PlatformCategory::Builder : public Fe::Item::Builder +class PlatformCategory::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: @@ -378,7 +378,7 @@ class PlatformCategory::Builder : public Fe::Item::Builder Builder& wNestedName(const QString& nestedName); }; -class Parent : public Fe::Item +class Parent : public Lr::Item { //-Inner Classes--------------------------------------------------------------------------------------------------- public: @@ -403,7 +403,7 @@ class Parent : public Fe::Item QUuid playlistId() const; }; -class Parent::Builder : public Fe::Item::Builder +class Parent::Builder : public Lr::Item::Builder { //-Constructor------------------------------------------------------------------------------------------------- public: diff --git a/app/src/frontend/fe-data.cpp b/app/src/launcher/lr-data.cpp similarity index 91% rename from app/src/frontend/fe-data.cpp rename to app/src/launcher/lr-data.cpp index 7d8377e..0f7e047 100644 --- a/app/src/frontend/fe-data.cpp +++ b/app/src/launcher/lr-data.cpp @@ -1,14 +1,14 @@ // Unit Include -#include "fe-data.h" +#include "lr-data.h" // Qx Includes #include #include // Project Includes -#include "fe-install.h" +#include "launcher/lr-install.h" -namespace Fe +namespace Lr { //=============================================================================================================== // DocHandlingError @@ -31,6 +31,7 @@ DocHandlingError::DocHandlingError(const DataDoc& doc, Type t, const QString& s) //Private: QString DocHandlingError::generatePrimaryString(const DataDoc& doc, Type t) { + // TODO: Use Qx for this QString formattedError = ERR_STRINGS[t]; formattedError.replace(M_DOC_TYPE, doc.identifier().docTypeString()); formattedError.replace(M_DOC_NAME, doc.identifier().docName()); @@ -178,7 +179,7 @@ Qx::Error Errorable::error() const { return mError; } //-Constructor----------------------------------------------------------------------------------------------------- //Protected: -UpdateableDoc::UpdateableDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions) : +UpdateableDoc::UpdateableDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions) : DataDoc(parent, docPath, docName), mUpdateOptions(updateOptions) {} @@ -193,7 +194,7 @@ void UpdateableDoc::finalize() {} // Does nothing for base class //-Constructor-------------------------------------------------------------------------------------------------------- //Protected: -PlatformDoc::PlatformDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions) : +PlatformDoc::PlatformDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions) : UpdateableDoc(parent, docPath, docName, updateOptions) {} @@ -225,7 +226,7 @@ PlatformDoc::Writer::Writer(DataDoc* sourceDoc) : //-Constructor-------------------------------------------------------------------------------------------------------- //Protected: -BasicPlatformDoc::BasicPlatformDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions) : +BasicPlatformDoc::BasicPlatformDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions) : PlatformDoc(parent, docPath, docName, updateOptions) {} @@ -247,23 +248,23 @@ void BasicPlatformDoc::addSet(const Fp::Set& set, const ImageSources& images) if(!mError.isValid()) { // Prepare game - std::shared_ptr feGame = prepareGame(set.game(), images); + std::shared_ptr lrGame = prepareGame(set.game(), images); // Add game - addUpdateableItem(mGamesExisting, mGamesFinal, feGame); + addUpdateableItem(mGamesExisting, mGamesFinal, lrGame); // Handle additional apps for(const Fp::AddApp& addApp : set.addApps()) { // Prepare - std::shared_ptr feAddApp = prepareAddApp(addApp); + std::shared_ptr lrAddApp = prepareAddApp(addApp); // Add - addUpdateableItem(mAddAppsExisting, mAddAppsFinal, feAddApp); + addUpdateableItem(mAddAppsExisting, mAddAppsFinal, lrAddApp); } // Allow install to handle images if needed - parent()->processDirectGameImages(feGame.get(), images); + parent()->processDirectGameImages(lrGame.get(), images); } } @@ -329,7 +330,7 @@ BasicPlatformDoc::Writer::Writer(DataDoc* sourceDoc) : //-Constructor-------------------------------------------------------------------------------------------------------- //Public: -PlaylistDoc::PlaylistDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions) : +PlaylistDoc::PlaylistDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions) : UpdateableDoc(parent, docPath, docName, updateOptions) {} @@ -370,7 +371,7 @@ PlaylistDoc::Writer::Writer(DataDoc* sourceDoc) : * it may be better to require a value for it in this base class' constructor so that all derivatives must provide * a default (likely null/empty) playlist header. */ -BasicPlaylistDoc::BasicPlaylistDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions) : +BasicPlaylistDoc::BasicPlaylistDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions) : PlaylistDoc(parent, docPath, docName, updateOptions) {} @@ -392,22 +393,22 @@ void BasicPlaylistDoc::setPlaylistData(const Fp::Playlist& playlist) { if(!mError.isValid()) { - std::shared_ptr fePlaylistHeader = preparePlaylistHeader(playlist); + std::shared_ptr lrPlaylistHeader = preparePlaylistHeader(playlist); // Ensure doc already existed before transferring (null check) if(mPlaylistHeader) - fePlaylistHeader->transferOtherFields(mPlaylistHeader->otherFields()); + lrPlaylistHeader->transferOtherFields(mPlaylistHeader->otherFields()); // Set instance header to new one - mPlaylistHeader = fePlaylistHeader; + mPlaylistHeader = lrPlaylistHeader; for(const auto& plg : playlist.playlistGames()) { // Prepare playlist game - std::shared_ptr fePlaylistGame = preparePlaylistGame(plg); + std::shared_ptr lrPlaylistGame = preparePlaylistGame(plg); // Add playlist game - addUpdateableItem(mPlaylistGamesExisting, mPlaylistGamesFinal, fePlaylistGame); + addUpdateableItem(mPlaylistGamesExisting, mPlaylistGamesFinal, lrPlaylistGame); } } } @@ -462,8 +463,8 @@ BasicPlaylistDoc::Writer::Writer(DataDoc* sourceDoc) : //-Constructor-------------------------------------------------------------------------------------------------------- //Public: -XmlDocReader::XmlDocReader(Fe::DataDoc* targetDoc, const QString& root) : - Fe::DataDoc::Reader(targetDoc), +XmlDocReader::XmlDocReader(DataDoc* targetDoc, const QString& root) : + DataDoc::Reader(targetDoc), mXmlFile(targetDoc->path()), mStreamReader(&mXmlFile), mRootElement(root) @@ -471,32 +472,32 @@ XmlDocReader::XmlDocReader(Fe::DataDoc* targetDoc, const QString& root) : //-Instance Functions------------------------------------------------------------------------------------------------- //Protected: -Fe::DocHandlingError XmlDocReader::streamStatus() const +DocHandlingError XmlDocReader::streamStatus() const { if(mStreamReader.hasError()) { Qx::XmlStreamReaderError xmlError(mStreamReader); - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocReadFailed, xmlError.text()); + return DocHandlingError(*mTargetDocument, DocHandlingError::DocReadFailed, xmlError.text()); } - return Fe::DocHandlingError(); + return DocHandlingError(); } //Public: -Fe::DocHandlingError XmlDocReader::readInto() +DocHandlingError XmlDocReader::readInto() { // Open File if(!mXmlFile.open(QFile::ReadOnly)) - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocCantOpen, mXmlFile.errorString()); + return DocHandlingError(*mTargetDocument, DocHandlingError::DocCantOpen, mXmlFile.errorString()); if(!mStreamReader.readNextStartElement()) { Qx::XmlStreamReaderError xmlError(mStreamReader); - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::DocReadFailed, xmlError.text()); + return DocHandlingError(*mTargetDocument, DocHandlingError::DocReadFailed, xmlError.text()); } if(mStreamReader.name() != mRootElement) - return Fe::DocHandlingError(*mTargetDocument, Fe::DocHandlingError::NotParentDoc); + return DocHandlingError(*mTargetDocument, DocHandlingError::NotParentDoc); return readTargetDoc(); @@ -509,8 +510,8 @@ Fe::DocHandlingError XmlDocReader::readInto() //-Constructor-------------------------------------------------------------------------------------------------------- //Public: -XmlDocWriter::XmlDocWriter(Fe::DataDoc* sourceDoc, const QString& root) : - Fe::DataDoc::Writer(sourceDoc), +XmlDocWriter::XmlDocWriter(DataDoc* sourceDoc, const QString& root) : + DataDoc::Writer(sourceDoc), mXmlFile(sourceDoc->path()), mStreamWriter(&mXmlFile), mRootElement(root) @@ -532,18 +533,18 @@ void XmlDocWriter::writeOtherFields(const QHash& otherFields) writeCleanTextElement(i.key(), i.value()); } -Fe::DocHandlingError XmlDocWriter::streamStatus() const +DocHandlingError XmlDocWriter::streamStatus() const { - return mStreamWriter.hasError() ? Fe::DocHandlingError(*mSourceDocument, Fe::DocHandlingError::DocWriteFailed, mStreamWriter.device()->errorString()) : - Fe::DocHandlingError(); + return mStreamWriter.hasError() ? DocHandlingError(*mSourceDocument, DocHandlingError::DocWriteFailed, mStreamWriter.device()->errorString()) : + DocHandlingError(); } //Public: -Fe::DocHandlingError XmlDocWriter::writeOutOf() +DocHandlingError XmlDocWriter::writeOutOf() { // Open File if(!mXmlFile.open(QFile::WriteOnly | QFile::Truncate)) // Discard previous contents - return Fe::DocHandlingError(*mSourceDocument, Fe::DocHandlingError::DocCantSave, mXmlFile.errorString()); + return DocHandlingError(*mSourceDocument, DocHandlingError::DocCantSave, mXmlFile.errorString()); // Enable auto formatting mStreamWriter.setAutoFormatting(true); diff --git a/app/src/frontend/fe-data.h b/app/src/launcher/lr-data.h similarity index 93% rename from app/src/frontend/fe-data.h rename to app/src/launcher/lr-data.h index c6486cf..0bf36d2 100644 --- a/app/src/frontend/fe-data.h +++ b/app/src/launcher/lr-data.h @@ -1,5 +1,5 @@ -#ifndef FE_DATA -#define FE_DATA +#ifndef LR_DATA +#define LR_DATA // Standard Library Includes #include @@ -19,7 +19,8 @@ #include // Project Includes -#include "fe-items.h" +#include "launcher/lr-items.h" +#include "import/settings.h" /* TODO: Right now all docs that need to be constructed by an install have that install marked as their friend, * but they also are using the Passkey Idiom, a key class with a private constructor that they are also friends @@ -34,7 +35,7 @@ * having to do Passkey. */ -namespace Fe +namespace Lr { //-Concepts------------------------------------------------------------------------------------------------------ template @@ -66,18 +67,8 @@ concept updateable_basicitem_container = Qx::qassociative && basic_item @@ -333,7 +324,7 @@ T* itemPtr(std::shared_ptr item) { return item.get(); } if(existingItems.contains(key)) { // Replace if existing update is on, move existing otherwise - if(mUpdateOptions.importMode == ImportMode::NewAndExisting) + if(mUpdateOptions.importMode == Import::UpdateMode::NewAndExisting) { itemPtr(newItem)->transferOtherFields(itemPtr(existingItems[key])->otherFields()); finalItems[key] = newItem; @@ -375,7 +366,7 @@ class PlatformDoc : public UpdateableDoc, public Errorable //-Constructor-------------------------------------------------------------------------------------------------------- protected: - explicit PlatformDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions); + explicit PlatformDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions); //-Instance Functions-------------------------------------------------------------------------------------------------- private: @@ -428,7 +419,7 @@ class BasicPlatformDoc : public PlatformDoc //-Constructor-------------------------------------------------------------------------------------------------------- protected: - explicit BasicPlatformDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions); + explicit BasicPlatformDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions); //-Instance Functions-------------------------------------------------------------------------------------------------- protected: @@ -477,7 +468,7 @@ class PlaylistDoc : public UpdateableDoc, public Errorable //-Constructor-------------------------------------------------------------------------------------------------------- protected: - explicit PlaylistDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions); + explicit PlaylistDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions); //-Instance Functions-------------------------------------------------------------------------------------------------- private: @@ -518,7 +509,7 @@ class BasicPlaylistDoc : public PlaylistDoc //-Constructor-------------------------------------------------------------------------------------------------------- protected: - explicit BasicPlaylistDoc(Install* const parent, const QString& docPath, QString docName, const UpdateOptions& updateOptions); + explicit BasicPlaylistDoc(Install* const parent, const QString& docPath, QString docName, const Import::UpdateOptions& updateOptions); //-Instance Functions-------------------------------------------------------------------------------------------------- protected: @@ -565,9 +556,9 @@ class BasicPlaylistDoc::Writer : public PlaylistDoc::Writer }; /* - * Not used by base implementation, but useful for multiple frontends + * Not used by base implementation, but useful for multiple launchers */ -class XmlDocReader : public virtual Fe::DataDoc::Reader +class XmlDocReader : public virtual DataDoc::Reader { //-Instance Variables-------------------------------------------------------------------------------------------------- protected: @@ -577,20 +568,20 @@ class XmlDocReader : public virtual Fe::DataDoc::Reader //-Constructor-------------------------------------------------------------------------------------------------------- public: - XmlDocReader(Fe::DataDoc* targetDoc, const QString& root); + XmlDocReader(DataDoc* targetDoc, const QString& root); //-Instance Functions------------------------------------------------------------------------------------------------- private: - virtual Fe::DocHandlingError readTargetDoc() = 0; + virtual DocHandlingError readTargetDoc() = 0; protected: - Fe::DocHandlingError streamStatus() const; + DocHandlingError streamStatus() const; public: - Fe::DocHandlingError readInto() override; + DocHandlingError readInto() override; }; -class XmlDocWriter : public virtual Fe::DataDoc::Writer +class XmlDocWriter : public virtual DataDoc::Writer { //-Instance Variables-------------------------------------------------------------------------------------------------- protected: @@ -600,18 +591,18 @@ class XmlDocWriter : public virtual Fe::DataDoc::Writer //-Constructor-------------------------------------------------------------------------------------------------------- public: - XmlDocWriter(Fe::DataDoc* sourceDoc, const QString& root); + XmlDocWriter(DataDoc* sourceDoc, const QString& root); //-Instance Functions------------------------------------------------------------------------------------------------- protected: virtual bool writeSourceDoc() = 0; void writeCleanTextElement(const QString& qualifiedName, const QString& text); void writeOtherFields(const QHash& otherFields); - Fe::DocHandlingError streamStatus() const; + DocHandlingError streamStatus() const; public: - Fe::DocHandlingError writeOutOf() override; + DocHandlingError writeOutOf() override; }; } -#endif // FE_DATA +#endif // LR_DATA diff --git a/app/src/frontend/fe-install.cpp b/app/src/launcher/lr-install.cpp similarity index 77% rename from app/src/frontend/fe-install.cpp rename to app/src/launcher/lr-install.cpp index 9a2a5dd..d186a15 100644 --- a/app/src/frontend/fe-install.cpp +++ b/app/src/launcher/lr-install.cpp @@ -1,10 +1,10 @@ // Unit Include -#include "fe-install.h" +#include "lr-install.h" // Qt Includes #include -namespace Fe +namespace Lr { //=============================================================================================================== @@ -27,17 +27,22 @@ std::shared_ptr Install::acquireMatch(const QString& installPath) // Check all installs against path and return match if found QMap::const_iterator i; + std::shared_ptr possibleMatch; + for(i = registry().constBegin(); i != registry().constEnd(); ++i) { Entry entry = i.value(); - std::shared_ptr possibleMatch = entry.factory->produce(installPath); + possibleMatch = entry.factory->produce(installPath); if(possibleMatch->isValid()) - return possibleMatch; + break; } - // Return nullptr on failure to find match - return nullptr; + /* Potentially wasteful, but if there was no match, return the last checked install so that the + * "Is Install Valid" property stays set to false, while the "Is Install Present" property + * stays true + */ + return possibleMatch; } //-Instance Functions-------------------------------------------------------------------------------------------- @@ -56,7 +61,7 @@ void Install::softReset() InstallFoundation::softReset(); } -bool Install::supportsImageMode(ImageMode imageMode) const { return preferredImageModeOrder().contains(imageMode); } +bool Install::supportsImageMode(Import::ImageMode imageMode) const { return preferredImageModeOrder().contains(imageMode); } QString Install::versionString() const { return u"Unknown Version"_s; } @@ -91,16 +96,16 @@ QString Install::translateDocName(const QString& originalName, DataDoc::Type typ return InstallFoundation::translateDocName(originalName, type); } -Fe::DocHandlingError Install::checkoutPlatformDoc(std::unique_ptr& returnBuffer, const QString& name) +DocHandlingError Install::checkoutPlatformDoc(std::unique_ptr& returnBuffer, const QString& name) { - // Translate to frontend doc name + // Translate to launcher doc name QString translatedName = translateDocName(name, DataDoc::Type::Platform); // Get initialized blank doc and reader std::shared_ptr docReader = preparePlatformDocCheckout(returnBuffer, translatedName); // Open document - Fe::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); + DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); // Set return null on failure if(readErrorStatus.isValid()) @@ -110,16 +115,16 @@ Fe::DocHandlingError Install::checkoutPlatformDoc(std::unique_ptr& return readErrorStatus; } -Fe::DocHandlingError Install::checkoutPlaylistDoc(std::unique_ptr& returnBuffer, const QString& name) +DocHandlingError Install::checkoutPlaylistDoc(std::unique_ptr& returnBuffer, const QString& name) { - // Translate to frontend doc name + // Translate to launcher doc name QString translatedName = translateDocName(name, DataDoc::Type::Playlist); // Get initialized blank doc and reader std::shared_ptr docReader = preparePlaylistDocCheckout(returnBuffer, translatedName); // Open document - Fe::DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); + DocHandlingError readErrorStatus = checkoutDataDocument(returnBuffer.get(), docReader); // Set return null on failure if(readErrorStatus.isValid()) @@ -129,7 +134,7 @@ Fe::DocHandlingError Install::checkoutPlaylistDoc(std::unique_ptr& return readErrorStatus; } -Fe::DocHandlingError Install::commitPlatformDoc(std::unique_ptr document) +DocHandlingError Install::commitPlatformDoc(std::unique_ptr document) { // Doc should belong to this install assert(document->parent() == this); @@ -138,7 +143,7 @@ Fe::DocHandlingError Install::commitPlatformDoc(std::unique_ptr doc std::shared_ptr docWriter = preparePlatformDocCommit(document); // Write - Fe::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); + DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); // Ensure document is cleared document.reset(); @@ -147,7 +152,7 @@ Fe::DocHandlingError Install::commitPlatformDoc(std::unique_ptr doc return writeErrorStatus; } -Fe::DocHandlingError Install::commitPlaylistDoc(std::unique_ptr document) +DocHandlingError Install::commitPlaylistDoc(std::unique_ptr document) { // Doc should belong to this install assert(document->parent() == this); @@ -156,7 +161,7 @@ Fe::DocHandlingError Install::commitPlaylistDoc(std::unique_ptr doc std::shared_ptr docWriter = preparePlaylistDocCommit(document); // Write - Fe::DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); + DocHandlingError writeErrorStatus = commitDataDocument(document.get(), docWriter); // Ensure document is cleared document.reset(); diff --git a/app/src/frontend/fe-install.h b/app/src/launcher/lr-install.h similarity index 76% rename from app/src/frontend/fe-install.h rename to app/src/launcher/lr-install.h index 471ea9f..4591d51 100644 --- a/app/src/frontend/fe-install.h +++ b/app/src/launcher/lr-install.h @@ -1,31 +1,31 @@ -#ifndef FE_INSTALL_H -#define FE_INSTALL_H +#ifndef LR_INSTALL_H +#define LR_INSTALL_H // Qt Includes #include // Project Includes -#include "fe-installfoundation.h" +#include "launcher/lr-installfoundation.h" //-Macros------------------------------------------------------------------------------------------------------------------- -#define REGISTER_FRONTEND(fe_name, fe_install, fe_icon_path, fe_helpUrl) \ - class fe_install##Factory : public Fe::InstallFactory \ +#define REGISTER_LAUNCHER(lr_name, lr_install, lr_icon_path, lr_helpUrl) \ + class lr_install##Factory : public Lr::InstallFactory \ { \ public: \ - fe_install##Factory() \ + lr_install##Factory() \ { \ Install::Entry entry { \ .factory = this, \ - .iconPath = fe_icon_path, \ - .helpUrl = fe_helpUrl \ + .iconPath = lr_icon_path, \ + .helpUrl = lr_helpUrl \ }; \ - Fe::Install::registerInstall(fe_name, entry); \ + Lr::Install::registerInstall(lr_name, entry); \ } \ - virtual std::shared_ptr produce(const QString& installPath) const { return std::make_shared(installPath); } \ + virtual std::shared_ptr produce(const QString& installPath) const { return std::make_shared(installPath); } \ }; \ - static fe_install##Factory _##install##Factory; + static lr_install##Factory _##install##Factory; -namespace Fe +namespace Lr { class InstallFactory @@ -52,7 +52,7 @@ class Install : public InstallFoundation //-Class Functions------------------------------------------------------------------------------------------------------ public: - // NOTE: Registry put behind function call to avoid SIOF since otherwise initialization of static registry before calls to registerFrontend would not be guaranteed + // NOTE: Registry put behind function call to avoid SIOF since otherwise initialization of static registry before calls to registerLauncher would not be guaranteed static QMap& registry(); static void registerInstall(const QString& name, const Entry& entry); static std::shared_ptr acquireMatch(const QString& installPath); @@ -75,8 +75,8 @@ class Install : public InstallFoundation // Info virtual QString name() const = 0; - virtual QList preferredImageModeOrder() const = 0; - bool supportsImageMode(ImageMode imageMode) const; + virtual QList preferredImageModeOrder() const = 0; + bool supportsImageMode(Import::ImageMode imageMode) const; virtual QString versionString() const; virtual bool isRunning() const = 0; @@ -92,14 +92,14 @@ class Install : public InstallFoundation // Doc handling virtual QString translateDocName(const QString& originalName, DataDoc::Type type) const override; - Fe::DocHandlingError checkoutPlatformDoc(std::unique_ptr& returnBuffer, const QString& name); - Fe::DocHandlingError checkoutPlaylistDoc(std::unique_ptr& returnBuffer, const QString& name); - Fe::DocHandlingError commitPlatformDoc(std::unique_ptr platformDoc); - Fe::DocHandlingError commitPlaylistDoc(std::unique_ptr playlistDoc); + DocHandlingError checkoutPlatformDoc(std::unique_ptr& returnBuffer, const QString& name); + DocHandlingError checkoutPlaylistDoc(std::unique_ptr& returnBuffer, const QString& name); + DocHandlingError commitPlatformDoc(std::unique_ptr platformDoc); + DocHandlingError commitPlaylistDoc(std::unique_ptr playlistDoc); // Image handling // NOTE: The image paths provided here can be null (i.e. images unavailable). Handle accordingly in derived. - virtual void processDirectGameImages(const Game* game, const Fe::ImageSources& imageSources) = 0; + virtual void processDirectGameImages(const Game* game, const ImageSources& imageSources) = 0; // TODO: These might need to be changed to support launchers where the platform images are tied closely to the platform documents, // but currently none do this so this works. @@ -109,4 +109,4 @@ class Install : public InstallFoundation }; } -#endif // FE_INSTALL_H +#endif // LR_INSTALL_H diff --git a/app/src/frontend/fe-installfoundation.cpp b/app/src/launcher/lr-installfoundation.cpp similarity index 91% rename from app/src/frontend/fe-installfoundation.cpp rename to app/src/launcher/lr-installfoundation.cpp index 1e6148e..16ff641 100644 --- a/app/src/frontend/fe-installfoundation.cpp +++ b/app/src/launcher/lr-installfoundation.cpp @@ -1,7 +1,7 @@ // Unit Include -#include "fe-installfoundation.h" +#include "lr-installfoundation.h" -namespace Fe +namespace Lr { //=============================================================================================================== @@ -108,14 +108,14 @@ QString InstallFoundation::translateDocName(const QString& originalName, DataDoc void InstallFoundation::catalogueExistingDoc(DataDoc::Identifier existingDoc) { mExistingDocuments.insert(existingDoc); } -Fe::DocHandlingError InstallFoundation::checkoutDataDocument(DataDoc* docToOpen, std::shared_ptr docReader) +DocHandlingError InstallFoundation::checkoutDataDocument(DataDoc* docToOpen, std::shared_ptr docReader) { // Error report to return - Fe::DocHandlingError openReadError; // Defaults to no error + DocHandlingError openReadError; // Defaults to no error // Check if lease is already out if(mLeasedDocuments.contains(docToOpen->identifier())) - openReadError = Fe::DocHandlingError(*docToOpen, Fe::DocHandlingError::DocAlreadyOpen); + openReadError = DocHandlingError(*docToOpen, DocHandlingError::DocAlreadyOpen); else { // Read existing file if present and a reader was provided @@ -131,7 +131,7 @@ Fe::DocHandlingError InstallFoundation::checkoutDataDocument(DataDoc* docToOpen, return openReadError; } -Fe::DocHandlingError InstallFoundation::commitDataDocument(DataDoc* docToSave, std::shared_ptr docWriter) +DocHandlingError InstallFoundation::commitDataDocument(DataDoc* docToSave, std::shared_ptr docWriter) { DataDoc::Identifier id = docToSave->identifier(); bool wasDeleted = mDeletedDocuments.contains(id); @@ -152,16 +152,16 @@ Fe::DocHandlingError InstallFoundation::commitDataDocument(DataDoc* docToSave, s if(QFile::exists(backupPath) && QFileInfo(backupPath).isFile()) { if(!QFile::remove(backupPath)) - return Fe::DocHandlingError(*docToSave, Fe::DocHandlingError::CantRemoveBackup); + return DocHandlingError(*docToSave, DocHandlingError::CantRemoveBackup); } if(!QFile::copy(docPath, backupPath)) - return Fe::DocHandlingError(*docToSave, Fe::DocHandlingError::CantCreateBackup); + return DocHandlingError(*docToSave, DocHandlingError::CantCreateBackup); } } // Error State - Fe::DocHandlingError commitError; + DocHandlingError commitError; // Handle modification if(!docToSave->isEmpty()) diff --git a/app/src/frontend/fe-installfoundation.h b/app/src/launcher/lr-installfoundation.h similarity index 87% rename from app/src/frontend/fe-installfoundation.h rename to app/src/launcher/lr-installfoundation.h index cc9e307..99c4b36 100644 --- a/app/src/frontend/fe-installfoundation.h +++ b/app/src/launcher/lr-installfoundation.h @@ -1,19 +1,17 @@ -#ifndef FE_INSTALLFOUNDATION_H -#define FE_INSTALLFOUNDATION_H +#ifndef LR_INSTALLFOUNDATION_H +#define LR_INSTALLFOUNDATION_H // Qt Includes #include // Project Includes -#include "fe-data.h" +#include "launcher/lr-data.h" +#include "import/settings.h" -namespace Fe +namespace Lr { -//-Enums---------------------------------------------------------------------------------------------------------- -enum class ImageMode {Copy, Reference, Link}; - -class QX_ERROR_TYPE(RevertError, "Fe::RevertError", 1301) +class QX_ERROR_TYPE(RevertError, "Lr::RevertError", 1301) { friend class InstallFoundation; //-Class Enums------------------------------------------------------------- @@ -67,8 +65,8 @@ class InstallFoundation public: struct ImportDetails { - UpdateOptions updateOptions; - ImageMode imageMode; + Import::UpdateOptions updateOptions; + Import::ImageMode imageMode; QString clifpPath; QList involvedPlatforms; QList involvedPlaylists; @@ -92,7 +90,7 @@ class InstallFoundation public: // Base errors // TODO: This is unused, should it be in-use somewhere? - static inline const QString ERR_UNSUPPORTED_FEATURE = u"A feature unsupported by the frontend was called upon!"_s; + static inline const QString ERR_UNSUPPORTED_FEATURE = u"A feature unsupported by the launcher was called upon!"_s; // Image Errors static inline const QString CAPTION_IMAGE_ERR = u"Error importing game image(s)"_s; @@ -147,8 +145,8 @@ class InstallFoundation virtual QString translateDocName(const QString& originalName, DataDoc::Type type) const; void catalogueExistingDoc(DataDoc::Identifier existingDoc); - Fe::DocHandlingError checkoutDataDocument(DataDoc* docToOpen, std::shared_ptr docReader); - Fe::DocHandlingError commitDataDocument(DataDoc* docToSave, std::shared_ptr docWriter); + DocHandlingError checkoutDataDocument(DataDoc* docToOpen, std::shared_ptr docReader); + DocHandlingError commitDataDocument(DataDoc* docToSave, std::shared_ptr docWriter); QList modifiedPlatforms() const; QList modifiedPlaylists() const; @@ -170,4 +168,4 @@ class InstallFoundation } -#endif // FE_INSTALLFOUNDATION_H +#endif // LR_INSTALLFOUNDATION_H diff --git a/app/src/frontend/fe-installfoundation_linux.cpp b/app/src/launcher/lr-installfoundation_linux.cpp similarity index 100% rename from app/src/frontend/fe-installfoundation_linux.cpp rename to app/src/launcher/lr-installfoundation_linux.cpp diff --git a/app/src/frontend/fe-installfoundation_win.cpp b/app/src/launcher/lr-installfoundation_win.cpp similarity index 98% rename from app/src/frontend/fe-installfoundation_win.cpp rename to app/src/launcher/lr-installfoundation_win.cpp index 922c7fa..7e8ff0d 100644 --- a/app/src/frontend/fe-installfoundation_win.cpp +++ b/app/src/launcher/lr-installfoundation_win.cpp @@ -1,10 +1,10 @@ // Unit Include -#include "fe-installfoundation.h" +#include "lr-installfoundation.h" // Windows Includes (Specifically for changing file permissions) #include "Aclapi.h" -namespace Fe +namespace Lr { //=============================================================================================================== // InstallFoundation diff --git a/app/src/frontend/fe-items.cpp b/app/src/launcher/lr-items.cpp similarity index 99% rename from app/src/frontend/fe-items.cpp rename to app/src/launcher/lr-items.cpp index 646c3c3..3f546dd 100644 --- a/app/src/frontend/fe-items.cpp +++ b/app/src/launcher/lr-items.cpp @@ -1,7 +1,7 @@ // Unit Include -#include "fe-items.h" +#include "lr-items.h" -namespace Fe +namespace Lr { //=============================================================================================================== diff --git a/app/src/frontend/fe-items.h b/app/src/launcher/lr-items.h similarity index 96% rename from app/src/frontend/fe-items.h rename to app/src/launcher/lr-items.h index 8122e83..ba0d674 100644 --- a/app/src/frontend/fe-items.h +++ b/app/src/launcher/lr-items.h @@ -1,5 +1,5 @@ -#ifndef FE_ITEMS_H -#define FE_ITEMS_H +#ifndef LR_ITEMS_H +#define LR_ITEMS_H // Standard Library Includes #include @@ -10,19 +10,19 @@ using namespace Qt::Literals::StringLiterals; -namespace Fe +namespace Lr { /* - * TODO: Right now there is no Fe::Set or similar. This would be nice because it would set a standard for Platform + * TODO: Right now there is no Lr::Set or similar. This would be nice because it would set a standard for Platform * docs that store their add apps directly with their main games; however, with the current system this would make * the 'containsXXX' methods hairy and slow unless they're outright removed (which they can be, they're unused), and * the update situation is even rougher. The set couldn't really derive from Item because its not an item by itself, * and even if it was the other fields that would get transferred wouldn't actually be from the game/add apps of the set, * but just empty ones along side them. Likely an extra function would have to be added to UpdateableDoc like * "addUpdateableSet", and finalize be modified as well. Lastly there is the question of whether or not to have a fixed - * set that just contains pointers to Fe::Game and a list of pointers to Fe::AddApp, or intended for the set to - * be derived from as well. A fixed set is likely the better choice since any even remotely compatible frontend should + * set that just contains pointers to Lr::Game and a list of pointers to Lr::AddApp, or intended for the set to + * be derived from as well. A fixed set is likely the better choice since any even remotely compatible launcher should * be able to work with it */ @@ -250,4 +250,4 @@ class PlaylistGame::Builder : public BasicItem::Builder } -#endif // FE_ITEMS_H +#endif // LR_ITEMS_H diff --git a/app/src/main.cpp b/app/src/main.cpp index 5bfd2d3..c941d8d 100644 --- a/app/src/main.cpp +++ b/app/src/main.cpp @@ -1,17 +1,9 @@ -#include "ui/mainwindow.h" +#include "kernel/controller.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); - MainWindow w; - - if(!w.initCompleted()) - { - QMessageBox::critical(nullptr, u"Cannot Start"_s, u"Initialization failed!"_s); - return 1; - } - - w.show(); + Controller c; return a.exec(); } diff --git a/app/src/ui/mainwindow.cpp b/app/src/ui/mainwindow.cpp index 723ffbb..b62b2c6 100644 --- a/app/src/ui/mainwindow.cpp +++ b/app/src/ui/mainwindow.cpp @@ -1,13 +1,13 @@ -// Standard Library Includes -#include -#include +// Unit Include +#include "mainwindow.h" +#include "ui_mainwindow.h" // Qt Includes #include #include #include -#include #include +#include #include #include #include @@ -16,99 +16,114 @@ #include // Qx Includes -#include -#include #include -#include -#include +#include -// Project Includes -#include "mainwindow.h" -#include "ui_mainwindow.h" -#include "project_vars.h" -#include "clifp.h" +// Magic Enum Includes +#include -/* TODO: Consider having this tool deploy a .ini file (or the like) into the target launcher install - * (with the exact location probably being guided by the specific Install child) that saves the settings - * used for the import, so that they can be loaded again when that install is targeted by future versions - * of the tool. Would have to account for an initial import vs update (likely just leaving the update settings - * blank). Wouldn't be a huge difference but could be a nice little time saver. - */ +// Project Includes +#include "import/properties.h" +#include "launcher/lr-install.h" +#include "kernel/clifp.h" //=============================================================================================================== -// MAIN WINDOW +// MainWindow::ImportSelection //=============================================================================================================== //-Constructor--------------------------------------------------------------------------------------------------- -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow), - mProgressPresenter(this) +MainWindow::SelectionList::SelectionList(QListWidget* widget) : + mWidget(widget) { - /*Register metatypes - * NOTE: Qt docs note these should be needed, as always, but since Qt6 signals/slots with these types seem to - * work fine without the following calls. - * See https://forum.qt.io/topic/136627/undocumented-automatic-metatype-registration-in-qt6 - */ - //qRegisterMetaType(); - //qRegisterMetaType(); - //qRegisterMetaType>(); - - // Ensure built-in CLIFp version is valid - if(CLIFp::internalVersion().isNull()) - { - QMessageBox::critical(this, CAPTION_GENERAL_FATAL_ERROR, MSG_FATAL_NO_INTERNAL_CLIFP_VER); - mInitCompleted = false; - return; - } - - // General setup - ui->setupUi(this); - QApplication::setApplicationName(PROJECT_FULL_NAME); - mHasLinkPermissions = testForLinkPermissions(); - setWindowTitle(PROJECT_FULL_NAME); - initializeEnableConditionMaps(); - initializeForms(); - initializeFrontendHelpActions(); - - // Check if Flashpoint is running - if(Qx::processIsRunning(Fp::Install::LAUNCHER_NAME)) - QMessageBox::warning(this, QApplication::applicationName(), MSG_FP_CLOSE_PROMPT); - - mInitCompleted = true; + connect(mWidget, &QListWidget::itemChanged, mWidget, [this](QListWidgetItem* item){ handleCheckChange(item); }); } -//-Destructor---------------------------------------------------------------------------------------------------- -MainWindow::~MainWindow() { delete ui; } - //-Instance Functions-------------------------------------------------------------------------------------------- //Private: -bool MainWindow::testForLinkPermissions() +void MainWindow::SelectionList::handleCheckChange(QListWidgetItem* item) { - QTemporaryDir testLinkDir; - if(testLinkDir.isValid()) + bool checked = item->checkState() == Qt::Checked; + int selCount = mSelCount.valueBypassingBindings(); + if(checked){ ++selCount; } else { --selCount; } + mSelCount = selCount; + + // Handle existing special case + QVariant vExisting = item->data(USER_ROLE_EXISTING); + if(vExisting.isValid() && vExisting.toBool()) { - QFile testLinkTarget(testLinkDir.filePath(u"linktarget.tmp"_s)); + int eSelCount = mExistSelCount.valueBypassingBindings(); + if(checked){ ++eSelCount; } else { --eSelCount; } + mExistSelCount = eSelCount; + } +} - if(testLinkTarget.open(QIODevice::WriteOnly)) - { - testLinkTarget.close(); - std::error_code symlinkError; - std::filesystem::create_symlink(testLinkTarget.fileName().toStdString(), testLinkDir.filePath(u"testlink.tmp"_s).toStdString(), symlinkError); +//Public: +void MainWindow::SelectionList::fill(const QList& imps) +{ + // Fill list widget + for(const auto& imp : imps) + { + // Configure item before adding to avoid unwanted triggering of handleCheckChange() + auto* item = new QListWidgetItem(imp.name); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Unchecked); - if(!symlinkError) - return true; + if(imp.existing) + { + item->setBackground(QBrush(smExistingItemColor)); + item->setData(true, USER_ROLE_EXISTING); } + else + item->setData(false, USER_ROLE_EXISTING); + + mWidget->addItem(item); } +} - // Default - return false; +void MainWindow::SelectionList::clear() +{ + mSelCount = 0; + mExistSelCount = 0; + mWidget->clear(); } +int MainWindow::SelectionList::selectedCount() const { return mSelCount; } +int MainWindow::SelectionList::existingSelectedCount() const { return mExistSelCount; } + +//=============================================================================================================== +// MainWindow +//=============================================================================================================== + +//-Constructor--------------------------------------------------------------------------------------------------- +MainWindow::MainWindow(const Import::Properties& importProperties, QWidget *parent) : + QMainWindow(parent), + ui([this]{ auto ui = new Ui::MainWindow; ui->setupUi(this); return ui;}()), + mPlatformSelections(ui->listWidget_platformChoices), + mPlaylistSelections(ui->listWidget_playlistChoices), + mImportProperties(importProperties), + mImageModeMap(initializeImageModeMap()), + mPlaylistGameModeMap(initializePlaylistGameModeMap()) +{ + // Prepare tag model + mTagModel.setAutoTristate(true); + mTagModel.setSortRole(Qt::DisplayRole); + + // General setup + setWindowTitle(QApplication::applicationName()); + initializeForms(); + initializeLauncherHelpActions(); + initializeBindings(); +} + +//-Destructor---------------------------------------------------------------------------------------------------- +MainWindow::~MainWindow() { delete ui; } + +//-Instance Functions-------------------------------------------------------------------------------------------- +//Private: void MainWindow::initializeForms() { // Capture existing item color from label for use in platform/playlist selection lists - mExistingItemColor = ui->label_existingItemColor->palette().color(QPalette::Window); + smExistingItemColor = ui->label_existingItemColor->palette().color(QPalette::Window); // Add CLIFp version to deploy option ui->action_deployCLIFp->setText(ui->action_deployCLIFp->text() + ' ' + CLIFp::internalVersion().normalized(2).toString()); @@ -125,413 +140,197 @@ void MainWindow::initializeForms() ui->radioButton_reference->text(), ui->radioButton_link->text()); - // Setup main forms - ui->label_flashpointVersion->clear(); - ui->label_frontendVersion->clear(); - // If no link permissions, inform user - if(!mHasLinkPermissions) + if(!mImportProperties.hasLinkPermissions()) ui->radioButton_link->setText(ui->radioButton_link->text().append(REQUIRE_ELEV)); - // Perform standard widget updates - refreshEnableStates(); - refreshCheckStates(); - // NOTE: THIS IS FOR DEBUG PURPOSES //checkLaunchBoxInput("C:/Users/Player/Desktop/LBTest/LaunchBox"); //checkFlashpointInput("D:/FP/Flashpoint 8.1 Ultimate"); } -void MainWindow::initializeEnableConditionMaps() +Qx::Bimap MainWindow::initializeImageModeMap() const { - /* TODO: When Qt6 ports built-in widgets to use the C++ bindable properties system, it - * would be great to convert this approach to using those instead, though this gets - * tricky when checking for things that aren't easy to make a QProperty, such as when - * checking for if the Install pointers are assigned - */ - - // Populate hash-map of widget element enable conditions - mWidgetEnableConditionMap[ui->groupBox_importSelection] = [&](){ return mFrontendInstall && mFlashpointInstall; }; - mWidgetEnableConditionMap[ui->groupBox_playlistGameMode] = [&](){ return getSelectedPlaylists().count() > 0; }; - mWidgetEnableConditionMap[ui->groupBox_updateMode] = [&](){ return selectionsMayModify(); }; - mWidgetEnableConditionMap[ui->groupBox_imageMode] = [&](){ return mFrontendInstall && mFlashpointInstall; }; - - mWidgetEnableConditionMap[ui->radioButton_reference] = [&](){ - return mFrontendInstall && mFlashpointInstall && mFrontendInstall->supportsImageMode(Fe::ImageMode::Reference); - }; - mWidgetEnableConditionMap[ui->radioButton_link] = [&](){ - return mHasLinkPermissions && mFrontendInstall && mFlashpointInstall && mFrontendInstall->supportsImageMode(Fe::ImageMode::Link); - }; - mWidgetEnableConditionMap[ui->radioButton_copy] = [&](){ - return mFrontendInstall && mFlashpointInstall && mFrontendInstall->supportsImageMode(Fe::ImageMode::Copy); + return{ + {Import::ImageMode::Link, ui->radioButton_link}, + {Import::ImageMode::Copy, ui->radioButton_copy}, + {Import::ImageMode::Reference, ui->radioButton_reference}, }; - - mWidgetEnableConditionMap[ui->pushButton_startImport] = [&](){ return getSelectedPlatforms().count() > 0 || - (getSelectedPlaylistGameMode() == ImportWorker::ForceAll && getSelectedPlaylists().count() > 0); }; - - // Populate hash-map of action element enable conditions - mActionEnableConditionMap[ui->action_forceDownloadImages] = [&](){ return mFlashpointInstall && mFlashpointInstall->preferences().onDemandImages; }; - mActionEnableConditionMap[ui->action_editTagFilter] = [&](){ return mFrontendInstall && mFlashpointInstall; }; -} - -void MainWindow::initializeFrontendHelpActions() -{ - // Add install help link for each registered install - auto i = Fe::Install::registry().cbegin(); - auto end = Fe::Install::registry().cend(); - - for(; i != end; i++) - { - QAction* feHelpAction = new QAction(ui->menu_frontendHelp); - feHelpAction->setObjectName(MENU_FE_HELP_OBJ_NAME_TEMPLATE.arg(i.key())); - feHelpAction->setText(i.key()); - feHelpAction->setIcon(QIcon(*(i->iconPath))); - ui->menu_frontendHelp->addAction(feHelpAction); - } -} - -bool MainWindow::installMatchesTargetSeries(const Fp::Install& fpInstall) -{ - Qx::VersionNumber fpVersion = fpInstall.versionInfo()->version(); - return TARGET_FP_VERSION_PREFIX.isPrefixOf(fpVersion) || - TARGET_FP_VERSION_PREFIX.normalized() == fpVersion; // Accounts for if FP doesn't use a trailing zero for major releases -} - -void MainWindow::checkManualInstallInput(InstallType install) -{ - QLineEdit* pathSource = install == InstallType::Frontend ? - ui->lineEdit_frontendPath : - ui->lineEdit_flashpointPath; - - QDir selectedDir = QDir::cleanPath(QDir::fromNativeSeparators(pathSource->text())); - if(!pathSource->text().isEmpty() && selectedDir.exists()) - validateInstall(selectedDir.absolutePath(), install); - else - invalidateInstall(install, false); } -void MainWindow::validateInstall(const QString& installPath, InstallType install) +QHash MainWindow::initializePlaylistGameModeMap() const { - switch(install) - { - case InstallType::Frontend: - mFrontendInstall = Fe::Install::acquireMatch(installPath); - if(mFrontendInstall) - { - ui->icon_frontend_install_status->setPixmap(QPixmap(u":/ui/Valid_Install.png"_s)); - ui->label_frontendVersion->setText(mFrontendInstall->name() + ' ' + mFrontendInstall->versionString()); - } - else - invalidateInstall(install, true); - break; - - case InstallType::Flashpoint: - mFlashpointInstall = std::make_shared(installPath, true); - if(mFlashpointInstall->isValid()) - { - ui->label_flashpointVersion->setText(mFlashpointInstall->versionInfo()->fullString()); - if(installMatchesTargetSeries(*mFlashpointInstall)) - ui->icon_flashpoint_install_status->setPixmap(QPixmap(u":/ui/Valid_Install.png"_s)); - else - { - ui->icon_flashpoint_install_status->setPixmap(QPixmap(u":/ui/Mismatch_Install.png"_s)); - QMessageBox::warning(this, QApplication::applicationName(), MSG_FP_VER_NOT_TARGET); - } - } - else - invalidateInstall(install, true); - break; - } - - refreshEnableStates(); - - if(mFrontendInstall && mFlashpointInstall) - gatherInstallInfo(); + return{ + {ui->radioButton_selectedPlatformsOnly, Import::PlaylistGameMode::SelectedPlatform}, + {ui->radioButton_forceAll, Import::PlaylistGameMode::ForceAll} + }; } -void MainWindow::gatherInstallInfo() +QHash MainWindow::initializeUpdateModeMap() const { - // Get data in order but only continue if each step is successful - if(parseFrontendData()) - { - // Show selection options - populateImportSelectionBoxes(); - - // Generate tab selection model - generateTagSelectionOptions(); - - // Ensure valid image mode - refreshCheckStates(); - - // Advance to next input stage - refreshEnableStates(); - } - else - invalidateInstall(InstallType::Frontend, false); + return{ + {ui->radioButton_onlyAdd, Import::UpdateMode::OnlyNew}, + {ui->radioButton_updateExisting, Import::UpdateMode::NewAndExisting} + }; } -void MainWindow::populateImportSelectionBoxes() +void MainWindow::initializeBindings() { - // Populate import selection boxes - clearListWidgets(); - ui->listWidget_platformChoices->addItems(mFlashpointInstall->database()->platformNames()); - ui->listWidget_playlistChoices->addItems(mFlashpointInstall->playlistManager()->playlistTitles()); + Bindings& b = mBindings; + b.launcherPath = Qx::Bindable(ui->lineEdit_launcherPath, "text"); + b.flashpointPath = Qx::Bindable(ui->lineEdit_flashpointPath, "text"); - // Set item attributes - QListWidgetItem* currentItem; - - for(int i = 0; i < ui->listWidget_platformChoices->count(); i++) - { - currentItem = ui->listWidget_platformChoices->item(i); - currentItem->setFlags(currentItem->flags() | Qt::ItemIsUserCheckable); - currentItem->setCheckState(Qt::Unchecked); - - if(mFrontendInstall->containsPlatform(currentItem->text())) - currentItem->setBackground(QBrush(mExistingItemColor)); - } - - for(int i = 0; i < ui->listWidget_playlistChoices->count(); i++) - { - currentItem = ui->listWidget_playlistChoices->item(i); - currentItem->setFlags(currentItem->flags() | Qt::ItemIsUserCheckable); - currentItem->setCheckState(Qt::Unchecked); + // Indirect Enabled + mImportProperties.bindableImageModeOrder().lifetimeOnValueChanged([&]{ + if(!mImportProperties.isLauncherReady()) + return; - if(mFrontendInstall->containsPlaylist(currentItem->text())) - currentItem->setBackground(QBrush(mExistingItemColor)); - } -} + auto validModes = mImportProperties.imageModeOrder(); + Q_ASSERT(!validModes.isEmpty()); -void MainWindow::generateTagSelectionOptions() -{ - // Ensure old options are dropped - mTagSelectionModel.reset(); + // Move selection to valid option if no longer valid + if(!validModes.contains(getSelectedImageMode())) + mImageModeMap.from(validModes.front())->setChecked(true); - // Get tag hierarchy - QMap tagMap = mFlashpointInstall->database()->tags(); + // Disable invalid mode buttons + magic_enum::enum_for_each([this, &validModes](auto val) { + constexpr Import::ImageMode im = val; + if(!validModes.contains(im)) + mImageModeMap.from(im)->setChecked(false); + }); + }); - // Create new model - mTagSelectionModel = std::make_unique(); - mTagSelectionModel->setAutoTristate(true); - mTagSelectionModel->setSortRole(Qt::DisplayRole); + // Enabled + b.importSelectionEnabled.setBinding([&]{ return mImportProperties.isLauncherReady() && mImportProperties.isFlashpointReady(); }); + b.importSelectionEnabled.lifetimeSubscribe([&]{ ui->groupBox_importSelection->setEnabled(b.importSelectionEnabled); }); + b.playlistGameModeEnabled.setBinding([&]{ return mPlaylistSelections.selectedCount() > 0; }); + b.playlistGameModeEnabled.lifetimeSubscribe([&]{ ui->groupBox_playlistGameMode->setEnabled(b.playlistGameModeEnabled); }); + b.updateModeEnabled.setBinding([&]{ return selectionsMayModify(); }); + b.updateModeEnabled.lifetimeSubscribe([&]{ ui->groupBox_updateMode->setEnabled(b.updateModeEnabled); }); + b.imageModeEnabled.setBinding([&]{ return mImportProperties.isLauncherReady() && mImportProperties.isFlashpointReady(); }); + b.imageModeEnabled.lifetimeSubscribe([&]{ ui->groupBox_imageMode->setEnabled(b.imageModeEnabled); }); + b.startImportEnabled.setBinding([&]{ + return mPlatformSelections.selectedCount() > 0 || (getSelectedPlaylistGameMode() == Import::PlaylistGameMode::ForceAll && mPlaylistSelections.selectedCount() > 0); + }); + b.startImportEnabled.lifetimeSubscribe([&]{ ui->pushButton_startImport->setEnabled(b.startImportEnabled); }); + b.forceDownloadImagesEnabled.setBinding([&]{ return mImportProperties.isImageDownloadable(); }); + b.forceDownloadImagesEnabled.lifetimeSubscribe([&]{ + bool e = b.forceDownloadImagesEnabled; + ui->action_forceDownloadImages->setEnabled(e); + if(!e) + ui->action_forceDownloadImages->setChecked(false); + }); + b.editTagFilterEnabled.setBinding([&]{ return mImportProperties.isFlashpointReady(); }); + b.editTagFilterEnabled.lifetimeSubscribe([&]{ ui->action_editTagFilter->setEnabled(b.editTagFilterEnabled); }); + + // Label text + b.launcherVersion.setBinding([&]{ return mImportProperties.launcherInfo(); }); + b.launcherVersion.lifetimeSubscribe([&]{ ui->label_launcherVersion->setText(b.launcherVersion); }); + b.flashpointVersion.setBinding([&]{ return mImportProperties.flashpointInfo(); }); + b.flashpointVersion.lifetimeSubscribe([&]{ ui->label_flashpointVersion->setText(b.flashpointVersion); }); + + // Line edit text + b.launcherPath.lifetimeOnValueChanged([&]{ emit installPathChanged(ui->lineEdit_launcherPath->text(), Import::Install::Launcher); }); + b.flashpointPath.lifetimeOnValueChanged([&]{ emit installPathChanged(ui->lineEdit_flashpointPath->text(), Import::Install::Flashpoint); }); + + // Icon + b.launcherStatus.setBinding([&]{ + return QPixmap(!mImportProperties.isLauncherPresent() ? u":/ui/No_Install.png"_s : + !mImportProperties.isLauncherReady() ? u":/ui/Invalid_Install.png"_s : + u":/ui/Valid_Install.png"_s); + }); + b.launcherStatus.lifetimeSubscribe([&]{ ui->icon_launcher_install_status->setPixmap(b.launcherStatus); }); - // Populate model - QStandardItem* modelRoot = mTagSelectionModel->invisibleRootItem(); - QMap::const_iterator i; + b.flashpointStatus.setBinding([&]{ + return QPixmap(!mImportProperties.isFlashpointPresent() ? u":/ui/No_Install.png"_s : + !mImportProperties.isFlashpointReady() ? u":/ui/Invalid_Install.png"_s : + !mImportProperties.isFlashpointTargetSeries() ? u":/ui/Mismatch_Install.png"_s : + u":/ui/Valid_Install.png"_s); + }); + b.flashpointStatus.lifetimeSubscribe([&]{ ui->icon_flashpoint_install_status->setPixmap(b.flashpointStatus); }); - // Add root tag categories - for(i = tagMap.constBegin(); i != tagMap.constEnd(); ++i) - { - QStandardItem* rootItem = new QStandardItem(QString(i->name)); - rootItem->setData(QBrush(i->color), Qt::BackgroundRole); - rootItem->setData(QBrush(Qx::Color::textFromBackground(i->color)), Qt::ForegroundRole); - rootItem->setCheckState(Qt::CheckState::Checked); - rootItem->setCheckable(true); - QMap::const_iterator j; - - // Add child tags - for(j = i->tags.constBegin(); j != i->tags.constEnd(); ++j) + // Tag map + mImportProperties.bindableTagMap().lifetimeSubscribe([&]{ + // Populate model + auto tagMap = mImportProperties.tagMap(); + if(tagMap.isEmpty()) { - QStandardItem* childItem = new QStandardItem(QString(j->primaryAlias)); - childItem->setData(j->id, USER_ROLE_TAG_ID); - childItem->setCheckState(Qt::CheckState::Checked); - childItem->setCheckable(true); - - rootItem->appendRow(childItem); + mTagModel.clear(); + return; } - modelRoot->appendRow(rootItem); - } - - // Sort - mTagSelectionModel->sort(0); -} - -bool MainWindow::parseFrontendData() -{ - // IO Error check instance - Qx::Error existingCheck; - - // Get list of existing platforms and playlists - existingCheck = mFrontendInstall->refreshExistingDocs(); - - // IO Error Check - if(existingCheck.isValid()) - Qx::postBlockingError(existingCheck); + QStandardItem* modelRoot = mTagModel.invisibleRootItem(); - // Return true on success - return !existingCheck.isValid(); -} - -bool MainWindow::installsHaveChanged() -{ - // TODO: Make this check more thorough + // Add root tag categories + for(const Fp::Db::TagCategory& tc : tagMap) + { + QStandardItem* rootItem = new QStandardItem(QString(tc.name)); + rootItem->setData(QBrush(tc.color), Qt::BackgroundRole); + rootItem->setData(QBrush(Qx::Color::textFromBackground(tc.color)), Qt::ForegroundRole); + rootItem->setCheckState(Qt::CheckState::Checked); + rootItem->setCheckable(true); + + // Add child tags + for(const Fp::Db::Tag& tag : tc.tags) + { + QStandardItem* childItem = new QStandardItem(QString(tag.primaryAlias)); + childItem->setData(tag.id, USER_ROLE_TAG_ID); + childItem->setCheckState(Qt::CheckState::Checked); + childItem->setCheckable(true); - // Check frontend existing items - bool changed = false; - mFrontendInstall->refreshExistingDocs(&changed); - return changed; -} + rootItem->appendRow(childItem); + } -void MainWindow::redoInputChecks() -{ - // Check existing locations again - validateInstall(mFrontendInstall->path(), InstallType::Frontend); - validateInstall(mFlashpointInstall->dir().absolutePath(), InstallType::Flashpoint); -} + modelRoot->appendRow(rootItem); + } -void MainWindow::invalidateInstall(InstallType install, bool informUser) -{ - clearListWidgets(); - mTagSelectionModel.reset(); // Void tag selection model + // Sort + mTagModel.sort(0); + }); - switch(install) - { - case InstallType::Frontend: - ui->icon_frontend_install_status->setPixmap(QPixmap(u":/ui/Invalid_Install.png"_s)); - ui->label_frontendVersion->clear(); - if(informUser) - QMessageBox::critical(this, QApplication::applicationName(), MSG_FE_INSTALL_INVALID); - mFrontendInstall.reset(); - break; - - case InstallType::Flashpoint: - ui->icon_flashpoint_install_status->setPixmap(QPixmap(u":/ui/Invalid_Install.png"_s)); - ui->label_flashpointVersion->clear(); - if(informUser) - Qx::postBlockingError(mFlashpointInstall->error(), QMessageBox::Ok); - mFlashpointInstall.reset(); - break; - } + // List widget items + mImportProperties.bindablePlatforms().lifetimeSubscribe([&]{ + // Always clear any existing + mPlatformSelections.clear(); - refreshEnableStates(); -} + auto platforms = mImportProperties.platforms(); + if(!platforms.isEmpty()) + mPlatformSelections.fill(platforms); + }); + mImportProperties.bindablePlaylists().lifetimeSubscribe([&]{ + // Always clear any existing + mPlaylistSelections.clear(); -void MainWindow::clearListWidgets() -{ - ui->listWidget_platformChoices->clear(); - ui->listWidget_playlistChoices->clear(); - mPlatformItemCheckStates.clear(); - mPlaylistItemCheckStates.clear(); + auto playlists = mImportProperties.playlists(); + if(!playlists.isEmpty()) + mPlaylistSelections.fill(playlists); + }); } -bool MainWindow::isExistingPlatformSelected() +void MainWindow::initializeLauncherHelpActions() { - // Check platform choices - for(int i = 0; i < ui->listWidget_platformChoices->count(); i++) - { - if(ui->listWidget_platformChoices->item(i)->checkState() == Qt::Checked && - mFrontendInstall->containsPlatform(ui->listWidget_platformChoices->item(i)->text())) - return true; - } - - // Return false if no match - return false; -} + // Add install help link for each registered install + auto i = Lr::Install::registry().cbegin(); + auto end = Lr::Install::registry().cend(); -bool MainWindow::isExistingPlaylistSelected() -{ - // Check platform choices - for(int i = 0; i < ui->listWidget_playlistChoices->count(); i++) + for(; i != end; i++) { - if(ui->listWidget_playlistChoices->item(i)->checkState() == Qt::Checked && - mFrontendInstall->containsPlaylist(ui->listWidget_playlistChoices->item(i)->text())) - return true; + QAction* lrHelpAction = new QAction(ui->menu_launcherHelp); + lrHelpAction->setObjectName(MENU_LR_HELP_OBJ_NAME_TEMPLATE.arg(i.key())); + lrHelpAction->setText(i.key()); + lrHelpAction->setIcon(QIcon(*(i->iconPath))); + ui->menu_launcherHelp->addAction(lrHelpAction); } - - // Return false if no match - return false; } - bool MainWindow::selectionsMayModify() { - return isExistingPlatformSelected() || isExistingPlaylistSelected() || - (getSelectedPlaylistGameMode() == ImportWorker::ForceAll && - mFrontendInstall->containsAnyPlatform(mFlashpointInstall->database()->platformNames())); -} - -void MainWindow::postSqlError(const QString& mainText, const QSqlError& sqlError) -{ - QMessageBox sqlErrorMsg; - sqlErrorMsg.setIcon(QMessageBox::Critical); - sqlErrorMsg.setText(mainText); - sqlErrorMsg.setInformativeText(sqlError.text()); - sqlErrorMsg.setStandardButtons(QMessageBox::Ok); - - sqlErrorMsg.exec(); -} - -void MainWindow::postListError(const QString& mainText, const QStringList& detailedItems) -{ - QMessageBox listError; - listError.setIcon(QMessageBox::Critical); - listError.setText(mainText); - listError.setDetailedText(detailedItems.join('\n')); - listError.setStandardButtons(QMessageBox::Ok); - - listError.exec(); -} - -void MainWindow::postIOError(const QString& mainText, const Qx::IoOpReport& report) -{ - QMessageBox ioErrorMsg; - ioErrorMsg.setIcon(QMessageBox::Critical); - ioErrorMsg.setText(mainText); - ioErrorMsg.setInformativeText(report.outcome()); - ioErrorMsg.setStandardButtons(QMessageBox::Ok); - - ioErrorMsg.exec(); -} - -void MainWindow::refreshEnableStates() -{ - QHash>::const_iterator i; - for(i = mWidgetEnableConditionMap.constBegin(); i != mWidgetEnableConditionMap.constEnd(); i++) - i.key()->setEnabled(i.value()()); + if(mPlatformSelections.existingSelectedCount() > 0 || mPlaylistSelections.existingSelectedCount() > 0) + return true; - QHash>::const_iterator j; - for(j = mActionEnableConditionMap.constBegin(); j != mActionEnableConditionMap.constEnd(); j++) - j.key()->setEnabled(j.value()()); -} - -void MainWindow::refreshCheckStates() -{ - // Determine allowed/preferred image mode order - QList modeOrder = mFrontendInstall ? mFrontendInstall->preferredImageModeOrder() : DEFAULT_IMAGE_MODE_ORDER; - - // Remove link as an option if user doesn't have permissions - if(!mHasLinkPermissions) - modeOrder.removeAll(Fe::ImageMode::Link); - - // Ensure an option remains - if(modeOrder.isEmpty()) - throw std::runtime_error("MainWindow::refreshCheckStates(): At least one image import mode must be available!"); - - // Move image mode selection to next preferred option if the current one is invalid - Fe::ImageMode im = getSelectedImageMode(); - if(!modeOrder.contains(im)) - { - Fe::ImageMode preferredMode = modeOrder.first(); - - switch(preferredMode) - { - case Fe::ImageMode::Link: - ui->radioButton_link->setChecked(true); - break; - case Fe::ImageMode::Reference: - ui->radioButton_reference->setChecked(true); - break; - case Fe::ImageMode::Copy: - ui->radioButton_copy->setChecked(true); - break; - default: - qCritical("MainWindow::refreshCheckStates(): Invalid preferred image mode."); - break; - } - } - - // Ensure that the force download images option is unchecked if not supported - if(ui->action_forceDownloadImages->isChecked() && mFlashpointInstall && !mFlashpointInstall->preferences().onDemandImages) - ui->action_forceDownloadImages->setChecked(false); + auto plats = mImportProperties.platforms(); + return std::any_of(plats.cbegin(), plats.cend(), [](const Import::Importee& i){ + return i.existing; // Checks if any platform is existing at all + }); } QStringList MainWindow::getSelectedPlatforms() const @@ -556,24 +355,32 @@ QStringList MainWindow::getSelectedPlaylists() const return selectedPlaylists; } -Fp::Db::InclusionOptions MainWindow::getSelectedInclusionOptions() const +Import::PlaylistGameMode MainWindow::getSelectedPlaylistGameMode() const { - return {generateTagExlusionSet(), ui->action_includeAnimations->isChecked()}; + QRadioButton* sel = static_cast(ui->buttonGroup_playlistGameMode->checkedButton()); + Q_ASSERT(sel); + return mPlaylistGameModeMap[sel]; } -Fe::UpdateOptions MainWindow::getSelectedUpdateOptions() const +Fp::Db::InclusionOptions MainWindow::getSelectedInclusionOptions() const { - return {ui->radioButton_onlyAdd->isChecked() ? Fe::ImportMode::OnlyNew : Fe::ImportMode::NewAndExisting, ui->checkBox_removeMissing->isChecked() }; + return {generateTagExlusionSet(), ui->action_includeAnimations->isChecked()}; } -Fe::ImageMode MainWindow::getSelectedImageMode() const +Import::UpdateOptions MainWindow::getSelectedUpdateOptions() const { - return ui->radioButton_copy->isChecked() ? Fe::ImageMode::Copy : ui->radioButton_reference->isChecked() ? Fe::ImageMode::Reference : Fe::ImageMode::Link; + return {ui->radioButton_onlyAdd->isChecked() ? Import::UpdateMode::OnlyNew : Import::UpdateMode::NewAndExisting, ui->checkBox_removeMissing->isChecked() }; + + QRadioButton* sel = static_cast(ui->buttonGroup_updateMode->checkedButton()); + Q_ASSERT(sel); + return mUpdateModeMap[sel]; } -ImportWorker::PlaylistGameMode MainWindow::getSelectedPlaylistGameMode() const +Import::ImageMode MainWindow::getSelectedImageMode() const { - return ui->radioButton_selectedPlatformsOnly->isChecked() ? ImportWorker::SelectedPlatform : ImportWorker::ForceAll; + QRadioButton* sel = static_cast(ui->buttonGroup_imageMode->checkedButton()); + Q_ASSERT(sel); + return mImageModeMap.from(sel); } bool MainWindow::getForceDownloadImages() const @@ -583,184 +390,50 @@ bool MainWindow::getForceDownloadImages() const void MainWindow::prepareImport() { - // Check that install contents haven't been altered - if(installsHaveChanged()) - { - QMessageBox::warning(this, QApplication::applicationName(), MSG_INSTALL_CONTENTS_CHANGED); - redoInputChecks(); - return; - } - - // Warn user if they are changing existing files - if(selectionsMayModify()) - if(QMessageBox::warning(this, QApplication::applicationName(), MSG_PRE_EXISTING_IMPORT, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Cancel) - return; - - // Warn user if Flashpoint is running - // Check if Flashpoint is running - if(Qx::processIsRunning(Fp::Install::LAUNCHER_NAME)) - QMessageBox::warning(this, QApplication::applicationName(), MSG_FP_CLOSE_PROMPT); - - // Only allow proceeding if frontend isn't running - bool feRunning; - while((feRunning = mFrontendInstall->isRunning())) - if(QMessageBox::critical(this, QApplication::applicationName(), MSG_FRONTEND_CLOSE_PROMPT, QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Retry) == QMessageBox::Cancel) - break; - - if(!feRunning) - { - // Start progress presentation - mProgressPresenter.setMinimum(0); - mProgressPresenter.setMaximum(0); - mProgressPresenter.setValue(0); - mProgressPresenter.setBusyState(); - mProgressPresenter.setLabelText(STEP_FP_DB_INITIAL_QUERY); - QApplication::processEvents(); // Force show progress immediately - - // Setup import worker - ImportWorker::ImportSelections impSel{.platforms = getSelectedPlatforms(), - .playlists =getSelectedPlaylists()}; - ImportWorker::OptionSet optSet{ - getSelectedUpdateOptions(), - getSelectedImageMode(), - getForceDownloadImages(), - getSelectedPlaylistGameMode(), - getSelectedInclusionOptions() - }; - ImportWorker importWorker(mFlashpointInstall, mFrontendInstall, impSel, optSet); - - // Setup blocking error connection - connect(&importWorker, &ImportWorker::blockingErrorOccured, this, &MainWindow::handleBlockingError); - - // Setup auth handler - connect(&importWorker, &ImportWorker::authenticationRequired, this, &MainWindow::handleAuthRequest); - - // Create process update connections - connect(&importWorker, &ImportWorker::progressStepChanged, &mProgressPresenter, &ProgressPresenter::setLabelText); - connect(&importWorker, &ImportWorker::progressValueChanged, &mProgressPresenter, &ProgressPresenter::setValue); - connect(&importWorker, &ImportWorker::progressMaximumChanged, &mProgressPresenter, &ProgressPresenter::setMaximum); - connect(&mProgressPresenter, &ProgressPresenter::canceled, &importWorker, &ImportWorker::notifyCanceled); - - // Import error tracker - Qx::Error importError; - - // Start import and forward result to handler - ImportWorker::ImportResult importResult = importWorker.doImport(importError); - handleImportResult(importResult, importError); - } -} - -void MainWindow::revertAllFrontendChanges() -{ - // Trackers - bool tempSkip = false; - bool alwaysSkip = false; - Fe::RevertError currentError; - int retryChoice; - - // Progress - QProgressDialog reversionProgress(CAPTION_REVERT, QString(), 0, mFrontendInstall->revertQueueCount(), this); - reversionProgress.setWindowModality(Qt::WindowModal); - reversionProgress.setAutoReset(false); - - while(mFrontendInstall->revertNextChange(currentError, alwaysSkip || tempSkip) != 0) - { - // Check for error - if(!currentError.isValid()) - { - tempSkip = false; - reversionProgress.setValue(reversionProgress.value() + 1); - } - else - { - retryChoice = Qx::postBlockingError(currentError, QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Abort, QMessageBox::Retry); - - if(retryChoice == QMessageBox::Ignore) - tempSkip = true; - else if(retryChoice == QMessageBox::Abort) - alwaysSkip = true; - } - } - - // Ensure progress dialog is closed - reversionProgress.close(); - - // Reset instance - mFrontendInstall->softReset(); -} - -void MainWindow::deployCLIFp(const Fp::Install& fp, QMessageBox::Button abandonButton) -{ - bool willDeploy = true; - - // Check for existing CLIFp - if(CLIFp::hasCLIFp(fp)) - { - // Notify user if this will be a downgrade - if(CLIFp::internalVersion() < CLIFp::installedVersion(fp)) - willDeploy = (QMessageBox::warning(this, CAPTION_CLIFP_DOWNGRADE, MSG_FP_CLFIP_WILL_DOWNGRADE, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes); - } + // Gather selection's and notify controller + Import::Selections impSel{.platforms = getSelectedPlatforms(), + .playlists =getSelectedPlaylists()}; + Import::OptionSet optSet{ + getSelectedUpdateOptions(), + getSelectedImageMode(), + getForceDownloadImages(), + getSelectedPlaylistGameMode(), + getSelectedInclusionOptions() + }; - // Deploy CLIFp if applicable - if(willDeploy) - { - // Deploy exe - QString deployError; - while(!CLIFp::deployCLIFp(deployError, fp)) - if(QMessageBox::critical(this, CAPTION_CLIFP_ERR, MSG_FP_CANT_DEPLOY_CLIFP.arg(deployError), QMessageBox::Retry | abandonButton, QMessageBox::Retry) == abandonButton) - break; - } + emit importTriggered(impSel, optSet, selectionsMayModify()); } -void MainWindow::standaloneCLIFpDeploy() -{ - // Browse for install - QString selectedDir = QFileDialog::getExistingDirectory(this, CAPTION_FLASHPOINT_BROWSE, QDir::currentPath()); - - if(!selectedDir.isEmpty()) - { - Fp::Install tempFlashpointInstall(selectedDir); - if(tempFlashpointInstall.isValid()) - { - if(!installMatchesTargetSeries(tempFlashpointInstall)) - QMessageBox::warning(this, QApplication::applicationName(), MSG_FP_VER_NOT_TARGET); - - deployCLIFp(tempFlashpointInstall, QMessageBox::Cancel); - } - else - Qx::postBlockingError(tempFlashpointInstall.error(), QMessageBox::Ok); - } -} void MainWindow::showTagSelectionDialog() { // Ensure tags have been populated - assert(mTagSelectionModel); + Q_ASSERT(mTagModel.rowCount() > 0); // Cache current selection states QHash originalCheckStates; - mTagSelectionModel->forEachItem([&](QStandardItem* item) { originalCheckStates[item] = item->checkState(); }); + mTagModel.forEachItem([&](QStandardItem* item) { originalCheckStates[item] = item->checkState(); }); // Create dialog Qx::TreeInputDialog tagSelectionDialog(this); - tagSelectionDialog.setModel(mTagSelectionModel.get()); + tagSelectionDialog.setModel(&mTagModel); tagSelectionDialog.setWindowFlags(tagSelectionDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); tagSelectionDialog.setWindowTitle(CAPTION_TAG_FILTER); - connect(&tagSelectionDialog, &Qx::TreeInputDialog::selectNoneClicked, mTagSelectionModel.get(), &Qx::StandardItemModel::selectNone); - connect(&tagSelectionDialog, &Qx::TreeInputDialog::selectAllClicked, mTagSelectionModel.get(), &Qx::StandardItemModel::selectAll); + connect(&tagSelectionDialog, &Qx::TreeInputDialog::selectNoneClicked, &mTagModel, &Qx::StandardItemModel::selectNone); + connect(&tagSelectionDialog, &Qx::TreeInputDialog::selectAllClicked, &mTagModel, &Qx::StandardItemModel::selectAll); // Present dialog and capture commitment choice int dc = tagSelectionDialog.exec(); // If new selections were canceled, restore previous ones if(dc == QDialog::Rejected) - mTagSelectionModel->forEachItem([&](QStandardItem* item) { item->setCheckState(originalCheckStates[item]); }); + mTagModel.forEachItem([&](QStandardItem* item) { item->setCheckState(originalCheckStates[item]); }); } QSet MainWindow::generateTagExlusionSet() const { QSet exclusionSet; - mTagSelectionModel->forEachItem([&exclusionSet](QStandardItem* item){ + mTagModel.forEachItem([&exclusionSet](QStandardItem* item){ if(item->data(USER_ROLE_TAG_ID).isValid() && item->checkState() == Qt::Unchecked) exclusionSet.insert(item->data(USER_ROLE_TAG_ID).toInt()); }); @@ -768,16 +441,6 @@ QSet MainWindow::generateTagExlusionSet() const return exclusionSet; } -//Protected: -void MainWindow::showEvent(QShowEvent* event) -{ - mProgressPresenter.attachWindow(windowHandle()); - QMainWindow::showEvent(event); -} - -//Public: -bool MainWindow::initCompleted() { return mInitCompleted; } - //-Slots--------------------------------------------------------------------------------------------------------- //Private: void MainWindow::all_on_action_triggered() @@ -793,7 +456,7 @@ void MainWindow::all_on_action_triggered() if(senderAction == ui->action_exit) QApplication::quit(); else if(senderAction == ui->action_deployCLIFp) - standaloneCLIFpDeploy(); + emit standaloneDeployTriggered(); else if(senderAction == ui->action_goToCLIFpGitHub) QDesktopServices::openUrl(URL_CLIFP_GITHUB); else if(senderAction == ui->action_goToFILGitHub) @@ -816,16 +479,13 @@ void MainWindow::all_on_pushButton_clicked() throw std::runtime_error("Pointer conversion to push button failed"); // Determine sender and take corresponding action - if(senderPushButton == ui->pushButton_frontendBrowse) + if(senderPushButton == ui->pushButton_launcherBrowse) { - QString selectedDir = QFileDialog::getExistingDirectory(this, CAPTION_FRONTEND_BROWSE, - (QFileInfo::exists(ui->lineEdit_frontendPath->text()) ? ui->lineEdit_frontendPath->text() : QDir::currentPath())); + QString selectedDir = QFileDialog::getExistingDirectory(this, CAPTION_LAUNCHER_BROWSE, + (QFileInfo::exists(ui->lineEdit_launcherPath->text()) ? ui->lineEdit_launcherPath->text() : QDir::currentPath())); if(!selectedDir.isEmpty()) - { - ui->lineEdit_frontendPath->setText(QDir::toNativeSeparators(selectedDir)); - validateInstall(selectedDir, InstallType::Frontend); - } + ui->lineEdit_launcherPath->setText(QDir::toNativeSeparators(selectedDir)); } else if(senderPushButton == ui->pushButton_flashpointBrowse) { @@ -833,10 +493,7 @@ void MainWindow::all_on_pushButton_clicked() (QFileInfo::exists(ui->lineEdit_flashpointPath->text()) ? ui->lineEdit_flashpointPath->text() : QDir::currentPath())); if(!selectedDir.isEmpty()) - { ui->lineEdit_flashpointPath->setText(QDir::toNativeSeparators(selectedDir)); - validateInstall(selectedDir, InstallType::Flashpoint); - } } else if(senderPushButton == ui->pushButton_selectAll_platforms) { @@ -872,72 +529,6 @@ void MainWindow::all_on_pushButton_clicked() throw std::runtime_error("Unhandled use of all_on_pushButton_clicked() slot"); } -void MainWindow::all_on_lineEdit_editingFinished() -{ - // Get the object that called this slot - QLineEdit* senderLineEdit = qobject_cast(sender()); - - // Ensure the signal that triggered this slot belongs to the above class by checking for null pointer - if(senderLineEdit == nullptr) - throw std::runtime_error("Pointer conversion to line edit failed"); - - // Determine sender and take corresponding action - if(senderLineEdit == ui->lineEdit_frontendPath) - checkManualInstallInput(InstallType::Frontend); - else if(senderLineEdit == ui->lineEdit_flashpointPath) - checkManualInstallInput(InstallType::Flashpoint); - else - throw std::runtime_error("Unhandled use of all_on_linedEdit_textEdited() slot"); -} - -void MainWindow::all_on_listWidget_itemChanged(QListWidgetItem* item) // Proxy for u"onItemChecked"_s -{ - // Get the object that called this slot - QListWidget* senderListWidget = qobject_cast(sender()); - - // Ensure the signal that triggered this slot belongs to the above class by checking for null pointer - if(senderListWidget == nullptr) - throw std::runtime_error("Pointer conversion to list widget failed"); - - if(senderListWidget == ui->listWidget_platformChoices) - { - // Check if change was change in check state - if(mPlatformItemCheckStates.contains(item) && item->checkState() != mPlatformItemCheckStates.value(item)) - refreshEnableStates(); - - // Add/update check state - mPlatformItemCheckStates[item] = item->checkState(); - } - else if(senderListWidget == ui->listWidget_playlistChoices) - { - // Check if change was change in check state - if(mPlaylistItemCheckStates.contains(item) && item->checkState() != mPlaylistItemCheckStates.value(item)) - refreshEnableStates(); - - // Add/update check state - mPlaylistItemCheckStates[item] = item->checkState(); - } - else - throw std::runtime_error("Unhandled use of all_on_listWidget_itemChanged() slot"); -} - -void MainWindow::all_on_radioButton_clicked() -{ - // Get the object that called this slot - QRadioButton* senderRadioButton = qobject_cast(sender()); - - // Ensure the signal that triggered this slot belongs to the above class by checking for null pointer - if(senderRadioButton == nullptr) - throw std::runtime_error("Pointer conversion to radio button failed"); - - if(senderRadioButton == ui->radioButton_selectedPlatformsOnly) - refreshEnableStates(); - else if(senderRadioButton == ui->radioButton_forceAll) - refreshEnableStates(); - else - throw std::runtime_error("Unhandled use of all_on_radioButton_clicked() slot"); -} - void MainWindow::all_on_menu_triggered(QAction *action) { // Get the object that called this slot @@ -947,90 +538,28 @@ void MainWindow::all_on_menu_triggered(QAction *action) if(senderMenu == nullptr) throw std::runtime_error("Pointer conversion to menu failed"); - if(senderMenu == ui->menu_frontendHelp) + if(senderMenu == ui->menu_launcherHelp) { // Get associated help URL and open it - QRegularExpressionMatch frontendMatch = MENU_FE_HELP_KEY_REGEX.match(action->objectName()); + QRegularExpressionMatch launcherMatch = MENU_LR_HELP_KEY_REGEX.match(action->objectName()); - if(frontendMatch.hasMatch()) + if(launcherMatch.hasMatch()) { - QString frontendName = frontendMatch.captured(u"frontend"_s); - if(!frontendName.isNull() && Fe::Install::registry().contains(frontendName)) + QString launcherName = launcherMatch.captured(u"launcher"_s); + if(!launcherName.isNull() && Lr::Install::registry().contains(launcherName)) { - const QUrl* helpUrl = Fe::Install::registry()[frontendName].helpUrl; + const QUrl* helpUrl = Lr::Install::registry()[launcherName].helpUrl; QDesktopServices::openUrl(*helpUrl); return; } } - qWarning("Frontend help action name could not be determined."); + qWarning("Launcher help action name could not be determined."); } else throw std::runtime_error("Unhandled use of all_on_menu_triggered() slot"); } -void MainWindow::handleBlockingError(std::shared_ptr response, const Qx::Error& blockingError, QMessageBox::StandardButtons choices) -{ - mProgressPresenter.setErrorState(); - // Post error and get response - int userChoice = Qx::postBlockingError(blockingError, choices); - // If applicable return selection - if(response) - *response = userChoice; - mProgressPresenter.resetState(); -} - -void MainWindow::handleAuthRequest(const QString& prompt, QAuthenticator* authenticator) -{ - Qx::LoginDialog ld; - ld.setPrompt(prompt); - - int choice = ld.exec(); - - if(choice == QDialog::Accepted) - { - authenticator->setUser(ld.username()); - authenticator->setPassword(ld.password()); - } -} - -void MainWindow::handleImportResult(ImportWorker::ImportResult importResult, const Qx::Error& errorReport) -{ - // Reset progress presenter - mProgressPresenter.reset(); - - // Post error report if present - if(errorReport.isValid()) - Qx::postBlockingError(errorReport, QMessageBox::Ok); - - if(importResult == ImportWorker::Successful) - { - deployCLIFp(*mFlashpointInstall, QMessageBox::Ignore); - - // Post-import message - QMessageBox::information(this, QApplication::applicationName(), MSG_POST_IMPORT); - - // Update selection lists to reflect newly existing platforms - gatherInstallInfo(); - } - else if(importResult == ImportWorker::Taskless) - { - QMessageBox::warning(this, CAPTION_TASKLESS_IMPORT, MSG_NO_WORK); - } - else if(importResult == ImportWorker::Canceled) - { - QMessageBox::critical(this, CAPTION_REVERT, MSG_USER_CANCELED); - revertAllFrontendChanges(); - } - else if(importResult == ImportWorker::Failed) - { - // Show general next steps message - QMessageBox::warning(this, CAPTION_REVERT, MSG_HAVE_TO_REVERT); - revertAllFrontendChanges(); - } - else - qCritical("unhandled import worker result type."); -} diff --git a/app/src/ui/mainwindow.h b/app/src/ui/mainwindow.h index 76ed60b..d1d9ac0 100644 --- a/app/src/ui/mainwindow.h +++ b/app/src/ui/mainwindow.h @@ -1,48 +1,83 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H +// Standard Library Includes +#include + // Qt Includes #include #include #include // Qx Includes -#include -#include #include - -// libfp Includes -#include +#include +#include +#include // Project Includes +#include "import/properties.h" #include "project_vars.h" -#include "ui/progresspresenter.h" -#include "frontend/fe-install.h" -#include "import-worker.h" -#include "clifp.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE +class QRadioButton; + class MainWindow : public QMainWindow { - Q_OBJECT // Required for classes that use Qt elements + Q_OBJECT; +//-Class Structs------------------------------------------------------------------------------------------------ +private: + struct Bindings + { + /* Ideally these could all just be QBindable, but that only works for Q_PROPERTIES that have + * a notify signal, and most of these don't, so we have to use our own property and subscribe + * to it as a workaround instead. + */ + Qx::Property importSelectionEnabled; + Qx::Property playlistGameModeEnabled; + Qx::Property updateModeEnabled; + Qx::Property imageModeEnabled; + Qx::Property startImportEnabled; + Qx::Property forceDownloadImagesEnabled; + Qx::Property editTagFilterEnabled; + Qx::Property launcherVersion; + Qx::Property flashpointVersion; + Qx::Bindable launcherPath; + Qx::Bindable flashpointPath; + Qx::Property launcherStatus; + Qx::Property flashpointStatus; + }; -//-Class Enums------------------------------------------------------------------------------------------------ +//-Inner Classes------------------------------------------------------------------------------------------------ private: - enum class InputStage {Paths, Imports}; - enum class InstallType {Frontend, Flashpoint}; + class SelectionList + { + static constexpr int USER_ROLE_EXISTING = 0x333; // Value chosen arbitrarily (must be > 0x100) + + QListWidget* mWidget; + QProperty mSelCount; + QProperty mExistSelCount; + + void handleCheckChange(QListWidgetItem* item); + + public: + SelectionList(QListWidget* widget); + + void fill(const QList& imps); + void clear(); + + int selectedCount() const; + int existingSelectedCount() const; + }; //-Class Variables-------------------------------------------------------------------------------------------- private: // UI Text static inline const QString REQUIRE_ELEV = u" [Run as Admin/Dev Mode]"_s; - // Messages - General - static inline const QString MSG_FATAL_NO_INTERNAL_CLIFP_VER = u"Failed to get version information from the internal copy of CLIFp.exe!\n" - "\n" - "Execution cannot continue."_s; // Messages - Help static inline const QString MSG_ABOUT = PROJECT_FULL_NAME + u"\n\nVersion: "_s + PROJECT_VERSION_STR; static inline const QString MSG_PLAYLIST_GAME_MODE_HELP = u"%1 - Games found in the selected playlists that are not part of any selected platform will be excluded.
" @@ -60,137 +95,69 @@ class MainWindow : public QMainWindow "games if this option is unchecked, but this may be useful for archival purposes or in case you later want to revert to a previous version of Flashpoint and maintain the entries personal " "metadata. Note that this option will also remove any games that are not covered by your current import selections (i.e. platforms, playlists, tag filter, etc.), even if they still remain " "in Flashpoint"_s; - static inline const QString MSG_IMAGE_MODE_HELP = u"%1 - All relevant images from Flashpoint will be fully copied into your frontend installation. This causes zero overhead but will require additional storage space proportional to " + static inline const QString MSG_IMAGE_MODE_HELP = u"%1 - All relevant images from Flashpoint will be fully copied into your launcher installation. This causes zero overhead but will require additional storage space proportional to " "the number of games you end up importing, up to double if all platforms are selected.
" "Space Consumption: High
" "Import Speed: Very Slow
" - "Frontend Access Speed: Fast
" + "Launcher Access Speed: Fast
" "
" - "%2 - Your frontend platform configuration will be altered so that the relevant image folders within Flashpoint are directly referenced by its media scanner, requiring no " + "%2 - Your launcher platform configuration will be altered so that the relevant image folders within Flashpoint are directly referenced by its media scanner, requiring no " "extra space and causing no overhead.
" "Space Consumption: None
" "Import Speed: Fast
" - ">Frontend Access Speed:Varies, but usually slow
" + ">Launcher Access Speed:Varies, but usually slow
" "
" - "%3 - A symbolic link to each relevant image from Flashpoint will be created in your frontend installation. These appear like the real files to the frontend, adding only a minuscule " + "%3 - A symbolic link to each relevant image from Flashpoint will be created in your launcher installation. These appear like the real files to the launcher, adding only a minuscule " "amount of overhead when it loads images and require almost no extra disk space to store.
" "Space Consumption: Near-zero
" "Import Speed: Slow
" - ">Frontend Access Speed: Fast
"_s; - - // Messages - Input - static inline const QString MSG_FE_INSTALL_INVALID = u"The specified directory either doesn't contain a valid frontend install, or it contains a version that is incompatible with this tool."_s; - static inline const QString MSG_FP_INSTALL_INVALID = u"The specified directory either doesn't contain a valid Flashpoint install, or it contains a version that is incompatible with this tool."_s; - static inline const QString MSG_FP_VER_NOT_TARGET = u"The selected Flashpoint install contains a version of Flashpoint that is different from the target version series (" PROJECT_TARGET_FP_VER_PFX_STR "), but appears to have a compatible structure. " - "You may proceed at your own risk as the tool is not guaranteed to work correctly in this circumstance. Please use a newer version of " PROJECT_SHORT_NAME " if available."_s; - - static inline const QString MSG_INSTALL_CONTENTS_CHANGED = u"The contents of your installs have been changed since the initial scan and therefore must be re-evaluated. You will need to make your selections again."_s; - - // Messages - General import procedure - static inline const QString MSG_PRE_EXISTING_IMPORT = u"One or more existing Platforms/Playlists may be affected by this import. These will be altered even if they did not originate from this program (i.e. if you " - "already happened to have a Platform/Playlist with the same name as one present in Flashpoint).\n" - "\n" - "Are you sure you want to proceed?"_s; - static inline const QString MSG_FRONTEND_CLOSE_PROMPT = u"The importer has detected that the selected frontend is running. It must be closed in order to continue. If recently closed, wait a few moments before trying to proceed again as it performs significant cleanup in the background."_s; - static inline const QString MSG_POST_IMPORT = u"The Flashpoint import has completed successfully. Next time you start the frontend it may take longer than usual as it may have to fill in some default fields for the imported Platforms/Playlists.\n" - "\n" - "If you wish to import further selections or update to a newer version of Flashpoint, simply re-run this procedure after pointing it to the desired Flashpoint installation."_s; - // Initial import status - static inline const QString STEP_FP_DB_INITIAL_QUERY = u"Making initial Flashpoint database queries..."_s; - - // Messages - FP General - static inline const QString MSG_FP_CLOSE_PROMPT = u"It is strongly recommended to close Flashpoint before proceeding as it can severely slow or interfere with the import process"_s; - - // Messages - FP Database read - static inline const QString MSG_FP_DB_MISSING_TABLE = u"The Flashpoint database is missing tables critical to the import process."_s; - static inline const QString MSG_FP_DB_TABLE_MISSING_COLUMN = u"The Flashpoint database tables are missing columns critical to the import process."_s; - static inline const QString MSG_FP_DB_UNEXPECTED_ERROR = u"An unexpected SQL error occurred while reading the Flashpoint database:"_s; - - // Messages - FP CLIFp - static inline const QString MSG_FP_CLFIP_WILL_DOWNGRADE = u"The existing version of "_s + CLIFp::EXE_NAME + u" in your Flashpoint install is newer than the version package with this tool.\n" - "\n" - "Replacing it with the packaged Version (downgrade) will likely cause compatibility issues unless you are specifically re-importing after downgrading your Flashpoint install to a previous version.\n" - "\n" - "Do you wish to downgrade "_s + CLIFp::EXE_NAME + u"?"_s; - - static inline const QString MSG_FP_CANT_DEPLOY_CLIFP = u"Failed to deploy "_s + CLIFp::EXE_NAME + u" to the selected Flashpoint install.\n" - "\n" - "%1\n" - "\n" - "If you choose to ignore this you will have to place CLIFp in your Flashpoint install directory manually."_s; - - // Messages - Import Result - static inline const QString MSG_HAVE_TO_REVERT = u"Due to previous unrecoverable errors, all changes that occurred during import will now be reverted (other than existing images that were replaced with newer versions).\n" - "\n" - "Afterwards, check to see if there is a newer version of " PROJECT_SHORT_NAME " and try again using that version. If not ask for help on the relevant forums where this tool was released (see Help).\n" - "\n" - "If you believe this to be due to a bug with this software, please submit an issue to its GitHub page (listed under help)"_s; - - static inline const QString MSG_USER_CANCELED = u"Import canceled by user, all changes that occurred during import will now be reverted (other than existing images that were replaced with newer versions)."_s; - static inline const QString MSG_NO_WORK = u"The provided import selections/options resulted in no tasks to perform. Double-check your settings."_s; + ">Launcher Access Speed: Fast
"_s; // Dialog captions - static inline const QString CAPTION_GENERAL_FATAL_ERROR = u"Fatal Error!"_s; - static inline const QString CAPTION_FRONTEND_BROWSE = u"Select the root directory of your frontend install..."_s; + static inline const QString CAPTION_LAUNCHER_BROWSE = u"Select the root directory of your launcher install..."_s; static inline const QString CAPTION_FLASHPOINT_BROWSE = u"Select the root directory of your Flashpoint install..."_s; static inline const QString CAPTION_PLAYLIST_GAME_MODE_HELP = u"Playlist game mode options"_s; static inline const QString CAPTION_UPDATE_MODE_HELP = u"Update mode options"_s; static inline const QString CAPTION_IMAGE_MODE_HELP = u"Image mode options"_s; - static inline const QString CAPTION_TASKLESS_IMPORT = u"Nothing to do"_s; - static inline const QString CAPTION_REVERT = u"Reverting changes..."_s; - static inline const QString CAPTION_CLIFP_ERR = u"Error deploying CLIFp"_s; - static inline const QString CAPTION_CLIFP_DOWNGRADE = u"Downgrade CLIFp?"_s; static inline const QString CAPTION_TAG_FILTER = u"Tag Filter"_s; // Menus - static inline const QString MENU_FE_HELP_OBJ_NAME_TEMPLATE = u"action_%1Help"_s; - static inline const QRegularExpression MENU_FE_HELP_KEY_REGEX = QRegularExpression(u"action_(?.*?)Help"_s); + static inline const QString MENU_LR_HELP_OBJ_NAME_TEMPLATE = u"action_%1Help"_s; + static inline const QRegularExpression MENU_LR_HELP_KEY_REGEX = QRegularExpression(u"action_(?.*?)Help"_s); // URLs static inline const QUrl URL_CLIFP_GITHUB = QUrl(u"https://github.com/oblivioncth/CLIFp"_s); static inline const QUrl URL_FIL_GITHUB = QUrl(u"https://github.com/oblivioncth/FIL"_s); - // Flashpoint version check - static inline const Qx::VersionNumber TARGET_FP_VERSION_PREFIX = Qx::VersionNumber::fromString(PROJECT_TARGET_FP_VER_PFX_STR); - - // Selection defaults - static inline const QList DEFAULT_IMAGE_MODE_ORDER = { - Fe::ImageMode::Link, - Fe::ImageMode::Reference, - Fe::ImageMode::Copy - }; - // User Roles static inline const int USER_ROLE_TAG_ID = 0x200; // Value chosen arbitrarily (must be > 0x100) + // Color + static inline QColor smExistingItemColor; + //-Instance Variables-------------------------------------------------------------------------------------------- private: - Ui::MainWindow *ui; - bool mInitCompleted; - - QHash> mWidgetEnableConditionMap; - QHash> mActionEnableConditionMap; - QColor mExistingItemColor; + Ui::MainWindow* ui; + SelectionList mPlatformSelections; + SelectionList mPlaylistSelections; + const Import::Properties& mImportProperties; - std::shared_ptr mFrontendInstall; - std::shared_ptr mFlashpointInstall; - - QHash mPlatformItemCheckStates; - QHash mPlaylistItemCheckStates; - std::unique_ptr mTagSelectionModel; - - bool mHasLinkPermissions = false; + Qx::Bimap mImageModeMap; + QHash mPlaylistGameModeMap; + QHash mUpdateModeMap; + Qx::StandardItemModel mTagModel; QString mArgedPlaylistGameModeHelp; QString mArgedUpdateModeHelp; QString mArgedImageModeHelp; - // Process monitoring - ProgressPresenter mProgressPresenter; + // Behavior + Bindings mBindings; + QList mBindingSubscribees; //-Constructor--------------------------------------------------------------------------------------------------- public: - MainWindow(QWidget *parent = nullptr); + MainWindow(const Import::Properties& importProperties, QWidget *parent = nullptr); //-Destructor---------------------------------------------------------------------------------------------------- public: @@ -198,68 +165,42 @@ class MainWindow : public QMainWindow //-Instance Functions-------------------------------------------------------------------------------------------- private: - bool testForLinkPermissions(); + // Init + Qx::Bimap initializeImageModeMap() const; + QHash initializePlaylistGameModeMap() const; + QHash initializeUpdateModeMap() const; + void initializeBindings(); void initializeForms(); - void initializeEnableConditionMaps(); - void initializeFrontendHelpActions(); - bool installMatchesTargetSeries(const Fp::Install& fpInstall); - void checkManualInstallInput(InstallType install); - void validateInstall(const QString& installPath, InstallType install); - void gatherInstallInfo(); - void populateImportSelectionBoxes(); - void generateTagSelectionOptions(); - bool parseFrontendData(); - bool installsHaveChanged(); - void redoInputChecks(); - - void invalidateInstall(InstallType install, bool informUser); - void clearListWidgets(); - bool isExistingPlatformSelected(); - bool isExistingPlaylistSelected(); - bool selectionsMayModify(); - - void postSqlError(const QString& mainText, const QSqlError& sqlError); - void postListError(const QString& mainText, const QStringList& detailedItems); - void postIOError(const QString& mainText, const Qx::IoOpReport& report); - - void refreshEnableStates(); - void refreshCheckStates(); + void initializeLauncherHelpActions(); + // Input querying + bool selectionsMayModify(); QStringList getSelectedPlatforms() const; QStringList getSelectedPlaylists() const; + Import::PlaylistGameMode getSelectedPlaylistGameMode() const; Fp::Db::InclusionOptions getSelectedInclusionOptions() const; - Fe::UpdateOptions getSelectedUpdateOptions() const; - Fe::ImageMode getSelectedImageMode() const; - ImportWorker::PlaylistGameMode getSelectedPlaylistGameMode() const; + Import::UpdateOptions getSelectedUpdateOptions() const; + Import::ImageMode getSelectedImageMode() const; bool getForceDownloadImages() const; + // Import void prepareImport(); - void revertAllFrontendChanges(); - void deployCLIFp(const Fp::Install& fp, QMessageBox::Button abandonButton); - void standaloneCLIFpDeploy(); + + // Tags (move to controller?) void showTagSelectionDialog(); QSet generateTagExlusionSet() const; -protected: - void showEvent(QShowEvent* event); - -public: - bool initCompleted(); - -//-Slots--------------------------------------------------------------------------------------------------------- +//-Signals & Slots---------------------------------------------------------------------------------------------------- private slots: // Direct UI, start with "all" to avoid Qt calling "connectSlotsByName" on these slots (slots that start with "on_") void all_on_action_triggered(); - void all_on_lineEdit_editingFinished(); void all_on_pushButton_clicked(); - void all_on_listWidget_itemChanged(QListWidgetItem* item); - void all_on_radioButton_clicked(); void all_on_menu_triggered(QAction *action); - // Import Exception Handling - void handleBlockingError(std::shared_ptr response, const Qx::Error& blockingError, QMessageBox::StandardButtons choices); - void handleAuthRequest(const QString& prompt, QAuthenticator* authenticator); - void handleImportResult(ImportWorker::ImportResult importResult, const Qx::Error& errorReport); +signals: + void installPathChanged(const QString& installPath, Import::Install type); + void importTriggered(Import::Selections sel, Import::OptionSet opt, bool mayModify); + void standaloneDeployTriggered(); }; #endif // MAINWINDOW_H diff --git a/app/src/ui/mainwindow.ui b/app/src/ui/mainwindow.ui index ba50b3f..2798c96 100644 --- a/app/src/ui/mainwindow.ui +++ b/app/src/ui/mainwindow.ui @@ -31,7 +31,7 @@ 9 - + 20 @@ -52,7 +52,7 @@ - + 0 @@ -87,16 +87,16 @@ - Qt::PlainText + Qt::TextFormat::PlainText - :/ui/No_Install.png + :/ui/No_Install.png true - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter @@ -124,7 +124,7 @@ - :/ui/No_Install.png + :/ui/No_Install.png true @@ -153,7 +153,7 @@ - + 1 @@ -228,9 +228,9 @@ 0 - + - Frontend Install: + Launcher Install: 0 @@ -238,7 +238,7 @@ - + true @@ -277,7 +277,7 @@ Platforms - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -296,7 +296,7 @@ - QAbstractItemView::NoSelection + QAbstractItemView::SelectionMode::NoSelection @@ -347,10 +347,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Preferred + QSizePolicy::Policy::Preferred @@ -368,7 +368,7 @@ Playlists - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -381,14 +381,14 @@ - QAbstractItemView::NoSelection + QAbstractItemView::SelectionMode::NoSelection - QLayout::SetDefaultConstraint + QLayout::SizeConstraint::SetDefaultConstraint @@ -450,7 +450,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -484,12 +484,9 @@ Help - + :/ui/Update_Mode_Info.png:/ui/Update_Mode_Info.png - - buttonGroup_imageMode - @@ -506,6 +503,9 @@ false + + buttonGroup_playlistGameMode + @@ -522,6 +522,9 @@ true + + buttonGroup_playlistGameMode + @@ -542,10 +545,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Expanding + QSizePolicy::Policy::Expanding @@ -579,7 +582,7 @@ Help - + :/ui/Update_Mode_Info.png:/ui/Update_Mode_Info.png @@ -604,6 +607,9 @@ true + + buttonGroup_updateMode + @@ -648,6 +654,9 @@ false + + buttonGroup_updateMode + @@ -777,7 +786,7 @@ Existing Item - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter @@ -836,7 +845,7 @@ Help - + :/ui/Update_Mode_Info.png:/ui/Update_Mode_Info.png @@ -844,7 +853,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -953,14 +962,14 @@ Help - + - Frontends + Launchers - + @@ -969,7 +978,7 @@ - + :/ui/Exit.png:/ui/Exit.png @@ -978,7 +987,7 @@ - + :/ui/GitHub.png:/ui/GitHub.png @@ -987,7 +996,7 @@ - + :/ui/GitHub.png:/ui/GitHub.png @@ -1005,7 +1014,7 @@ - + :/ui/CLIFp.png:/ui/CLIFp.png @@ -1057,7 +1066,7 @@ - + :/ui/About.png:/ui/About.png @@ -1066,7 +1075,7 @@ - + @@ -1086,7 +1095,7 @@ - pushButton_frontendBrowse + pushButton_launcherBrowse clicked() MainWindow all_on_pushButton_clicked() @@ -1101,38 +1110,6 @@ - - lineEdit_frontendPath - editingFinished() - MainWindow - all_on_lineEdit_editingFinished() - - - 170 - 94 - - - 399 - 299 - - - - - lineEdit_flashpointPath - editingFinished() - MainWindow - all_on_lineEdit_editingFinished() - - - 170 - 144 - - - 399 - 299 - - - pushButton_selectAll_platforms clicked() @@ -1197,22 +1174,6 @@ - - listWidget_platformChoices - itemChanged(QListWidgetItem*) - MainWindow - all_on_listWidget_itemChanged(QListWidgetItem*) - - - 125 - 336 - - - 244 - 333 - - - pushButton_exit clicked() @@ -1357,54 +1318,6 @@ - - listWidget_playlistChoices - itemChanged(QListWidgetItem*) - MainWindow - all_on_listWidget_itemChanged(QListWidgetItem*) - - - 486 - 354 - - - 323 - 479 - - - - - radioButton_selectedPlatformsOnly - clicked() - MainWindow - all_on_radioButton_clicked() - - - 219 - 568 - - - 323 - 479 - - - - - radioButton_forceAll - clicked() - MainWindow - all_on_radioButton_clicked() - - - 219 - 596 - - - 323 - 479 - - - action_editTagFilter triggered() @@ -1449,6 +1362,8 @@ all_on_menu_triggered(QAction*) + + diff --git a/app/src/ui/progresspresenter.cpp b/app/src/ui/progresspresenter.cpp index 5ff3c9e..595e01e 100644 --- a/app/src/ui/progresspresenter.cpp +++ b/app/src/ui/progresspresenter.cpp @@ -24,7 +24,6 @@ void ProgressPresenter::setupProgressDialog() // Initialize dialog mDialog.setCancelButtonText(u"Cancel"_s); mDialog.setWindowModality(Qt::WindowModal); - mDialog.setWindowTitle(CAPTION_IMPORTING); mDialog.setWindowFlags(mDialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); mDialog.setAutoReset(false); mDialog.setAutoClose(false); @@ -59,6 +58,8 @@ void ProgressPresenter::setBusyState() #endif } +void ProgressPresenter::setCaption(const QString& caption) { mDialog.setWindowTitle(caption); } + void ProgressPresenter::resetState() { #ifdef _WIN32 @@ -76,6 +77,8 @@ void ProgressPresenter::reset() #endif } +int ProgressPresenter::value() const { return mDialog.value(); } + //-Slots--------------------------------------------------------------------------------------------------------- //Public: void ProgressPresenter::setLabelText(const QString& text) { mDialog.setLabelText(text); } diff --git a/app/src/ui/progresspresenter.h b/app/src/ui/progresspresenter.h index 2ba9c46..1e5ed26 100644 --- a/app/src/ui/progresspresenter.h +++ b/app/src/ui/progresspresenter.h @@ -14,10 +14,6 @@ using namespace Qt::StringLiterals; class ProgressPresenter : public QObject { Q_OBJECT -//-Class Variables-------------------------------------------------------------------------------------------- -private: - static inline const QString CAPTION_IMPORTING = u"FP Import"_s; - //-Instance Variables-------------------------------------------------------------------------------------------- private: #ifdef _WIN32 @@ -37,8 +33,10 @@ class ProgressPresenter : public QObject void attachWindow(QWindow* window); void setErrorState(); void setBusyState(); + void setCaption(const QString& caption); void resetState(); void reset(); + int value() const; //-Slots--------------------------------------------------------------------------------------------------------- public slots: