Replaces the initialization dialog with a wizard (#147)

This commit is contained in:
Putta Khunchalee 2023-02-17 01:47:18 +07:00 committed by GitHub
parent f0cc04d0ca
commit 27a25a297d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 376 additions and 178 deletions

View file

@ -33,7 +33,7 @@ add_executable(obliteration WIN32
game_models.cpp
game_settings.cpp
game_settings_dialog.cpp
initialize_dialog.cpp
initialize_wizard.cpp
log_formatter.cpp
main.cpp
main_window.cpp

View file

@ -1,136 +0,0 @@
#include "initialize_dialog.hpp"
#include "settings.hpp"
#include <QDialogButtonBox>
#include <QDir>
#include <QFileDialog>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
InitializeDialog::InitializeDialog() :
m_systemDirectory(nullptr),
m_gamesDirectory(nullptr)
{
auto layout = new QVBoxLayout(this);
layout->addLayout(setupSettings());
layout->addStretch();
layout->addWidget(setupDialogActions());
setWindowTitle("Initialize Obliteration");
}
InitializeDialog::~InitializeDialog()
{
}
void InitializeDialog::browseSystemDirectory()
{
auto path = QFileDialog::getExistingDirectory(this, "Location to install system files");
if (!path.isEmpty()) {
m_systemDirectory->setText(QDir::toNativeSeparators(path));
}
}
void InitializeDialog::browseGamesDirectory()
{
auto path = QFileDialog::getExistingDirectory(this, "Location to install games");
if (!path.isEmpty()) {
m_gamesDirectory->setText(QDir::toNativeSeparators(path));
}
}
QLayout *InitializeDialog::setupSettings()
{
auto layout = new QFormLayout();
layout->setLabelAlignment(Qt::AlignRight);
layout->addRow("Path to install system files:", setupSystemDirectory());
layout->addRow("Path to install games:", setupGamesDirectory());
return layout;
}
QLayout *InitializeDialog::setupSystemDirectory()
{
auto layout = new QHBoxLayout();
// Input.
m_systemDirectory = new QLineEdit();
m_systemDirectory->setText(readSystemDirectorySetting());
m_systemDirectory->setMinimumWidth(400);
layout->addWidget(m_systemDirectory);
// Browse button.
auto browse = new QPushButton("...");
connect(browse, &QPushButton::clicked, this, &InitializeDialog::browseSystemDirectory);
layout->addWidget(browse);
return layout;
}
QLayout *InitializeDialog::setupGamesDirectory()
{
auto layout = new QHBoxLayout();
// Input.
m_gamesDirectory = new QLineEdit();
m_gamesDirectory->setText(readGamesDirectorySetting());
m_gamesDirectory->setMinimumWidth(400);
layout->addWidget(m_gamesDirectory);
// Browse button.
auto browse = new QPushButton("...");
connect(browse, &QPushButton::clicked, this, &InitializeDialog::browseGamesDirectory);
layout->addWidget(browse);
return layout;
}
QWidget *InitializeDialog::setupDialogActions()
{
auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(actions, &QDialogButtonBox::accepted, this, &InitializeDialog::save);
connect(actions, &QDialogButtonBox::rejected, this, &InitializeDialog::reject);
return actions;
}
void InitializeDialog::save()
{
// Check systen directory.
auto systemDirectory = m_systemDirectory->text();
if (systemDirectory.isEmpty() || !QDir(systemDirectory).exists() || !QDir::isAbsolutePath(systemDirectory)) {
QMessageBox::critical(this, "Error", "The value for location to install system files is not valid.");
return;
}
// Check games directory.
auto gamesDirectory = m_gamesDirectory->text();
if (gamesDirectory.isEmpty() || !QDir(gamesDirectory).exists() || !QDir::isAbsolutePath(gamesDirectory)) {
QMessageBox::critical(this, "Error", "The value for location to install games is not valid.");
return;
}
// Write settings and close dialog.
writeSystemDirectorySetting(QDir::toNativeSeparators(systemDirectory));
writeGamesDirectorySetting(QDir::toNativeSeparators(gamesDirectory));
accept();
}

View file

@ -1,27 +0,0 @@
#pragma once
#include <QDialog>
class QLayout;
class QLineEdit;
class InitializeDialog final : public QDialog {
public:
InitializeDialog();
~InitializeDialog();
private slots:
void browseSystemDirectory();
void browseGamesDirectory();
private:
QLayout *setupSettings();
QLayout *setupSystemDirectory();
QLayout *setupGamesDirectory();
QWidget *setupDialogActions();
void save();
private:
QLineEdit *m_systemDirectory;
QLineEdit *m_gamesDirectory;
};

328
src/initialize_wizard.cpp Normal file
View file

@ -0,0 +1,328 @@
#include "initialize_wizard.hpp"
#include "settings.hpp"
#include "system.hpp"
#include <QDir>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWizardPage>
#define FIELD_SYSTEM_LOCATION "systemLocation"
#define FIELD_GAMES_LOCATION "gamesLocation"
enum PageId {
PageIntro,
PageSystem,
PageGame,
PageFirmware,
PageConclusion
};
class IntroPage : public QWizardPage {
public:
IntroPage()
{
auto layout = new QVBoxLayout();
// Page properties.
setTitle("Introduction");
// Introduction.
auto intro = new QLabel("This wizard will help you setup Obliteration.");
layout->addWidget(intro);
setLayout(layout);
}
};
class SystemPage : public QWizardPage {
public:
SystemPage() : m_input(nullptr)
{
auto layout = new QVBoxLayout();
// Page properties.
setTitle("Location for system files");
setSubTitle("The selected directory will be using for any PS4 data like save game data and firmware files.");
// Widgets.
layout->addLayout(setupInputRow());
setLayout(layout);
}
bool validatePage() override
{
auto path = m_input->text();
if (!QDir::isAbsolutePath(path)) {
QMessageBox::critical(this, "Error", "The location must be an absolute path.");
return false;
}
if (!QDir(path).exists()) {
QMessageBox::critical(this, "Error", "The location does not exists.");
return false;
}
return true;
}
private:
QLayout *setupInputRow()
{
auto layout = new QHBoxLayout();
// Label.
auto label = new QLabel("&Location:");
layout->addWidget(label);
// Input.
m_input = new QLineEdit();
m_input->setText(readSystemDirectorySetting());
label->setBuddy(m_input);
layout->addWidget(m_input);
registerField(FIELD_SYSTEM_LOCATION "*", m_input);
// Browse.
auto browse = new QPushButton("...");
connect(browse, &QPushButton::clicked, this, &SystemPage::browseDirectory);
layout->addWidget(browse);
return layout;
}
void browseDirectory()
{
auto path = QFileDialog::getExistingDirectory(this, "Location for system files");
if (!path.isEmpty()) {
m_input->setText(QDir::toNativeSeparators(path));
}
}
private:
QLineEdit *m_input;
};
class GamePage : public QWizardPage {
public:
GamePage() : m_input(nullptr)
{
auto layout = new QVBoxLayout();
// Page properties.
setTitle("Location to install games");
setSubTitle("The selected directory will be using for game installation. Cannot be the same as system files and must be an empty directory.");
// Widgets.
layout->addLayout(setupInputRow());
setLayout(layout);
}
bool validatePage() override
{
auto path = m_input->text();
if (!QDir::isAbsolutePath(path)) {
QMessageBox::critical(this, "Error", "The specified location must be an absolute path.");
return false;
}
if (!QDir(path).exists()) {
QMessageBox::critical(this, "Error", "The specified location does not exists.");
return false;
}
if (path == field(FIELD_SYSTEM_LOCATION).toString()) {
QMessageBox::critical(this, "Error", "The specified location cannot be the same location as system files.");
return false;
}
return true;
}
private:
QLayout *setupInputRow()
{
auto layout = new QHBoxLayout();
// Label.
auto label = new QLabel("&Location:");
layout->addWidget(label);
// Input.
m_input = new QLineEdit();
m_input->setText(readGamesDirectorySetting());
label->setBuddy(m_input);
layout->addWidget(m_input);
registerField(FIELD_GAMES_LOCATION "*", m_input);
// Browse button.
auto browse = new QPushButton("...");
connect(browse, &QPushButton::clicked, this, &GamePage::browseDirectory);
layout->addWidget(browse);
return layout;
}
void browseDirectory()
{
auto path = QFileDialog::getExistingDirectory(this, "Location to install games");
if (!path.isEmpty()) {
m_input->setText(QDir::toNativeSeparators(path));
}
}
private:
QLineEdit *m_input;
};
class FirmwarePage : public QWizardPage {
public:
FirmwarePage() : m_completed(false)
{
auto layout = new QVBoxLayout();
// Page properties.
setTitle("Install firmware");
setSubTitle("Obliteration required some firmware files from PS4 in order to work. You need to install those files before you can use Obliteration.");
// Install button.
auto install = new QPushButton("Install firmware...");
connect(install, &QPushButton::clicked, this, &FirmwarePage::install);
layout->addStretch();
layout->addWidget(install, 0, Qt::AlignHCenter);
layout->addStretch();
setLayout(layout);
}
bool isComplete() const {
return m_completed;
}
private:
void install()
{
// Get system path.
auto wizard = this->wizard();
auto systemPath = wizard->hasVisitedPage(PageSystem)
? field(FIELD_SYSTEM_LOCATION).toString()
: readSystemDirectorySetting();
// Install.
m_completed = updateSystemFiles(systemPath, this);
emit completeChanged();
// Move to next page automatically if installation was completed successfully.
if (m_completed) {
wizard->next();
}
}
private:
bool m_completed;
};
class ConclusionPage : public QWizardPage {
public:
ConclusionPage()
{
auto layout = new QVBoxLayout();
// Page properties.
setTitle("Setup completed");
// Introduction.
auto intro = new QLabel("You can now install your games and play it with Obliteration.");
layout->addWidget(intro);
setLayout(layout);
}
bool validatePage() override
{
auto wizard = this->wizard();
if (wizard->hasVisitedPage(PageSystem)) {
auto path = field(FIELD_SYSTEM_LOCATION).toString();
writeSystemDirectorySetting(QDir::toNativeSeparators(path));
}
if (wizard->hasVisitedPage(PageGame)) {
auto path = field(FIELD_GAMES_LOCATION).toString();
writeGamesDirectorySetting(QDir::toNativeSeparators(path));
}
return true;
}
};
InitializeWizard::InitializeWizard()
{
// Window properties.
setWindowTitle("Setup Obliteration");
// Pages.
setPage(PageIntro, new IntroPage());
setPage(PageSystem, new SystemPage());
setPage(PageGame, new GamePage());
setPage(PageFirmware, new FirmwarePage());
setPage(PageConclusion, new ConclusionPage());
}
InitializeWizard::~InitializeWizard()
{
}
int InitializeWizard::nextId() const
{
switch (currentId()) {
case PageIntro:
if (!hasSystemDirectorySetting()) {
return PageSystem;
}
Q_FALLTHROUGH();
case PageSystem:
if (!hasGamesDirectorySetting()) {
return PageGame;
}
Q_FALLTHROUGH();
case PageGame:
if (hasVisitedPage(PageSystem)) {
// No system path has been configured before.
if (!hasSystemFilesInstalled(field(FIELD_SYSTEM_LOCATION).toString())) {
return PageFirmware;
}
} else if (!hasSystemFilesInstalled()) {
return PageFirmware;
}
Q_FALLTHROUGH();
case PageFirmware:
return PageConclusion;
case PageConclusion:
default:
return -1;
}
}

12
src/initialize_wizard.hpp Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include <QWizard>
class InitializeWizard : public QWizard {
public:
InitializeWizard();
~InitializeWizard();
public:
int nextId() const override;
};

View file

@ -1,4 +1,4 @@
#include "initialize_dialog.hpp"
#include "initialize_wizard.hpp"
#include "main_window.hpp"
#include "settings.hpp"
#include "system.hpp"
@ -26,22 +26,15 @@ int main(int argc, char *argv[])
set_darkmode();
#endif
// Initialize user settings.
if (!hasRequiredUserSettings()) {
InitializeDialog init;
// Check if no any required settings.
if (!hasRequiredUserSettings() || !hasSystemFilesInstalled()) {
InitializeWizard init;
if (!init.exec()) {
return 1;
}
}
// Install system files.
if (!hasSystemFilesInstalled()) {
if (!updateSystemFiles(nullptr)) {
return 1;
}
}
// Run main window.
MainWindow win;

View file

@ -11,10 +11,15 @@ namespace UserSettings {
#define scope(name) QSettings s; s.beginGroup(name)
bool hasRequiredUserSettings()
{
return hasSystemDirectorySetting() && hasGamesDirectorySetting();
}
bool hasSystemDirectorySetting()
{
scope(SettingGroups::user);
return s.contains(UserSettings::systemDirectory) && s.contains(UserSettings::gamesDirectory);
return s.contains(UserSettings::systemDirectory);
}
QString readSystemDirectorySetting()
@ -33,6 +38,13 @@ void writeSystemDirectorySetting(const QString &v)
s.setValue(UserSettings::systemDirectory, v);
}
bool hasGamesDirectorySetting()
{
scope(SettingGroups::user);
return s.contains(UserSettings::gamesDirectory);
}
QString readGamesDirectorySetting()
{
scope(SettingGroups::user);

View file

@ -4,9 +4,11 @@
bool hasRequiredUserSettings();
bool hasSystemDirectorySetting();
QString readSystemDirectorySetting();
void writeSystemDirectorySetting(const QString &v);
bool hasGamesDirectorySetting();
QString readGamesDirectorySetting();
void writeGamesDirectorySetting(const QString &v);

View file

@ -11,7 +11,12 @@
bool hasSystemFilesInstalled()
{
auto libkernel = toPath(readSystemDirectorySetting());
return hasSystemFilesInstalled(readSystemDirectorySetting());
}
bool hasSystemFilesInstalled(const QString &systemPath)
{
auto libkernel = toPath(systemPath);
try {
libkernel /= STR("system");
@ -26,6 +31,11 @@ bool hasSystemFilesInstalled()
}
bool updateSystemFiles(QWidget *parent)
{
return updateSystemFiles(readSystemDirectorySetting(), parent);
}
bool updateSystemFiles(const QString &systemPath, QWidget *parent)
{
// Browse for PS4UPDATE1.PUP.dec.
auto pupPath = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, "Install PS4UPDATE1.PUP.dec", QString(), "PS4UPDATE1.PUP.dec")).toStdString();
@ -47,7 +57,7 @@ bool updateSystemFiles(QWidget *parent)
}
// Dump system image.
auto output = joinPath(readSystemDirectorySetting(), "system");
auto output = joinPath(systemPath, "system");
error = pup_dump_system(pup, output.c_str(), [](const char *name, std::uint64_t total, std::uint64_t written, void *ud) {
auto toProgress = [total](std::uint64_t v) -> int {
if (total >= 1024UL*1024UL*1024UL*1024UL) { // >= 1TB

View file

@ -1,6 +1,10 @@
#pragma once
#include <QString>
class QWidget;
bool hasSystemFilesInstalled();
bool hasSystemFilesInstalled(const QString &systemPath);
bool updateSystemFiles(QWidget *parent = nullptr);
bool updateSystemFiles(const QString &systemPath, QWidget *parent = nullptr);