mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-03 14:48:46 +00:00
Merge branch 'shadps4-emu:main' into fix-bb-lighting
This commit is contained in:
commit
f5002f8010
13 changed files with 177 additions and 78 deletions
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
@ -9,6 +9,10 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "*" ]
|
branches: [ "*" ]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ci-${{ github.event_name }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.event_name == 'push' }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ sudo apt install build-essential clang git cmake libasound2-dev libpulse-dev lib
|
||||||
|
|
||||||
#### Fedora
|
#### Fedora
|
||||||
```
|
```
|
||||||
sudo dnf install clang cmake libatomic alsa-lib-devel pipewire-jack-audio-connection-kit-devel openal-devel openssl-devel libevdev-devel libudev-devel qt6-qtbase-devel qt6-qtbase-private-devel qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel vulkan-devel vulkan-validation-layers
|
sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-connection-kit-devel openal-devel openssl-devel libevdev-devel libudev-devel libXext-devel qt6-qtbase-devel qt6-qtbase-private-devel qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel vulkan-devel vulkan-validation-layers
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Arch Linux
|
#### Arch Linux
|
||||||
|
|
|
@ -100,6 +100,7 @@ s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
|
||||||
if (ptr == nullptr) {
|
if (ptr == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
lock.unlock();
|
||||||
// TODO mixing channels
|
// TODO mixing channels
|
||||||
SDL_bool result = SDL_PutAudioStreamData(
|
SDL_bool result = SDL_PutAudioStreamData(
|
||||||
port.stream, ptr, port.samples_num * port.sample_size * port.channels_num);
|
port.stream, ptr, port.samples_num * port.sample_size * port.channels_num);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <fmt/xchar.h> // for wstring support
|
#include <fmt/xchar.h> // for wstring support
|
||||||
#include <toml.hpp>
|
#include <toml.hpp>
|
||||||
#include "common/logging/formatter.h"
|
#include "common/logging/formatter.h"
|
||||||
|
#include "common/path_util.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
namespace toml {
|
namespace toml {
|
||||||
|
@ -59,6 +60,7 @@ static bool vkCrashDiagnostic = false;
|
||||||
|
|
||||||
// Gui
|
// Gui
|
||||||
std::filesystem::path settings_install_dir = {};
|
std::filesystem::path settings_install_dir = {};
|
||||||
|
std::filesystem::path settings_addon_install_dir = {};
|
||||||
u32 main_window_geometry_x = 400;
|
u32 main_window_geometry_x = 400;
|
||||||
u32 main_window_geometry_y = 400;
|
u32 main_window_geometry_y = 400;
|
||||||
u32 main_window_geometry_w = 1280;
|
u32 main_window_geometry_w = 1280;
|
||||||
|
@ -299,6 +301,9 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
|
||||||
void setGameInstallDir(const std::filesystem::path& dir) {
|
void setGameInstallDir(const std::filesystem::path& dir) {
|
||||||
settings_install_dir = dir;
|
settings_install_dir = dir;
|
||||||
}
|
}
|
||||||
|
void setAddonInstallDir(const std::filesystem::path& dir) {
|
||||||
|
settings_addon_install_dir = dir;
|
||||||
|
}
|
||||||
void setMainWindowTheme(u32 theme) {
|
void setMainWindowTheme(u32 theme) {
|
||||||
mw_themes = theme;
|
mw_themes = theme;
|
||||||
}
|
}
|
||||||
|
@ -355,6 +360,13 @@ u32 getMainWindowGeometryH() {
|
||||||
std::filesystem::path getGameInstallDir() {
|
std::filesystem::path getGameInstallDir() {
|
||||||
return settings_install_dir;
|
return settings_install_dir;
|
||||||
}
|
}
|
||||||
|
std::filesystem::path getAddonInstallDir() {
|
||||||
|
if (settings_addon_install_dir.empty()) {
|
||||||
|
// Default for users without a config file or a config file from before this option existed
|
||||||
|
return Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "addcont";
|
||||||
|
}
|
||||||
|
return settings_addon_install_dir;
|
||||||
|
}
|
||||||
u32 getMainWindowTheme() {
|
u32 getMainWindowTheme() {
|
||||||
return mw_themes;
|
return mw_themes;
|
||||||
}
|
}
|
||||||
|
@ -482,6 +494,7 @@ void load(const std::filesystem::path& path) {
|
||||||
m_window_size_W = toml::find_or<int>(gui, "mw_width", 0);
|
m_window_size_W = toml::find_or<int>(gui, "mw_width", 0);
|
||||||
m_window_size_H = toml::find_or<int>(gui, "mw_height", 0);
|
m_window_size_H = toml::find_or<int>(gui, "mw_height", 0);
|
||||||
settings_install_dir = toml::find_fs_path_or(gui, "installDir", {});
|
settings_install_dir = toml::find_fs_path_or(gui, "installDir", {});
|
||||||
|
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
|
||||||
main_window_geometry_x = toml::find_or<int>(gui, "geometry_x", 0);
|
main_window_geometry_x = toml::find_or<int>(gui, "geometry_x", 0);
|
||||||
main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0);
|
main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0);
|
||||||
main_window_geometry_w = toml::find_or<int>(gui, "geometry_w", 0);
|
main_window_geometry_w = toml::find_or<int>(gui, "geometry_w", 0);
|
||||||
|
@ -556,6 +569,8 @@ void save(const std::filesystem::path& path) {
|
||||||
data["GUI"]["mw_width"] = m_window_size_W;
|
data["GUI"]["mw_width"] = m_window_size_W;
|
||||||
data["GUI"]["mw_height"] = m_window_size_H;
|
data["GUI"]["mw_height"] = m_window_size_H;
|
||||||
data["GUI"]["installDir"] = std::string{fmt::UTF(settings_install_dir.u8string()).data};
|
data["GUI"]["installDir"] = std::string{fmt::UTF(settings_install_dir.u8string()).data};
|
||||||
|
data["GUI"]["addonInstallDir"] =
|
||||||
|
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
|
||||||
data["GUI"]["geometry_x"] = main_window_geometry_x;
|
data["GUI"]["geometry_x"] = main_window_geometry_x;
|
||||||
data["GUI"]["geometry_y"] = main_window_geometry_y;
|
data["GUI"]["geometry_y"] = main_window_geometry_y;
|
||||||
data["GUI"]["geometry_w"] = main_window_geometry_w;
|
data["GUI"]["geometry_w"] = main_window_geometry_w;
|
||||||
|
|
|
@ -76,6 +76,7 @@ bool vkCrashDiagnosticEnabled();
|
||||||
// Gui
|
// Gui
|
||||||
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
|
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
|
||||||
void setGameInstallDir(const std::filesystem::path& dir);
|
void setGameInstallDir(const std::filesystem::path& dir);
|
||||||
|
void setAddonInstallDir(const std::filesystem::path& dir);
|
||||||
void setMainWindowTheme(u32 theme);
|
void setMainWindowTheme(u32 theme);
|
||||||
void setIconSize(u32 size);
|
void setIconSize(u32 size);
|
||||||
void setIconSizeGrid(u32 size);
|
void setIconSizeGrid(u32 size);
|
||||||
|
@ -94,6 +95,7 @@ u32 getMainWindowGeometryY();
|
||||||
u32 getMainWindowGeometryW();
|
u32 getMainWindowGeometryW();
|
||||||
u32 getMainWindowGeometryH();
|
u32 getMainWindowGeometryH();
|
||||||
std::filesystem::path getGameInstallDir();
|
std::filesystem::path getGameInstallDir();
|
||||||
|
std::filesystem::path getAddonInstallDir();
|
||||||
u32 getMainWindowTheme();
|
u32 getMainWindowTheme();
|
||||||
u32 getIconSize();
|
u32 getIconSize();
|
||||||
u32 getIconSizeGrid();
|
u32 getIconSizeGrid();
|
||||||
|
|
|
@ -119,7 +119,6 @@ static auto UserPaths = [] {
|
||||||
create_path(PathType::CapturesDir, user_dir / CAPTURES_DIR);
|
create_path(PathType::CapturesDir, user_dir / CAPTURES_DIR);
|
||||||
create_path(PathType::CheatsDir, user_dir / CHEATS_DIR);
|
create_path(PathType::CheatsDir, user_dir / CHEATS_DIR);
|
||||||
create_path(PathType::PatchesDir, user_dir / PATCHES_DIR);
|
create_path(PathType::PatchesDir, user_dir / PATCHES_DIR);
|
||||||
create_path(PathType::AddonsDir, user_dir / ADDONS_DIR);
|
|
||||||
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
|
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
|
||||||
|
|
||||||
return paths;
|
return paths;
|
||||||
|
|
|
@ -26,7 +26,6 @@ enum class PathType {
|
||||||
CapturesDir, // Where rdoc captures are stored.
|
CapturesDir, // Where rdoc captures are stored.
|
||||||
CheatsDir, // Where cheats are stored.
|
CheatsDir, // Where cheats are stored.
|
||||||
PatchesDir, // Where patches are stored.
|
PatchesDir, // Where patches are stored.
|
||||||
AddonsDir, // Where additional content is stored.
|
|
||||||
MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is stored.
|
MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is stored.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath) {
|
||||||
file.Read(entry);
|
file.Read(entry);
|
||||||
std::string_view name(entry.entry_name);
|
std::string_view name(entry.entry_name);
|
||||||
if (entry.flag == 0 && name.find("TROP") != std::string::npos) { // PNG
|
if (entry.flag == 0 && name.find("TROP") != std::string::npos) { // PNG
|
||||||
if (file.Seek(entry.entry_pos)) {
|
if (!file.Seek(entry.entry_pos)) {
|
||||||
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry offset");
|
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry offset");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath) {
|
||||||
}
|
}
|
||||||
if (entry.flag == 3 && np_comm_id[0] == 'N' &&
|
if (entry.flag == 3 && np_comm_id[0] == 'N' &&
|
||||||
np_comm_id[1] == 'P') { // ESFM, encrypted.
|
np_comm_id[1] == 'P') { // ESFM, encrypted.
|
||||||
if (file.Seek(entry.entry_pos)) {
|
if (!file.Seek(entry.entry_pos)) {
|
||||||
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry offset");
|
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry offset");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath) {
|
||||||
// clean xml file.
|
// clean xml file.
|
||||||
std::vector<u8> ESFM(entry.entry_len - iv_len);
|
std::vector<u8> ESFM(entry.entry_len - iv_len);
|
||||||
std::vector<u8> XML(entry.entry_len - iv_len);
|
std::vector<u8> XML(entry.entry_len - iv_len);
|
||||||
if (file.Seek(entry.entry_pos + iv_len)) {
|
if (!file.Seek(entry.entry_pos + iv_len)) {
|
||||||
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry + iv offset");
|
LOG_CRITICAL(Common_Filesystem, "Failed to seek to TRP entry + iv offset");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "app_content.h"
|
#include "app_content.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/config.h"
|
||||||
#include "common/io_file.h"
|
#include "common/io_file.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/path_util.h"
|
#include "common/path_util.h"
|
||||||
|
@ -59,8 +60,7 @@ int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label,
|
||||||
OrbisAppContentMountPoint* mount_point) {
|
OrbisAppContentMountPoint* mount_point) {
|
||||||
LOG_INFO(Lib_AppContent, "called");
|
LOG_INFO(Lib_AppContent, "called");
|
||||||
|
|
||||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::AddonsDir) / title_id /
|
const auto& mount_dir = Config::getAddonInstallDir() / title_id / entitlement_label->data;
|
||||||
entitlement_label->data;
|
|
||||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||||
|
|
||||||
for (int i = 0; i < addcont_count; i++) {
|
for (int i = 0; i < addcont_count; i++) {
|
||||||
|
@ -246,7 +246,7 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar
|
||||||
LOG_ERROR(Lib_AppContent, "(DUMMY) called");
|
LOG_ERROR(Lib_AppContent, "(DUMMY) called");
|
||||||
auto* param_sfo = Common::Singleton<PSF>::Instance();
|
auto* param_sfo = Common::Singleton<PSF>::Instance();
|
||||||
|
|
||||||
const auto addons_dir = Common::FS::GetUserPath(Common::FS::PathType::AddonsDir);
|
const auto addons_dir = Config::getAddonInstallDir();
|
||||||
if (const auto value = param_sfo->GetString("TITLE_ID"); value.has_value()) {
|
if (const auto value = param_sfo->GetString("TITLE_ID"); value.has_value()) {
|
||||||
title_id = *value;
|
title_id = *value;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -456,10 +456,9 @@ void CheatsPatches::downloadCheats(const QString& source, const QString& gameSer
|
||||||
if (source == "GoldHEN") {
|
if (source == "GoldHEN") {
|
||||||
url = "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/main/json.txt";
|
url = "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/main/json.txt";
|
||||||
} else if (source == "wolf2022") {
|
} else if (source == "wolf2022") {
|
||||||
url = "https://wolf2022.ir/trainer/" + gameSerial + "_" + gameVersion + ".json";
|
url = "https://wolf2022.ir/trainer/list.json";
|
||||||
} else if (source == "shadPS4") {
|
} else if (source == "shadPS4") {
|
||||||
url = "https://raw.githubusercontent.com/shadps4-emu/ps4_cheats/main/"
|
url = "https://raw.githubusercontent.com/shadps4-emu/ps4_cheats/main/CHEATS_JSON.txt";
|
||||||
"CHEATS_JSON.txt";
|
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(this, tr("Invalid Source"),
|
QMessageBox::warning(this, tr("Invalid Source"),
|
||||||
QString(tr("The selected source is invalid.") + "\n%1").arg(source));
|
QString(tr("The selected source is invalid.") + "\n%1").arg(source));
|
||||||
|
@ -474,44 +473,32 @@ void CheatsPatches::downloadCheats(const QString& source, const QString& gameSer
|
||||||
QByteArray jsonData = reply->readAll();
|
QByteArray jsonData = reply->readAll();
|
||||||
bool foundFiles = false;
|
bool foundFiles = false;
|
||||||
|
|
||||||
if (source == "GoldHEN" || source == "shadPS4") {
|
if (source == "wolf2022") {
|
||||||
QString textContent(jsonData);
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
|
||||||
QRegularExpression regex(
|
QJsonArray gamesArray = jsonDoc.object().value("games").toArray();
|
||||||
QString("%1_%2[^=]*\\.json").arg(gameSerial).arg(gameVersion));
|
|
||||||
QRegularExpressionMatchIterator matches = regex.globalMatch(textContent);
|
|
||||||
QString baseUrl;
|
|
||||||
|
|
||||||
if (source == "GoldHEN") {
|
foreach (const QJsonValue& value, gamesArray) {
|
||||||
baseUrl = "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/"
|
QJsonObject gameObject = value.toObject();
|
||||||
"main/json/";
|
QString title = gameObject.value("title").toString();
|
||||||
} else {
|
QString version = gameObject.value("version").toString();
|
||||||
baseUrl = "https://raw.githubusercontent.com/shadps4-emu/ps4_cheats/"
|
|
||||||
"main/CHEATS/";
|
|
||||||
}
|
|
||||||
|
|
||||||
while (matches.hasNext()) {
|
if (title == gameSerial &&
|
||||||
QRegularExpressionMatch match = matches.next();
|
(version == gameVersion || version == gameVersion.mid(1))) {
|
||||||
QString fileName = match.captured(0);
|
QString fileUrl =
|
||||||
|
"https://wolf2022.ir/trainer/" + gameObject.value("url").toString();
|
||||||
|
|
||||||
if (!fileName.isEmpty()) {
|
QString localFileName = gameObject.value("url").toString();
|
||||||
QString newFileName = fileName;
|
localFileName =
|
||||||
int dotIndex = newFileName.lastIndexOf('.');
|
localFileName.left(localFileName.lastIndexOf('.')) + "_wolf2022.json";
|
||||||
if (dotIndex != -1) {
|
|
||||||
|
|
||||||
if (source == "GoldHEN") {
|
QString localFilePath = dir.filePath(localFileName);
|
||||||
newFileName.insert(dotIndex, "_GoldHEN");
|
|
||||||
} else {
|
|
||||||
newFileName.insert(dotIndex, "_shadPS4");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString fileUrl = baseUrl + fileName;
|
|
||||||
QString localFilePath = dir.filePath(newFileName);
|
|
||||||
|
|
||||||
if (QFile::exists(localFilePath) && showMessageBox) {
|
if (QFile::exists(localFilePath) && showMessageBox) {
|
||||||
QMessageBox::StandardButton reply;
|
QMessageBox::StandardButton reply;
|
||||||
reply = QMessageBox::question(
|
reply = QMessageBox::question(
|
||||||
this, tr("File Exists"),
|
this, tr("File Exists"),
|
||||||
tr("File already exists. Do you want to replace it?"),
|
tr("File already exists. Do you want to replace it?") + "\n" +
|
||||||
|
localFileName,
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
if (reply == QMessageBox::No) {
|
if (reply == QMessageBox::No) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -549,38 +536,81 @@ void CheatsPatches::downloadCheats(const QString& source, const QString& gameSer
|
||||||
if (!foundFiles && showMessageBox) {
|
if (!foundFiles && showMessageBox) {
|
||||||
QMessageBox::warning(this, tr("Cheats Not Found"), tr("CheatsNotFound_MSG"));
|
QMessageBox::warning(this, tr("Cheats Not Found"), tr("CheatsNotFound_MSG"));
|
||||||
}
|
}
|
||||||
} else if (source == "wolf2022") {
|
} else if (source == "GoldHEN" || source == "shadPS4") {
|
||||||
QString fileName = QFileInfo(QUrl(url).path()).fileName();
|
QString textContent(jsonData);
|
||||||
QString baseFileName = fileName;
|
QRegularExpression regex(
|
||||||
int dotIndex = baseFileName.lastIndexOf('.');
|
QString("%1_%2[^=]*\\.json").arg(gameSerial).arg(gameVersion));
|
||||||
|
QRegularExpressionMatchIterator matches = regex.globalMatch(textContent);
|
||||||
|
QString baseUrl;
|
||||||
|
|
||||||
|
if (source == "GoldHEN") {
|
||||||
|
baseUrl = "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/"
|
||||||
|
"main/json/";
|
||||||
|
} else {
|
||||||
|
baseUrl = "https://raw.githubusercontent.com/shadps4-emu/ps4_cheats/"
|
||||||
|
"main/CHEATS/";
|
||||||
|
}
|
||||||
|
|
||||||
|
while (matches.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matches.next();
|
||||||
|
QString fileName = match.captured(0);
|
||||||
|
|
||||||
|
if (!fileName.isEmpty()) {
|
||||||
|
QString newFileName = fileName;
|
||||||
|
int dotIndex = newFileName.lastIndexOf('.');
|
||||||
if (dotIndex != -1) {
|
if (dotIndex != -1) {
|
||||||
baseFileName.insert(dotIndex, "_wolf2022");
|
|
||||||
|
if (source == "GoldHEN") {
|
||||||
|
newFileName.insert(dotIndex, "_GoldHEN");
|
||||||
|
} else {
|
||||||
|
newFileName.insert(dotIndex, "_shadPS4");
|
||||||
}
|
}
|
||||||
QString filePath;
|
}
|
||||||
Common::FS::PathToQString(filePath,
|
QString fileUrl = baseUrl + fileName;
|
||||||
Common::FS::GetUserPath(Common::FS::PathType::CheatsDir));
|
QString localFilePath = dir.filePath(newFileName);
|
||||||
filePath += "/" + baseFileName;
|
|
||||||
if (QFile::exists(filePath) && showMessageBox) {
|
if (QFile::exists(localFilePath) && showMessageBox) {
|
||||||
QMessageBox::StandardButton reply2;
|
QMessageBox::StandardButton reply;
|
||||||
reply2 =
|
reply = QMessageBox::question(
|
||||||
QMessageBox::question(this, tr("File Exists"),
|
this, tr("File Exists"),
|
||||||
tr("File already exists. Do you want to replace it?"),
|
tr("File already exists. Do you want to replace it?") + "\n" +
|
||||||
|
newFileName,
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
if (reply2 == QMessageBox::No) {
|
if (reply == QMessageBox::No) {
|
||||||
reply->deleteLater();
|
continue;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QFile cheatFile(filePath);
|
QNetworkRequest fileRequest(fileUrl);
|
||||||
if (cheatFile.open(QIODevice::WriteOnly)) {
|
QNetworkReply* fileReply = manager->get(fileRequest);
|
||||||
cheatFile.write(jsonData);
|
|
||||||
cheatFile.close();
|
connect(fileReply, &QNetworkReply::finished, [=, this]() {
|
||||||
foundFiles = true;
|
if (fileReply->error() == QNetworkReply::NoError) {
|
||||||
populateFileListCheats();
|
QByteArray fileData = fileReply->readAll();
|
||||||
|
QFile localFile(localFilePath);
|
||||||
|
if (localFile.open(QIODevice::WriteOnly)) {
|
||||||
|
localFile.write(fileData);
|
||||||
|
localFile.close();
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(
|
QMessageBox::warning(
|
||||||
this, tr("Error"),
|
this, tr("Error"),
|
||||||
QString(tr("Failed to save file:") + "\n%1").arg(filePath));
|
QString(tr("Failed to save file:") + "\n%1")
|
||||||
|
.arg(localFilePath));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(this, tr("Error"),
|
||||||
|
QString(tr("Failed to download file:") +
|
||||||
|
"%1\n\n" + tr("Error:") + "%2")
|
||||||
|
.arg(fileUrl)
|
||||||
|
.arg(fileReply->errorString()));
|
||||||
|
}
|
||||||
|
fileReply->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
foundFiles = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundFiles && showMessageBox) {
|
||||||
|
QMessageBox::warning(this, tr("Cheats Not Found"), tr("CheatsNotFound_MSG"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (foundFiles && showMessageBox) {
|
if (foundFiles && showMessageBox) {
|
||||||
|
@ -910,11 +940,16 @@ void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonAr
|
||||||
void CheatsPatches::populateFileListCheats() {
|
void CheatsPatches::populateFileListCheats() {
|
||||||
QString cheatsDir;
|
QString cheatsDir;
|
||||||
Common::FS::PathToQString(cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir));
|
Common::FS::PathToQString(cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir));
|
||||||
QString pattern = m_gameSerial + "_" + m_gameVersion + "*.json";
|
|
||||||
|
QString fullGameVersion = m_gameVersion;
|
||||||
|
QString modifiedGameVersion = m_gameVersion.mid(1);
|
||||||
|
|
||||||
|
QString patternWithFirstChar = m_gameSerial + "_" + fullGameVersion + "*.json";
|
||||||
|
QString patternWithoutFirstChar = m_gameSerial + "_" + modifiedGameVersion + "*.json";
|
||||||
|
|
||||||
QDir dir(cheatsDir);
|
QDir dir(cheatsDir);
|
||||||
QStringList filters;
|
QStringList filters;
|
||||||
filters << pattern;
|
filters << patternWithFirstChar << patternWithoutFirstChar;
|
||||||
dir.setNameFilters(filters);
|
dir.setNameFilters(filters);
|
||||||
|
|
||||||
QFileInfoList fileList = dir.entryInfoList(QDir::Files);
|
QFileInfoList fileList = dir.entryInfoList(QDir::Files);
|
||||||
|
|
|
@ -18,6 +18,7 @@ GameInstallDialog::GameInstallDialog() : m_gamesDirectory(nullptr) {
|
||||||
auto layout = new QVBoxLayout(this);
|
auto layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
layout->addWidget(SetupGamesDirectory());
|
layout->addWidget(SetupGamesDirectory());
|
||||||
|
layout->addWidget(SetupAddonsDirectory());
|
||||||
layout->addStretch();
|
layout->addStretch();
|
||||||
layout->addWidget(SetupDialogActions());
|
layout->addWidget(SetupDialogActions());
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ GameInstallDialog::GameInstallDialog() : m_gamesDirectory(nullptr) {
|
||||||
|
|
||||||
GameInstallDialog::~GameInstallDialog() {}
|
GameInstallDialog::~GameInstallDialog() {}
|
||||||
|
|
||||||
void GameInstallDialog::Browse() {
|
void GameInstallDialog::BrowseGamesDirectory() {
|
||||||
auto path = QFileDialog::getExistingDirectory(this, tr("Directory to install games"));
|
auto path = QFileDialog::getExistingDirectory(this, tr("Directory to install games"));
|
||||||
|
|
||||||
if (!path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
|
@ -35,6 +36,14 @@ void GameInstallDialog::Browse() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameInstallDialog::BrowseAddonsDirectory() {
|
||||||
|
auto path = QFileDialog::getExistingDirectory(this, tr("Directory to install DLC"));
|
||||||
|
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
m_addonsDirectory->setText(QDir::toNativeSeparators(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QWidget* GameInstallDialog::SetupGamesDirectory() {
|
QWidget* GameInstallDialog::SetupGamesDirectory() {
|
||||||
auto group = new QGroupBox(tr("Directory to install games"));
|
auto group = new QGroupBox(tr("Directory to install games"));
|
||||||
auto layout = new QHBoxLayout(group);
|
auto layout = new QHBoxLayout(group);
|
||||||
|
@ -51,7 +60,30 @@ QWidget* GameInstallDialog::SetupGamesDirectory() {
|
||||||
// Browse button.
|
// Browse button.
|
||||||
auto browse = new QPushButton(tr("Browse"));
|
auto browse = new QPushButton(tr("Browse"));
|
||||||
|
|
||||||
connect(browse, &QPushButton::clicked, this, &GameInstallDialog::Browse);
|
connect(browse, &QPushButton::clicked, this, &GameInstallDialog::BrowseGamesDirectory);
|
||||||
|
|
||||||
|
layout->addWidget(browse);
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* GameInstallDialog::SetupAddonsDirectory() {
|
||||||
|
auto group = new QGroupBox(tr("Directory to install DLC"));
|
||||||
|
auto layout = new QHBoxLayout(group);
|
||||||
|
|
||||||
|
// Input.
|
||||||
|
m_addonsDirectory = new QLineEdit();
|
||||||
|
QString install_dir;
|
||||||
|
Common::FS::PathToQString(install_dir, Config::getAddonInstallDir());
|
||||||
|
m_addonsDirectory->setText(install_dir);
|
||||||
|
m_addonsDirectory->setMinimumWidth(400);
|
||||||
|
|
||||||
|
layout->addWidget(m_addonsDirectory);
|
||||||
|
|
||||||
|
// Browse button.
|
||||||
|
auto browse = new QPushButton(tr("Browse"));
|
||||||
|
|
||||||
|
connect(browse, &QPushButton::clicked, this, &GameInstallDialog::BrowseAddonsDirectory);
|
||||||
|
|
||||||
layout->addWidget(browse);
|
layout->addWidget(browse);
|
||||||
|
|
||||||
|
@ -70,6 +102,7 @@ QWidget* GameInstallDialog::SetupDialogActions() {
|
||||||
void GameInstallDialog::Save() {
|
void GameInstallDialog::Save() {
|
||||||
// Check games directory.
|
// Check games directory.
|
||||||
auto gamesDirectory = m_gamesDirectory->text();
|
auto gamesDirectory = m_gamesDirectory->text();
|
||||||
|
auto addonsDirectory = m_addonsDirectory->text();
|
||||||
|
|
||||||
if (gamesDirectory.isEmpty() || !QDir(gamesDirectory).exists() ||
|
if (gamesDirectory.isEmpty() || !QDir(gamesDirectory).exists() ||
|
||||||
!QDir::isAbsolutePath(gamesDirectory)) {
|
!QDir::isAbsolutePath(gamesDirectory)) {
|
||||||
|
@ -78,7 +111,15 @@ void GameInstallDialog::Save() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (addonsDirectory.isEmpty() || !QDir(addonsDirectory).exists() ||
|
||||||
|
!QDir::isAbsolutePath(addonsDirectory)) {
|
||||||
|
QMessageBox::critical(this, tr("Error"),
|
||||||
|
"The value for location to install DLC is not valid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Config::setGameInstallDir(Common::FS::PathFromQString(gamesDirectory));
|
Config::setGameInstallDir(Common::FS::PathFromQString(gamesDirectory));
|
||||||
|
Config::setAddonInstallDir(Common::FS::PathFromQString(addonsDirectory));
|
||||||
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
|
||||||
Config::save(config_dir / "config.toml");
|
Config::save(config_dir / "config.toml");
|
||||||
accept();
|
accept();
|
||||||
|
|
|
@ -16,13 +16,16 @@ public:
|
||||||
~GameInstallDialog();
|
~GameInstallDialog();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void Browse();
|
void BrowseGamesDirectory();
|
||||||
|
void BrowseAddonsDirectory();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* SetupGamesDirectory();
|
QWidget* SetupGamesDirectory();
|
||||||
|
QWidget* SetupAddonsDirectory();
|
||||||
QWidget* SetupDialogActions();
|
QWidget* SetupDialogActions();
|
||||||
void Save();
|
void Save();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLineEdit* m_gamesDirectory;
|
QLineEdit* m_gamesDirectory;
|
||||||
|
QLineEdit* m_addonsDirectory;
|
||||||
};
|
};
|
|
@ -687,8 +687,8 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
|
||||||
}
|
}
|
||||||
std::string entitlement_label = Common::SplitString(content_id, '-')[2];
|
std::string entitlement_label = Common::SplitString(content_id, '-')[2];
|
||||||
|
|
||||||
auto addon_extract_path = Common::FS::GetUserPath(Common::FS::PathType::AddonsDir) /
|
auto addon_extract_path =
|
||||||
pkg.GetTitleID() / entitlement_label;
|
Config::getAddonInstallDir() / pkg.GetTitleID() / entitlement_label;
|
||||||
QString addonDirPath;
|
QString addonDirPath;
|
||||||
Common::FS::PathToQString(addonDirPath, addon_extract_path);
|
Common::FS::PathToQString(addonDirPath, addon_extract_path);
|
||||||
QDir addon_dir(addonDirPath);
|
QDir addon_dir(addonDirPath);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue