Skip to content

Commit

Permalink
Merge pull request #82 from oblivioncth/dev
Browse files Browse the repository at this point in the history
Merge to master for v0.9.10
  • Loading branch information
oblivioncth authored Nov 27, 2023
2 parents 0f48e0f + 68a811c commit ec6ff16
Show file tree
Hide file tree
Showing 54 changed files with 1,127 additions and 1,047 deletions.
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

0 comments on commit ec6ff16

Please sign in to comment.