Merge branch 'master' into patch-1

This commit is contained in:
Megamouse 2026-04-19 19:57:47 +02:00 committed by GitHub
commit 5226b37bf8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 401 additions and 104 deletions

View file

@ -7,24 +7,24 @@ if(USE_SYSTEM_CURL)
target_link_libraries(3rdparty_libcurl INTERFACE CURL::libcurl)
else()
message(STATUS "RPCS3: building libcurl + wolfssl submodules")
set(BUILD_CURL_EXE OFF CACHE BOOL "Set to ON to build curl executable.")
set(BUILD_STATIC_CURL OFF CACHE BOOL "Set to ON to build curl executable with static libcurl.")
set(BUILD_STATIC_LIBS ON CACHE BOOL "Set to ON to build static libcurl.")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Set to ON to build shared libcurl.")
set(BUILD_CURL_EXE OFF CACHE INTERNAL "")
set(BUILD_STATIC_CURL OFF CACHE INTERNAL "")
set(BUILD_STATIC_LIBS ON CACHE INTERNAL "")
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "")
find_package(WolfSSL REQUIRED)
set(CURL_USE_WOLFSSL ON CACHE BOOL "enable wolfSSL for SSL/TLS")
set(CURL_USE_OPENSSL OFF CACHE BOOL "Use OpenSSL code. Experimental")
set(HTTP_ONLY ON CACHE BOOL "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)")
set(USE_LIBIDN2 OFF CACHE BOOL "Use libidn2 for IDN support") # Disabled because MacOS CI doesn't work otherwise
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
option(CURL_DISABLE_INSTALL "Disable installation targets" ON)
set(CURL_USE_WOLFSSL ON CACHE INTERNAL "")
set(CURL_USE_OPENSSL OFF CACHE INTERNAL "")
set(HTTP_ONLY ON CACHE INTERNAL "")
set(USE_LIBIDN2 OFF CACHE INTERNAL "") # Disabled because MacOS CI doesn't work otherwise
set(CURL_CA_PATH "none" CACHE INTERNAL "")
set(CURL_DISABLE_INSTALL ON CACHE INTERNAL "")
if(WIN32)
set(ENABLE_UNICODE ON CACHE BOOL "enable Unicode")
set(ENABLE_UNICODE ON CACHE INTERNAL "")
endif()
set(CURL_USE_LIBSSH2 OFF CACHE BOOL "Use libSSH2")
set(CURL_USE_LIBPSL OFF CACHE BOOL "Use libPSL")
option(BUILD_TESTING "Build tests" OFF)
option(BUILD_EXAMPLES "Build libcurl examples" OFF)
set(CURL_USE_LIBSSH2 OFF CACHE INTERNAL "")
set(CURL_USE_LIBPSL OFF CACHE INTERNAL "")
set(BUILD_TESTING OFF CACHE INTERNAL "")
set(BUILD_EXAMPLES OFF CACHE INTERNAL "")
add_subdirectory(curl EXCLUDE_FROM_ALL)

@ -1 +1 @@
Subproject commit 95ab3fdca83ea294efd3b092e9a53c5a39886444
Subproject commit 3061454d980de7d53608f594194cfac722721d2a

View file

@ -119,7 +119,8 @@ echo -e "\n\nResult:\n"
# Find the max length of the paths (before '->')
max_len=0
while IFS='->' read -r left _; do
while read -r line; do
left="${line%%->*}"
len=$(echo -n "$left" | wc -c)
if (( len > max_len )); then
max_len=$len
@ -127,8 +128,10 @@ while IFS='->' read -r left _; do
done < "$resultfile"
# Print with padding so '->' lines up
while IFS='->' read -r left right; do
right=$(echo "$right" | sed 's/^[[:space:]]*>*[[:space:]]*//')
while read -r line; do
left="${line%%->*}"
right="${line#*->}"
right=$(echo "$right" | sed 's/^[[:space:]]*//')
printf "%-${max_len}s -> %s\n" "$left" "$right"
done < "$resultfile"

View file

@ -4,23 +4,22 @@ if(USE_SYSTEM_WOLFSSL)
add_library(wolfssl INTERFACE)
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
else()
option(WOLFSSL_TLS13 "Enable wolfSSL TLS v1.3 (default: enabled)" OFF)
set(WOLFSSL_SHA3 ON CACHE STRING "Enable wolfSSL SHA-3 support (default: enabled on x86_64/aarch64)")
set(WOLFSSL_SHAKE256 ON CACHE STRING "Enable wolfSSL SHAKE256 support (default: enabled on x86_64/aarch64)")
option(WOLFSSL_BASE64_ENCODE "Enable Base64 encoding (default: enabled on x86_64)" OFF)
option(WOLFSSL_DES3 "Enable DES3 (default: disabled)" ON)
option(WOLFSSL_PWDBASED "Enable PWDBASED (default: disabled)" ON)
option(WOLFSSL_FAST_MATH "Enable fast math ops (default: disabled)" ON)
option(WOLFSSL_EXAMPLES "Enable examples (default: enabled)" OFF)
option(WOLFSSL_CRYPT_TESTS "Enable Crypt Bench/Test (default: enabled)" OFF)
option(WOLFSSL_ASYNC_THREADS "Enable Asynchronous Threading (default: enabled)" OFF)
option(WOLFSSL_BUILD_OUT_OF_TREE "Don't generate files in the source tree (default: yes)" ON)
option(WOLFSSL_SNI "Enable SNI (default: disabled)" ON)
option(WOLFSSL_OPENSSLEXTRA "Enable extra OpenSSL API, size+ (default: disabled)" ON)
option(WOLFSSL_HARDEN "Enable Hardened build, Enables Timing Resistance and Blinding (default: enabled)" OFF)
option(WOLFSSL_ALT_CERT_CHAINS "Enable support for Alternate certification chains (default: disabled)" ON)
set(WOLFSSL_TLS13 OFF CACHE INTERNAL "")
set(WOLFSSL_SHA3 ON CACHE INTERNAL "")
set(WOLFSSL_SHAKE256 ON CACHE INTERNAL "")
set(WOLFSSL_BASE64_ENCODE OFF CACHE INTERNAL "")
set(WOLFSSL_DES3 ON CACHE INTERNAL "")
set(WOLFSSL_PWDBASED ON CACHE INTERNAL "")
set(WOLFSSL_FAST_MATH ON CACHE INTERNAL "")
set(WOLFSSL_EXAMPLES OFF CACHE INTERNAL "")
set(WOLFSSL_CRYPT_TESTS OFF CACHE INTERNAL "")
set(WOLFSSL_ASYNC_THREADS OFF CACHE INTERNAL "")
set(WOLFSSL_BUILD_OUT_OF_TREE ON CACHE INTERNAL "")
set(WOLFSSL_SNI ON CACHE INTERNAL "")
set(WOLFSSL_OPENSSLEXTRA ON CACHE INTERNAL "")
set(WOLFSSL_ALT_CERT_CHAINS ON CACHE INTERNAL "")
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_NO_OPTIONS_H)
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_USE_OPTIONS_H)
endif()

View file

@ -933,7 +933,7 @@ game_boot_result Emulator::GetElfPathFromDir(std::string& elf_path, const std::s
return game_boot_result::invalid_file_or_folder;
}
game_boot_result Emulator::BootGame(const std::string& path, const std::string& title_id, bool direct, cfg_mode config_mode, const std::string& config_path, const std::string& db_config)
game_boot_result Emulator::BootGame(const std::string& path, const std::string& title_id, bool direct, cfg_mode config_mode, const std::string& config_path, const std::optional<std::string>& db_config)
{
if (m_restrict_emu_state_change)
{
@ -1565,8 +1565,23 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
sys_log.notice("Version: APP_VER=%s VERSION=%s", version_app, version_disc);
{
// We add the database configuration if it is set, unless we are using a mode that specifically selects a different configuration.
bool add_database_config = !m_db_config.empty() && (m_config_mode == cfg_mode::database_config || m_config_mode == cfg_mode::custom || m_config_mode == cfg_mode::continuous);
if (m_config_mode == cfg_mode::database_config || m_config_mode == cfg_mode::custom)
{
if (!m_db_config)
{
// Get database config if possible. This only happens if the database config hasn't been set by the UI (e.g. if booted with no-gui).
// We only know the title_id for sure at this point, so it doesn't make sense to retrieve it earlier.
m_db_config = Emu.GetCallbacks().get_database_config(m_title_id);
}
// We add the database configuration if it is set, unless we are using a mode that specifically selects a different configuration.
m_add_database_config = m_db_config && !m_db_config->empty();
}
else if (m_config_mode != cfg_mode::continuous)
{
// Reset flag unless in continuous mode
m_add_database_config = false;
}
if (m_config_mode == cfg_mode::custom_selection || (m_config_mode == cfg_mode::continuous && !m_config_path.empty()))
{
@ -1610,7 +1625,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
{
g_cfg.name = config_path;
m_config_path = config_path;
add_database_config = false; // A custom config exists. Do not add the database config.
m_add_database_config = false; // A custom config exists. Do not add the database config.
break;
}
@ -1619,12 +1634,12 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
}
}
if (add_database_config)
if (m_add_database_config && m_db_config && !m_db_config->empty())
{
// Add database config
sys_log.notice("Applying database config");
if (g_cfg.from_string(m_db_config))
if (g_cfg.from_string(*m_db_config))
{
g_cfg.name = "database_config";
}
@ -3373,7 +3388,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
klic.clear();
hdd1.clear();
init_mem_containers = nullptr;
m_db_config.clear();
m_db_config = std::nullopt;
m_config_path.clear();
m_config_mode = cfg_mode::custom;
read_used_savestate_versions();
@ -4018,7 +4033,9 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
if (allow_autoexit)
{
Quit(g_cfg.misc.autoexit.get());
const bool autoexit = g_cfg.misc.autoexit.get();
sys_log.notice("Quit with main_window::closeEvent. (autoexit=%d)", autoexit);
Quit(autoexit);
}
if (after_kill_callback)

View file

@ -115,6 +115,7 @@ struct EmuCallbacks
std::function<void()> check_microphone_permissions;
std::function<std::unique_ptr<class video_source>()> make_video_source;
std::function<void(bool)> enable_gamemode;
std::function<std::string(const std::string&)> get_database_config;
};
namespace utils
@ -146,7 +147,7 @@ class Emulator final
cfg_mode m_config_mode = cfg_mode::custom;
std::string m_config_path;
std::string m_db_config;
std::optional<std::string> m_db_config; // std::nullopt means it has not been retrieved yet
std::string m_path;
std::string m_path_old;
std::string m_path_original;
@ -171,6 +172,7 @@ class Emulator final
bool m_continuous_mode = false;
bool m_has_gui = true;
bool m_add_database_config = false;
bool m_state_inspection_savestate = false;
@ -370,7 +372,8 @@ public:
const std::string& GetUsedDatabaseConfig() const
{
return m_db_config;
static std::string empty_db_config;
return m_db_config ? *m_db_config : empty_db_config;
}
bool IsChildProcess() const
@ -422,7 +425,7 @@ public:
return emulation_state_guard_t{this};
}
game_boot_result BootGame(const std::string& path, const std::string& title_id = "", bool direct = false, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::string& db_config = "");
game_boot_result BootGame(const std::string& path, const std::string& title_id = "", bool direct = false, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::optional<std::string>& db_config = std::nullopt);
bool BootRsxCapture(const std::string& path);
void SetForceBoot(bool force_boot);

View file

@ -282,7 +282,7 @@ struct cfg_root : cfg::node
cfg::_bool paint_move_spheres{this, "Paint move spheres", false, true};
cfg::_bool allow_move_hue_set_by_game{this, "Allow move hue set by game", false, true};
cfg::_bool lock_overlay_input_to_player_one{this, "Lock overlay input to player one", false, true};
cfg::string midi_devices{this, "Emulated Midi devices", "ßßß@@@ßßß@@@ßßß@@@"};
cfg::string midi_devices{this, "Emulated Midi devices", "Keyboardßßß@@@Keyboardßßß@@@Keyboardßßß@@@"};
cfg::_bool load_sdl_mappings{ this, "Load SDL GameController Mappings", true };
cfg::_bool pad_debug_overlay{ this, "IO Debug overlay", false, true };
cfg::_bool mouse_debug_overlay{ this, "Mouse Debug overlay", false, true };

View file

@ -199,7 +199,7 @@ bool iso_file_decryption::init(const std::string& path)
const u32 region_count = char_arr_BE_to_uint(sec0_sec1.data());
// Ensure the region count is a proper value
if (region_count < 1 || region_count > 31) // It's non-PS3ISO
if (region_count < 1 || region_count > 127) // It's non-PS3ISO
{
iso_log.error("init: Failed to read region information: '%s' (region_count=%d)", path, region_count);
return false;

View file

@ -58,7 +58,7 @@ void headless_application::InitializeCallbacks()
on_exit();
}
sys_log.notice("Quitting headless application");
sys_log.notice("Quitting headless application (force_quit=%d)", force_quit);
quit();
return true;
}

View file

@ -2,6 +2,8 @@
#include "main_application.h"
#include "display_sleep_control.h"
#include "gamemode_control.h"
#include "rpcs3qt/gui_settings.h"
#include "rpcs3qt/config_database.h"
#include "util/types.hpp"
#include "util/logs.hpp"
@ -409,5 +411,28 @@ EmuCallbacks main_application::CreateCallbacks()
return path + suffix;
};
callbacks.get_database_config = [](const std::string& title_id)
{
if (title_id.empty())
return std::string();
sys_log.notice("Trying to retrieve database config for: '%s'", title_id);
const auto settings = std::make_shared<gui_settings>();
config_database config_db(settings, nullptr);
config_db.request_config_database(false);
if (!config_db.has_config(title_id))
return std::string();
if (const auto config = config_db.get_config(title_id))
{
sys_log.notice("Found database config for: '%s'", title_id);
return config.value();
}
return std::string();
};
return callbacks;
}

View file

@ -1,76 +1,127 @@
#include "stdafx.h"
#include "config_checker.h"
#include "midi_creator.h"
#include "microphone_creator.h"
#include "Emu/system_config.h"
#include "Emu/system_utils.hpp"
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QLabel>
LOG_CHANNEL(gui_log, "GUI");
config_checker::config_checker(QWidget* parent, const QString& content, bool is_log) : QDialog(parent)
config_checker::config_checker(QWidget* parent, const QString& content_or_serial, checker_mode mode, const std::string& db_config)
: QDialog(parent)
, m_checker_mode(mode)
, m_content_or_serial(content_or_serial)
, m_db_config(db_config)
{
setObjectName("config_checker");
setWindowTitle(tr("Config Checker"));
setAttribute(Qt::WA_DeleteOnClose);
QVBoxLayout* layout = new QVBoxLayout();
QLabel* label = new QLabel(this);
layout->addWidget(label);
QComboBox* combo = nullptr;
QString result;
if (check_config(content, result, is_log))
if (mode == checker_mode::gamelist)
{
setWindowTitle(tr("Interesting!"));
m_serial = content_or_serial.toStdString();
if (result.isEmpty())
combo = new QComboBox(this);
std::string custom_config_path;
if (std::string config_path = rpcs3::utils::get_custom_config_path(m_serial); fs::is_file(config_path))
{
label->setText(tr("Found config.\nIt seems to match the default config."));
custom_config_path = std::move(config_path);
combo->addItem(tr("Custom Configuration"), static_cast<int>(cfg_mode::custom));
}
else
combo->addItem(tr("Database + Global Configuration"), static_cast<int>(cfg_mode::database_config));
combo->setCurrentIndex(combo->findData(static_cast<int>(custom_config_path.empty() ? cfg_mode::database_config : cfg_mode::custom)));
connect(combo, &QComboBox::currentIndexChanged, this, [this, combo]()
{
label->setText(tr("Found config.\nSome settings seem to deviate from the default config:"));
check_config(static_cast<cfg_mode>(combo->currentData().toInt()));
});
QTextEdit* text_box = new QTextEdit();
text_box->setReadOnly(true);
text_box->setHtml(result);
layout->addWidget(text_box);
layout->addWidget(combo);
}
resize(400, 600);
}
}
else
{
setWindowTitle(tr("Ooops!"));
label->setText(result);
}
m_label = new QLabel(this);
layout->addWidget(m_label);
m_text_box = new QTextEdit();
m_text_box->setReadOnly(true);
layout->addWidget(m_text_box);
QDialogButtonBox* box = new QDialogButtonBox(QDialogButtonBox::Close);
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
layout->addWidget(box);
setLayout(layout);
resize(400, 600);
check_config(combo ? static_cast<cfg_mode>(combo->currentData().toInt()) : cfg_mode::database_config);
}
bool config_checker::check_config(QString content, QString& result, bool is_log)
void config_checker::check_config(cfg_mode mode)
{
cfg_root config{};
QString result;
if (is_log)
if (check_config(mode, m_content_or_serial, result))
{
if (m_checker_mode == checker_mode::gamelist)
{
if (result.isEmpty())
{
m_label->setText(tr("The configuration seems to match the default config."));
}
else
{
m_label->setText(tr("Config database settings are marked with an * in front of the name.\nSome settings seem to deviate from the default config:"));
}
}
else
{
if (result.isEmpty())
{
m_label->setText(tr("Found config.\nIt seems to match the default config."));
}
else
{
m_label->setText(tr("Found config.\nSome settings seem to deviate from the default config:"));
}
}
m_text_box->setVisible(!result.isEmpty());
m_text_box->setHtml(result);
}
else
{
m_label->setText(result);
}
}
bool config_checker::check_config(cfg_mode mode, QString content_or_serial, QString& result)
{
std::unique_ptr<cfg_root> config = std::make_unique<cfg_root>();
std::unique_ptr<cfg_root> config_db_only;
if (m_checker_mode == checker_mode::log)
{
const QString start_token = "SYS: Used configuration:\n";
const QString end_token = "\n·";
qsizetype start = content.indexOf(start_token);
qsizetype start = content_or_serial.indexOf(start_token);
qsizetype end = -1;
if (start >= 0)
{
start += start_token.size();
end = content.indexOf(end_token, start);
end = content_or_serial.indexOf(end_token, start);
}
if (end < 0)
@ -79,24 +130,93 @@ bool config_checker::check_config(QString content, QString& result, bool is_log)
return false;
}
content = content.mid(start, end - start);
content_or_serial = content_or_serial.mid(start, end - start);
}
if (!config.from_string(content.toStdString()))
if (m_checker_mode == checker_mode::gamelist)
{
gui_log.error("log_viewer: Failed to parse config:\n%s", content);
config->from_default();
// Load global config
const std::string cfg_path = fs::get_config_dir(true) + "config.yml";
if (const fs::file cfg_file{cfg_path})
{
gui_log.notice("config_checker: Applying global config: %s", cfg_path);
if (!config->from_string(cfg_file.to_string()))
{
gui_log.error("config_checker: Failed to apply global config: %s", cfg_path);
result = tr("Failed to apply global config!");
return false;
}
}
// Load custom config
const std::string custom_config_path = rpcs3::utils::get_custom_config_path(m_serial);
if (mode == cfg_mode::custom && !custom_config_path.empty())
{
if (const fs::file cfg_file{custom_config_path})
{
gui_log.notice("config_checker: Applying custom config: %s", custom_config_path);
if (!config->from_string(cfg_file.to_string()))
{
gui_log.error("config_checker: Failed to apply custom config: %s", custom_config_path);
result = tr("Failed to apply custom config!");
return false;
}
}
}
if (mode == cfg_mode::database_config && !m_db_config.empty())
{
gui_log.notice("config_checker: Applying database config: %s", custom_config_path);
if (!config->from_string(m_db_config))
{
gui_log.error("config_checker: Failed to apply database config:\n%s", m_db_config);
result = tr("Failed to apply database config!");
return false;
}
config_db_only = std::make_unique<cfg_root>();
config_db_only->from_default();
if (!config_db_only->from_string(m_db_config))
{
gui_log.error("config_checker: Failed to apply database config:\n%s", m_db_config);
result = tr("Failed to apply database config!");
return false;
}
}
}
else if (!config->from_string(content_or_serial.toStdString()))
{
gui_log.error("config_checker: Failed to parse config:\n%s", content_or_serial);
result = tr("Cannot find any config!");
return false;
}
std::function<void(const cfg::_base*, std::string&, int)> print_diff_recursive;
print_diff_recursive = [&print_diff_recursive](const cfg::_base* base, std::string& diff, int indentation) -> void
std::function<void(const cfg::_base*, const cfg::_base*, std::string&, int)> print_diff_recursive;
print_diff_recursive = [this, &print_diff_recursive, &config](const cfg::_base* base, const cfg::_base* base_db_only, std::string& diff, int indentation) -> void
{
if (!base)
{
return;
}
// Ignore some irrelevant settings in gamelist mode
if (m_checker_mode == checker_mode::gamelist && base->get_type() != cfg::type::node)
{
const std::string key = base->get_name();
if (key == config->sys.console_psid.get_name() ||
key == config->sys.system_name.get_name() ||
key == config->video.vk.adapter.get_name())
{
return;
}
}
const auto indent = [](std::string& str, int indentation)
{
for (int i = 0; i < indentation * 2; i++)
@ -105,6 +225,16 @@ bool config_checker::check_config(QString content, QString& result, bool is_log)
}
};
const auto base_name_db = [base](bool is_db_config)
{
if (is_db_config)
{
return "*" + base->get_name();
}
return base->get_name();
};
switch (base->get_type())
{
case cfg::type::node:
@ -115,7 +245,20 @@ bool config_checker::check_config(QString content, QString& result, bool is_log)
for (const auto& n : node->get_nodes())
{
print_diff_recursive(n, diff_tmp, indentation + 1);
const cfg::_base* n_db_only = nullptr;
if (const auto& node_db_only = static_cast<const cfg::node*>(base_db_only))
{
for (const auto& n_db : node_db_only->get_nodes())
{
if (n_db->get_name() == n->get_name())
{
n_db_only = n_db;
break;
}
}
}
print_diff_recursive(n, n_db_only, diff_tmp, indentation + 1);
}
if (!diff_tmp.empty())
@ -142,19 +285,75 @@ bool config_checker::check_config(QString content, QString& result, bool is_log)
const std::string val = base->to_string();
const std::string def = base->def_to_string();
if (val != def)
{
indent(diff, indentation);
if (val == def)
break;
if (def.empty())
indent(diff, indentation);
if (m_checker_mode == checker_mode::gamelist)
{
if (base->get_name() == config->io.midi_devices.get_name())
{
fmt::append(diff, "%s: <span style=\"color:red;\">%s</span><br>", base->get_name(), val);
fmt::append(diff, "%s:<br>", base->get_name());
midi_creator mc {};
mc.parse_devices(def);
const std::array<midi_device, max_midi_devices> def_devices = mc.get_selection_list();
mc.parse_devices(val);
const std::array<midi_device, max_midi_devices> devices = mc.get_selection_list();
for (usz i = 0; i < devices.size(); i++)
{
const midi_device& def_device = def_devices[i];
const midi_device& device = devices[i];
if (device.name == def_device.name)
continue;
indent(diff, indentation + 1);
fmt::append(diff, "Device %d: <span style=\"color:red;\">%s: %s</span><br>", i + 1, device.type, device.name);
}
break;
}
else
else if (base->get_name() == config->audio.microphone_devices.get_name())
{
fmt::append(diff, "%s: <span style=\"color:red;\">%s</span> <span style=\"color:gray;\">default:</span> <span style=\"color:green;\">%s</span><br>", base->get_name(), val, def);
fmt::append(diff, "%s:<br>", base->get_name());
microphone_creator mc {};
mc.parse_devices(def);
const std::array<std::string, 4> def_devices = mc.get_selection_list();
mc.parse_devices(val);
const std::array<std::string, 4> devices = mc.get_selection_list();
for (usz i = 0; i < devices.size(); i++)
{
const std::string& def_device = def_devices[i];
const std::string& device = devices[i];
if (device == def_device)
continue;
indent(diff, indentation + 1);
fmt::append(diff, "Device %d: <span style=\"color:red;\">%s</span><br>", i + 1, device);
}
break;
}
}
const bool is_db_config = base_db_only && base_db_only->to_string() != def;
if (def.empty())
{
fmt::append(diff, "%s: <span style=\"color:red;\">%s</span><br>", base_name_db(is_db_config), val);
}
else
{
fmt::append(diff, "%s: <span style=\"color:red;\">%s</span> <span style=\"color:gray;\">default:</span> <span style=\"color:green;\">%s</span><br>", base_name_db(is_db_config), val, def);
}
break;
}
case cfg::type::set:
@ -208,7 +407,7 @@ bool config_checker::check_config(QString content, QString& result, bool is_log)
};
std::string diff;
print_diff_recursive(&config, diff, 0);
print_diff_recursive(config.get(), config_db_only.get(), diff, 0);
result = QString::fromStdString(diff);
return true;

View file

@ -1,13 +1,34 @@
#pragma once
#include "Emu/config_mode.h"
#include <QDialog>
#include <QLabel>
#include <QTextEdit>
class config_checker : public QDialog
{
Q_OBJECT
public:
config_checker(QWidget* parent, const QString& path, bool is_log);
enum class checker_mode
{
config,
log,
gamelist
};
bool check_config(QString content, QString& result, bool is_log);
config_checker(QWidget* parent, const QString& content_or_serial, checker_mode mode, const std::string& db_config = {});
private:
void check_config(cfg_mode mode);
bool check_config(cfg_mode mode, QString content_or_serial, QString& result);
QLabel* m_label = nullptr;
QTextEdit* m_text_box = nullptr;
checker_mode m_checker_mode = checker_mode::config;
QString m_content_or_serial;
std::string m_db_config;
std::string m_serial;
};

View file

@ -103,7 +103,7 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p
{
if (m_curl_abort)
{
network_log.notice("Download aborted");
network_log.notice("Download aborted (url='%s')", url);
return;
}
@ -114,7 +114,7 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p
if (m_curl_success)
{
network_log.notice("Download finished");
network_log.notice("Download finished (url='%s')", url);
if (check_return_code && m_download_attempts < 3)
{

View file

@ -12,6 +12,7 @@
#include "patch_manager_dialog.h"
#include "persistent_settings.h"
#include "config_database.h"
#include "config_checker.h"
#include "Utilities/File.h"
#include "Emu/system_utils.hpp"
@ -167,6 +168,26 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
QAction* pad_configure = addAction(gameinfo->has_custom_pad_config
? tr("&Change Custom Gamepad Configuration")
: tr("&Create Custom Gamepad Configuration"));
QAction* compare_config = addAction(tr("&Compare Configurations"));
connect(compare_config, &QAction::triggered, this, [this, serial]()
{
std::string db_config;
if (config_database* db = m_game_list_frame->GetConfigDatabase(); db->has_config(serial))
{
if (const std::optional<std::string> config = db->get_config(serial))
{
db_config = *config;
}
else
{
game_list_log.error("No database config found for '%s'", serial);
}
}
config_checker* dlg = new config_checker(m_game_list_frame, QString::fromStdString(serial), config_checker::checker_mode::gamelist, db_config);
dlg->open();
});
QAction* configure_patches = addAction(tr("&Manage Game Patches"));
addSeparator();

View file

@ -640,13 +640,15 @@ void gui_application::InitializeCallbacks()
on_exit();
}
const bool no_gui = !m_main_window;
if (m_main_window)
{
// Close main window in order to save its window state
m_main_window->close();
}
gui_log.notice("Quitting gui application");
gui_log.notice("Quitting gui application (force_quit=%d, no-gui=%d)", force_quit, no_gui);
quit();
return true;
}

View file

@ -201,7 +201,7 @@ void log_viewer::show_context_menu(const QPoint& pos)
connect(config, &QAction::triggered, this, [this]()
{
config_checker* dlg = new config_checker(this, m_full_log, true);
config_checker* dlg = new config_checker(this, m_full_log, config_checker::checker_mode::log);
dlg->open();
});

View file

@ -984,7 +984,7 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo
const game_compatibility* compat = m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr;
// Let the user choose the packages to install and select the order in which they shall be installed.
pkg_install_dialog dlg(file_paths, compat, this);
pkg_install_dialog dlg(file_paths, from_boot, compat, this);
connect(&dlg, &QDialog::finished, this, [&](int result)
{
if (result != QDialog::Accepted)
@ -1009,7 +1009,8 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo
if (canceled)
{
return false;
// return "true" if installation of optional packages (requested by some games at first boot) is skipped
return from_boot;
}
if (!from_boot)
@ -3249,7 +3250,7 @@ void main_window::CreateConnects()
m_gui_settings->SetValue(gui::fd_cfg_check, file_info.path());
config_checker* dlg = new config_checker(this, content, file_path.endsWith(".log") || file_path.endsWith(".log.gz"));
config_checker* dlg = new config_checker(this, content, (file_path.endsWith(".log") || file_path.endsWith(".log.gz")) ? config_checker::checker_mode::log : config_checker::checker_mode::config);
dlg->open();
});
@ -3943,6 +3944,7 @@ void main_window::closeEvent(QCloseEvent* closeEvent)
Q_EMIT NotifyWindowCloseEvent(true);
gui_log.notice("Quit with main_window::closeEvent");
Emu.Quit(true);
}

View file

@ -24,7 +24,7 @@ enum Roles
DataSizeRole = Qt::UserRole + 5,
};
pkg_install_dialog::pkg_install_dialog(const QStringList& paths, const game_compatibility* compat, QWidget* parent)
pkg_install_dialog::pkg_install_dialog(const QStringList& paths, bool from_boot, const game_compatibility* compat, QWidget* parent)
: QDialog(parent)
{
ensure(!paths.empty());
@ -148,6 +148,11 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, const game_comp
buttons->button(QDialogButtonBox::Ok)->setText(tr("Install"));
buttons->button(QDialogButtonBox::Ok)->setDefault(true);
if (from_boot)
{
buttons->button(QDialogButtonBox::Cancel)->setText(tr("Skip"));
}
m_dir_list->sortItems();
m_dir_list->setCurrentRow(0);
m_dir_list->setMinimumWidth((m_dir_list->sizeHintForColumn(0) * 125) / 100);

View file

@ -17,7 +17,7 @@ class pkg_install_dialog : public QDialog
Q_OBJECT
public:
explicit pkg_install_dialog(const QStringList& paths, const game_compatibility* compat, QWidget* parent = nullptr);
explicit pkg_install_dialog(const QStringList& paths, bool from_boot, const game_compatibility* compat, QWidget* parent = nullptr);
std::vector<compat::package_info> get_paths_to_install() const;
bool precompile_caches() const { return m_precompile_caches; }
bool create_desktop_shortcuts() const { return m_create_desktop_shortcuts; }