Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge to master for v0.9.10 #82

Merged
merged 29 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1588793
Make ProcessBider cross platform
oblivioncth Nov 18, 2023
56c33f3
Allow all TitleCommand options for CPrepare
oblivioncth Nov 9, 2023
97c83a2
Allow multiple downloads with TDownload
oblivioncth Nov 9, 2023
014374f
Add CDownload, allows for batch downloading of datapacks from playlists
oblivioncth Nov 9, 2023
5f11711
Merge pull request #79 from oblivioncth/feature/download_command
oblivioncth Nov 9, 2023
ff05293
Use Command::requiredOptions() in CRun
oblivioncth Nov 9, 2023
5167993
Tweak Qt error message string
oblivioncth Nov 9, 2023
b57d94f
Bump Qx off dead commit
oblivioncth Nov 9, 2023
94ea0fa
Update libfp, use Fp::Toolkit
oblivioncth Nov 10, 2023
623c71d
CDownload check if pack exists before queing.
oblivioncth Nov 10, 2023
3717272
Update libfp
oblivioncth Nov 10, 2023
729b959
Use parsed data pack parameters
oblivioncth Nov 10, 2023
b57ea4c
Update libfp for Entry alias
oblivioncth Nov 12, 2023
a43a237
Refactor CPlay; more organized, no need to clear task queue
oblivioncth Nov 12, 2023
d73d62f
Use Fp::Entry alias where possible
oblivioncth Nov 12, 2023
7848c43
Implement handling of '-server' mount parameter
oblivioncth Nov 12, 2023
95c820b
Implement handling of '-extracted' mount parameter
oblivioncth Nov 12, 2023
afb6384
Handle libfp root path access change
oblivioncth Nov 12, 2023
dc52faf
Update libfp
oblivioncth Nov 17, 2023
5b925c7
Normalize '--extracted' mount parameter slashes before check
oblivioncth Nov 17, 2023
8c8de04
Fix superfluous search repeats in TExec::escapeForShell()
oblivioncth Nov 18, 2023
ac4c5ad
De-quote fully quoted arguments when it's unnecessary
oblivioncth Nov 18, 2023
53794f0
Change :browser_mode: override to Chrome
oblivioncth Nov 18, 2023
8672255
Merge pull request #80 from oblivioncth/cross_platform_bider
oblivioncth Nov 19, 2023
d8e2293
Add companion mode: CLIFp piggybacks off the Launcher's services
oblivioncth Nov 19, 2023
729cb95
Merge pull request #81 from oblivioncth/feature/companion_mode
oblivioncth Nov 23, 2023
b570f56
Add core logging helpers to reduce 'NAME' usage.
oblivioncth Nov 23, 2023
7ea6207
Update README.md
oblivioncth Nov 26, 2023
68a811c
Bump
oblivioncth Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ cmake_minimum_required(VERSION 3.24.0...3.26.0)
# Project
# NOTE: DON'T USE TRAILING ZEROS IN VERSIONS
project(CLIFp
VERSION 0.9.9.1
VERSION 0.9.10
LANGUAGES CXX
DESCRIPTION "Command-line Interface for Flashpoint Archive"
)

# Get helper scripts
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FetchOBCMake.cmake)
fetch_ob_cmake("v0.3.3")
fetch_ob_cmake("v0.3.4")

# Initialize project according to standard rules
include(OB/Project)
Expand Down Expand Up @@ -72,14 +72,14 @@ endif()

include(OB/FetchQx)
ob_fetch_qx(
REF "v0.5.5.1"
REF "v0.5.6"
COMPONENTS
${CLIFP_QX_COMPONENTS}
)

# Fetch libfp (build and import from source)
include(OB/Fetchlibfp)
ob_fetch_libfp("v0.5.1.1")
ob_fetch_libfp("v0.5.2")

# Fetch QI-QMP (build and import from source)
include(OB/FetchQI-QMP)
Expand Down
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ Update:
## Compatability

### General
All Flashpoint features are generally supported, other than editing configuration files and user data (like playlists) and querying title meta-data through the command-line, and searching is more limited. See the [All Commands/Options](#all-commandsoptions) section for more information.

Most flashpoint features are supported. The regular launcher still must be used for the following:
- Changing user configuration, like preferences, playlists, etc.
- Updating the launcher and downloading game updates

See the [All Commands/Options](#all-commandsoptions) section for more information.

While constantly testing for complete compatibility is infeasible given the size of Flashpoint, CLIFp was designed with full compatibility in mind and theoretically is 100% compatible with the Flashpoint collection.

Expand Down Expand Up @@ -105,7 +110,7 @@ Or if feeling spontaneous, use the **-r** switch, followed by a library filter t
See the [All Commands/Options](#all-commandsoptions) section for more information.

**Direct Execution:**
The legacy approach is to use the **run** command with the **--app** and **--param** switches. This will start Flashpoint's webserver and then start the application specified with the provided parameters:
The legacy approach is to use the **run** command with the **--app** and **--param** switches. This will start Flashpoint's services and then start the application specified with the provided parameters:

CLIFp run --app="FPSoftware\Flash\flashplayer_32_sa.exe" --param="http://www.mowa.org/work/buttons/buttons_art/basic.swf"

Expand All @@ -114,6 +119,7 @@ If the application needs to use files from a Data Pack that pack will need to be
The applications and arguments that are used for each game/animation can be found within the Flashpoint database ([FP Install Dir]\Data\flashpoint.sqlite)

### Flashpoint Protocol

CLIFp supports the "flashpoint" protocol, which means it can launch titles through URL with a custom scheme, followed by a title's UUID, like this:

flashpoint://37e5c215-9c39-4a3d-9912-b4343a17027e
Expand All @@ -135,6 +141,12 @@ If for whatever reason the service through which you wish to share a link does n
> [!IMPORTANT]
> You will want to disable the "Register As Protocol Handler" option in the default launcher or else it will replace CLIFp as the "flashpoint" protocol handler every time it's started.

### Companion Mode

It is recommended to only use CLIFp when the regular launcher isn't running as it allows fully independent operation since it can start and stop required services on its own; however, CLIFp can be started while the standard launcher is running, in which case it will run in "Companion Mode" and utilize the launcher's services instead.

The catch with this mode is that CLIFp will be required to shutdown if at any point the standard launcher is closed.

## All Commands/Options

Most options have short and long forms, which are interchangeable. For options that take a value, a space or **=** can be used between the option and its value, i.e.
Expand Down Expand Up @@ -171,6 +183,14 @@ The **-title-strict** and **-subtitle-strict** options only consider exact match
Tip: You can use **-subtitle** with an empty string (i.e. `-s ""`) to see all of the additional-apps for a given title.

### Command List:

**download** - Downloads data packs for games that require them in bulk

Options:
- **-p | --playlist** Name of the playlist to download games for.

--------------------------------------------------------------------------------

**link** - Creates a shortcut to a Flashpoint title

Options:
Expand Down Expand Up @@ -199,7 +219,7 @@ Options:

--------------------------------------------------------------------------------

**run** - Start Flashpoint's webserver and then execute the provided application
**run** - Start Flashpoint's services and then execute the provided application

Options:
- **-a | --app:** Relative (to Flashpoint Directory) path of application to launch
Expand Down
4 changes: 2 additions & 2 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ set(CLIFP_SOURCE
kernel/errorstatus.cpp
command/command.h
command/command.cpp
command/c-download.h
command/c-download.cpp
command/c-link.h
command/c-link.cpp
command/c-play.h
Expand Down Expand Up @@ -88,8 +90,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL Windows)
task/t-exec_win.cpp
task/t-bideprocess.h
task/t-bideprocess.cpp
tools/processbider.h
tools/processbider.cpp
)

list(APPEND CLIFP_LINKS
Expand Down
123 changes: 123 additions & 0 deletions app/src/command/c-download.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Unit Include
#include "c-download.h"

// Project Includes
#include "task/t-download.h"
#include "task/t-generic.h"

//===============================================================================================================
// CDownloadError
//===============================================================================================================

//-Constructor-------------------------------------------------------------
//Private:
CDownloadError::CDownloadError(Type t, const QString& s) :
mType(t),
mSpecific(s)
{}

//-Instance Functions-------------------------------------------------------------
//Public:
bool CDownloadError::isValid() const { return mType != NoError; }
QString CDownloadError::specific() const { return mSpecific; }
CDownloadError::Type CDownloadError::type() const { return mType; }

//Private:
Qx::Severity CDownloadError::deriveSeverity() const { return Qx::Critical; }
quint32 CDownloadError::deriveValue() const { return mType; }
QString CDownloadError::derivePrimary() const { return ERR_STRINGS.value(mType); }
QString CDownloadError::deriveSecondary() const { return mSpecific; }

//===============================================================================================================
// CDownload
//===============================================================================================================

//-Constructor-------------------------------------------------------------
//Public:
CDownload::CDownload(Core& coreRef) : Command(coreRef) {}

//-Instance Functions-------------------------------------------------------------
//Protected:
QList<const QCommandLineOption*> CDownload::options() const { return CL_OPTIONS_SPECIFIC + Command::options(); }
QSet<const QCommandLineOption*> CDownload::requiredOptions() const { return CL_OPTIONS_REQUIRED + Command::requiredOptions(); }
QString CDownload::name() const { return NAME; }

Qx::Error CDownload::perform()
{
QString playlistName = mParser.value(CL_OPTION_PLAYLIST).trimmed();
mCore.setStatus(STATUS_DOWNLOAD, playlistName);

Fp::Db* db = mCore.fpInstall().database();
Fp::PlaylistManager* pm = mCore.fpInstall().playlistManager();
if(Qx::Error pError = pm->populate(); pError.isValid())
return pError;

// Find playlist
QList<Fp::Playlist> playlists = pm->playlists();
auto pItr = std::find_if(playlists.cbegin(), playlists.cend(), [&playlistName](auto p){
return p.title() == playlistName || p.title().trimmed() == playlistName; // Some playlists have spaces for sorting purposes
});

if(pItr == playlists.cend())
{
CDownloadError err(CDownloadError::InvalidPlaylist, playlistName);
postError(err);
return err;
}
logEvent(LOG_EVENT_PLAYLIST_MATCH.arg(pItr->id().toString(QUuid::WithoutBraces)));

// Queue downloads for each game
TDownload* downloadTask = new TDownload(&mCore);
downloadTask->setStage(Task::Stage::Primary);
downloadTask->setDescription(u"playlist data packs"_s);
QList<int> dataIds;

const Fp::Toolkit* tk = mCore.fpInstall().toolkit();
for(const auto& pg : pItr->playlistGames())
{
// Get data
Fp::GameData gameData;
if(Fp::DbError gdErr = db->getGameData(gameData, pg.gameId()); gdErr.isValid())
{
postError(gdErr);
return gdErr;
}

if(gameData.isNull())
{
logEvent(LOG_EVENT_NON_DATAPACK.arg(pg.gameId().toString(QUuid::WithoutBraces)));
continue;
}

if(tk->datapackIsPresent(gameData))
continue;

// Queue download
downloadTask->addFile({.target = tk->datapackUrl(gameData), .dest = tk->datapackPath(gameData), .checksum = gameData.sha256()});

// Note data id
dataIds.append(gameData.id());
}

if(downloadTask->isEmpty())
{
logEvent(LOG_EVENT_NO_OP);
return CDownloadError();
}

// Enqueue download task
mCore.enqueueSingleTask(downloadTask);

// Enqueue onDiskState update task
Core* corePtr = &mCore; // Safe, will outlive task
TGeneric* onDiskUpdateTask = new TGeneric(corePtr);
onDiskUpdateTask->setStage(Task::Stage::Primary);
onDiskUpdateTask->setDescription(u"Update GameData onDisk state."_s);
onDiskUpdateTask->setAction([dataIds, corePtr]{
return corePtr->fpInstall().database()->updateGameDataOnDiskState(dataIds, true);
});
mCore.enqueueSingleTask(onDiskUpdateTask);

// Return success
return CDownloadError();
}
90 changes: 90 additions & 0 deletions app/src/command/c-download.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#ifndef CDOWNLOAD_H
#define CDOWNLOAD_H

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

// Project Includes
#include "command/command.h"

class QX_ERROR_TYPE(CDownloadError, "CDownloadError", 1217)
{
friend class CDownload;
//-Class Enums-------------------------------------------------------------
public:
enum Type
{
NoError,
InvalidPlaylist
};

//-Class Variables-------------------------------------------------------------
private:
static inline const QHash<Type, QString> ERR_STRINGS{
{NoError, u""_s},
{InvalidPlaylist, u""_s}
};

//-Instance Variables-------------------------------------------------------------
private:
Type mType;
QString mSpecific;

//-Constructor-------------------------------------------------------------
private:
CDownloadError(Type t = NoError, const QString& s = {});

//-Instance Functions-------------------------------------------------------------
public:
bool isValid() const;
Type type() const;
QString specific() const;

private:
Qx::Severity deriveSeverity() const override;
quint32 deriveValue() const override;
QString derivePrimary() const override;
QString deriveSecondary() const override;
};

class CDownload : public Command
{
//-Class Variables------------------------------------------------------------------------------------------------------
private:
// Status
static inline const QString STATUS_DOWNLOAD = u"Downloading data packs"_s;

// Logging
static inline const QString LOG_EVENT_PLAYLIST_MATCH = u"Playlist matches ID: %1"_s;
static inline const QString LOG_EVENT_NON_DATAPACK = u"Game %1 does not use a data pack."_s;
static inline const QString LOG_EVENT_NO_OP = u"No datapacks to download."_s;

// Command line option strings
static inline const QString CL_OPT_PLAYLIST_S_NAME = u"p"_s;
static inline const QString CL_OPT_PLAYLIST_L_NAME = u"playlist"_s;
static inline const QString CL_OPT_PLAYLIST_DESC = u"Name of the playlist to download games for."_s;

// Command line options
static inline const QCommandLineOption CL_OPTION_PLAYLIST{{CL_OPT_PLAYLIST_S_NAME, CL_OPT_PLAYLIST_L_NAME}, CL_OPT_PLAYLIST_DESC, u"playlist"_s}; // Takes value
static inline const QList<const QCommandLineOption*> CL_OPTIONS_SPECIFIC{&CL_OPTION_PLAYLIST};
static inline const QSet<const QCommandLineOption*> CL_OPTIONS_REQUIRED{&CL_OPTION_PLAYLIST};

public:
// Meta
static inline const QString NAME = u"download"_s;
static inline const QString DESCRIPTION = u"Download game data packs in bulk"_s;

//-Constructor----------------------------------------------------------------------------------------------------------
public:
CDownload(Core& coreRef);

//-Instance Functions------------------------------------------------------------------------------------------------------
protected:
QList<const QCommandLineOption*> options() const override;
QSet<const QCommandLineOption*> requiredOptions() const override;
QString name() const override;
Qx::Error perform() override;
};
REGISTER_COMMAND(CDownload::NAME, CDownload, CDownload::DESCRIPTION);

#endif // CDOWNLOAD_H
Loading
Loading