diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dfe9348a..0e6bdf660 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ endif() option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF) option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON) option(ENABLE_UPDATER "Enables the options to updater" ON) +option(ENABLE_UPDATER_EXE "Enables the new executable for updates" ON) # First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR. if (APPLE AND CMAKE_OSX_ARCHITECTURES) @@ -1074,6 +1075,26 @@ if (ENABLE_QT_GUI) ${EMULATOR} src/images/shadPS4.icns ) + if (ENABLE_UPDATER_EXE) + qt_add_executable(shadps4_updater + ${AUDIO_CORE} + ${IMGUI} + ${INPUT} + ${COMMON} + ${CORE} + ${SHADER_RECOMPILER} + ${VIDEO_CORE} + ${EMULATOR} + ${UPDATER} + ${RESOURCE_FILES} + src/qt_gui/main_window_themes.cpp + src/qt_gui/main_window_themes.h + src/qt_gui/background_music_player.cpp + src/qt_gui/background_music_player.h + src/updater/main.cpp + src/images/shadPS4.icns + ) + endif() else() add_executable(shadps4 ${AUDIO_CORE} @@ -1097,20 +1118,55 @@ create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb) + +if (ENABLE_UPDATER_EXE) + create_target_directory_groups(shadps4_updater) + + target_link_libraries(shadps4_updater PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) + target_link_libraries(shadps4_updater PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb) +endif() + +#Hide console for shadps4_updater +if (ENABLE_UPDATER_EXE) + if (WIN32) + set_target_properties(shadps4_updater PROPERTIES WIN32_EXECUTABLE TRUE) + endif() +endif() + +if(WIN32) + target_link_libraries(shadps4 PRIVATE shell32) + if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE shell32) + endif() +endif() + target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") +if (ENABLE_UPDATER_EXE) + target_compile_definitions(shadps4_updater PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") +endif() + if (ENABLE_DISCORD_RPC) target_compile_definitions(shadps4 PRIVATE ENABLE_DISCORD_RPC) + if (ENABLE_UPDATER_EXE) + target_compile_definitions(shadps4_updater PRIVATE ENABLE_DISCORD_RPC) + endif() endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") # Optional due to https://github.com/shadps4-emu/shadPS4/issues/1704 if (ENABLE_USERFAULTFD) target_compile_definitions(shadps4 PRIVATE ENABLE_USERFAULTFD) + if (ENABLE_UPDATER_EXE) + target_compile_definitions(shadps4_updater PRIVATE ENABLE_USERFAULTFD) + endif() endif() target_link_libraries(shadps4 PRIVATE uuid) + if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE uuid) + endif() endif() if (APPLE) @@ -1118,9 +1174,15 @@ if (APPLE) if (ENABLE_QT_GUI) set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d") set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}") + if (ENABLE_UPDATER_EXE) + set_property(TARGET shadps4_updater APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}") + endif() set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH}) else() set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path") + if (ENABLE_UPDATER_EXE) + set_property(TARGET shadps4_updater APPEND PROPERTY BUILD_RPATH "@executable_path") + endif() set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}) endif() @@ -1143,14 +1205,23 @@ if (APPLE) add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST}) add_dependencies(CopyMoltenVK MoltenVK) add_dependencies(shadps4 CopyMoltenVK) + if (ENABLE_UPDATER_EXE) + add_dependencies(shadps4_updater CopyMoltenVK) + endif() if (ARCHITECTURE STREQUAL "x86_64") # Reserve system-managed memory space. target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000) + if (ENABLE_UPDATER_EXE) + target_link_options(shadps4_updater PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000) + endif() endif() # Replacement for std::chrono::time_zone target_link_libraries(shadps4 PRIVATE date::date-tz) + if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE date::date-tz) + endif() endif() if (NOT ENABLE_QT_GUI) @@ -1159,6 +1230,9 @@ endif() if (ENABLE_QT_GUI) target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia) + if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia) + endif() add_definitions(-DENABLE_QT_GUI) if (ENABLE_UPDATER) add_definitions(-DENABLE_UPDATER) @@ -1167,6 +1241,9 @@ endif() if (WIN32) target_link_libraries(shadps4 PRIVATE mincore) + if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE mincore) + endif() if (MSVC) # MSVC likes putting opinions on what people can use, disable: @@ -1185,37 +1262,64 @@ if (WIN32) if (MSVC) target_link_libraries(shadps4 PRIVATE clang_rt.builtins-x86_64.lib) + if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE clang_rt.builtins-x86_64.lib) + endif() endif() # Disable ASLR so we can reserve the user area if (MSVC) target_link_options(shadps4 PRIVATE /DYNAMICBASE:NO) + if (ENABLE_UPDATER_EXE) + target_link_options(shadps4_updater PRIVATE /DYNAMICBASE:NO) + endif() else() target_link_options(shadps4 PRIVATE -Wl,--disable-dynamicbase) + if (ENABLE_UPDATER_EXE) + target_link_options(shadps4_updater PRIVATE -Wl,--disable-dynamicbase) + endif() endif() # Increase stack commit area (Needed, otherwise there are crashes) if (MSVC) target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000) + if (ENABLE_UPDATER_EXE) + target_link_options(shadps4_updater PRIVATE /STACK:0x200000,0x200000) + endif() else() target_link_options(shadps4 PRIVATE -Wl,--stack,2097152) + if (ENABLE_UPDATER_EXE) + target_link_options(shadps4_updater PRIVATE -Wl,--stack,2097152) + endif() endif() endif() if (WIN32) target_sources(shadps4 PRIVATE src/shadps4.rc) + if (ENABLE_UPDATER_EXE) + target_sources(shadps4_updater PRIVATE src/shadps4.rc) + endif() endif() add_definitions(-DBOOST_ASIO_STANDALONE) target_include_directories(shadps4 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +if (ENABLE_UPDATER_EXE) + target_include_directories(shadps4_updater PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +endif() # Shaders sources set(HOST_SHADERS_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/src/video_core/host_shaders) add_subdirectory(${HOST_SHADERS_INCLUDE}) add_dependencies(shadps4 host_shaders) +if (ENABLE_UPDATER_EXE) + add_dependencies(shadps4_updater host_shaders) +endif() target_include_directories(shadps4 PRIVATE ${HOST_SHADERS_INCLUDE}) +if (ENABLE_UPDATER_EXE) + target_include_directories(shadps4_updater PRIVATE ${HOST_SHADERS_INCLUDE}) +endif() # embed resources @@ -1228,11 +1332,20 @@ cmrc_add_resource_library(embedded-resources src/images/platinum.png src/images/silver.png) target_link_libraries(shadps4 PRIVATE res::embedded) +if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE res::embedded) +endif() # ImGui resources add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/imgui/renderer) add_dependencies(shadps4 ImGui_Resources) +if (ENABLE_UPDATER_EXE) + add_dependencies(shadps4_updater ImGui_Resources) +endif() target_include_directories(shadps4 PRIVATE ${IMGUI_RESOURCES_INCLUDE}) +if (ENABLE_UPDATER_EXE) + target_include_directories(shadps4_updater PRIVATE ${IMGUI_RESOURCES_INCLUDE}) +endif() if (ENABLE_QT_GUI) set_target_properties(shadps4 PROPERTIES @@ -1242,7 +1355,15 @@ if (ENABLE_QT_GUI) MACOSX_BUNDLE_ICON_FILE "shadPS4.icns" MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}" ) - + if (ENABLE_UPDATER_EXE) + set_target_properties(shadps4 PROPERTIES + # WIN32_EXECUTABLE ON + MACOSX_BUNDLE ON + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in" + MACOSX_BUNDLE_ICON_FILE "shadPS4.icns" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}" + ) + endif() set_source_files_properties(src/images/shadPS4.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) endif() @@ -1251,12 +1372,18 @@ if (UNIX AND NOT APPLE) if (ENABLE_QT_GUI) find_package(OpenSSL REQUIRED) target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES}) + if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE ${OPENSSL_LIBRARIES}) + endif() endif() endif() # Discord RPC if (ENABLE_DISCORD_RPC) target_link_libraries(shadps4 PRIVATE discord-rpc) + if (ENABLE_UPDATER_EXE) + target_link_libraries(shadps4_updater PRIVATE discord-rpc) + endif() endif() # Install rules diff --git a/src/common/config.cpp b/src/common/config.cpp index 6565ab82a..ce1272723 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -53,6 +53,7 @@ static bool isDebugDump = false; static bool isShaderDebug = false; static bool isShowSplash = false; static bool isAutoUpdate = false; +static bool isStartupUpdate = false; static bool isAlwaysShowChangelog = false; static std::string isSideTrophy = "right"; static bool isNullGpu = false; @@ -280,6 +281,10 @@ bool autoUpdate() { return isAutoUpdate; } +bool startupUpdate() { + return isStartupUpdate; +} + bool alwaysShowChangelog() { return isAlwaysShowChangelog; } @@ -388,6 +393,10 @@ void setAutoUpdate(bool enable) { isAutoUpdate = enable; } +void setStartupUpdate(bool enable) { + isStartupUpdate = enable; +} + void setAlwaysShowChangelog(bool enable) { isAlwaysShowChangelog = enable; } @@ -780,6 +789,7 @@ void load(const std::filesystem::path& path) { } isShowSplash = toml::find_or(general, "showSplash", true); isAutoUpdate = toml::find_or(general, "autoUpdate", false); + isStartupUpdate = toml::find_or(general, "startupUpdate", false); isAlwaysShowChangelog = toml::find_or(general, "alwaysShowChangelog", false); isSideTrophy = toml::find_or(general, "sideTrophy", "right"); compatibilityData = toml::find_or(general, "compatibilityEnabled", false); @@ -976,6 +986,7 @@ void save(const std::filesystem::path& path) { data["General"]["chooseHomeTab"] = chooseHomeTab; data["General"]["showSplash"] = isShowSplash; data["General"]["autoUpdate"] = isAutoUpdate; + data["General"]["startupUpdate"] = isStartupUpdate; data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog; data["General"]["sideTrophy"] = isSideTrophy; data["General"]["compatibilityEnabled"] = compatibilityData; @@ -1136,6 +1147,7 @@ void setDefaultValues() { isShaderDebug = false; isShowSplash = false; isAutoUpdate = false; + isStartupUpdate = false; isAlwaysShowChangelog = false; isSideTrophy = "right"; isNullGpu = false; diff --git a/src/common/config.h b/src/common/config.h index 404854ae2..e009d477e 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -70,6 +70,7 @@ bool debugDump(); bool collectShadersForDebug(); bool showSplash(); bool autoUpdate(); +bool startupUpdate(); bool alwaysShowChangelog(); std::string sideTrophy(); bool nullGpu(); @@ -84,6 +85,7 @@ void setDebugDump(bool enable); void setCollectShaderForDebug(bool enable); void setShowSplash(bool enable); void setAutoUpdate(bool enable); +void setStartupUpdate(bool enable); void setAlwaysShowChangelog(bool enable); void setSideTrophy(std::string side); void setNullGpu(bool enable); diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index b0858840a..75abed61f 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -444,8 +445,7 @@ void CheckUpdate::Install() { QString userPath; Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir)); - QString rootPath; - Common::FS::PathToQString(rootPath, std::filesystem::current_path()); + QString rootPath = QCoreApplication::applicationDirPath(); QString tempDirPath = userPath + "/temp_download_update"; QString startingUpdate = tr("Starting Update..."); diff --git a/src/qt_gui/main_window_themes.cpp b/src/qt_gui/main_window_themes.cpp index 624673cba..98a78ce9f 100644 --- a/src/qt_gui/main_window_themes.cpp +++ b/src/qt_gui/main_window_themes.cpp @@ -9,12 +9,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setStyleSheet(""); switch (theme) { case Theme::Dark: - mw_searchbar->setStyleSheet( - "QLineEdit {" - "background-color: #1e1e1e; color: #ffffff; border: 1px solid #ffffff; " - "border-radius: 4px; padding: 5px; }" - "QLineEdit:focus {" - "border: 1px solid #2A82DA; }"); + if (mw_searchbar) { + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #1e1e1e; color: #ffffff; border: 1px solid #ffffff; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); + } themePalette.setColor(QPalette::Window, QColor(50, 50, 50)); themePalette.setColor(QPalette::WindowText, Qt::white); themePalette.setColor(QPalette::Base, QColor(20, 20, 20)); @@ -31,12 +33,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::Light: - mw_searchbar->setStyleSheet( - "QLineEdit {" - "background-color: #ffffff; color: #000000; border: 1px solid #000000; " - "border-radius: 4px; padding: 5px; }" - "QLineEdit:focus {" - "border: 1px solid #2A82DA; }"); + if (mw_searchbar) { + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #ffffff; color: #000000; border: 1px solid #000000; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); + } themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray themePalette.setColor(QPalette::WindowText, Qt::black); // Black themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish @@ -52,12 +56,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::Green: - mw_searchbar->setStyleSheet( - "QLineEdit {" - "background-color: #192819; color: #ffffff; border: 1px solid #ffffff; " - "border-radius: 4px; padding: 5px; }" - "QLineEdit:focus {" - "border: 1px solid #2A82DA; }"); + if (mw_searchbar) { + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #192819; color: #ffffff; border: 1px solid #ffffff; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); + } themePalette.setColor(QPalette::Window, QColor(53, 69, 53)); // Dark green background themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(25, 40, 25)); // Darker green base @@ -76,12 +82,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::Blue: - mw_searchbar->setStyleSheet( - "QLineEdit {" - "background-color: #14283c; color: #ffffff; border: 1px solid #ffffff; " - "border-radius: 4px; padding: 5px; }" - "QLineEdit:focus {" - "border: 1px solid #2A82DA; }"); + if (mw_searchbar) { + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #14283c; color: #ffffff; border: 1px solid #ffffff; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); + } themePalette.setColor(QPalette::Window, QColor(40, 60, 90)); // Dark blue background themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(20, 40, 60)); // Darker blue base @@ -101,12 +109,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::Violet: - mw_searchbar->setStyleSheet( - "QLineEdit {" - "background-color: #501e5a; color: #ffffff; border: 1px solid #ffffff; " - "border-radius: 4px; padding: 5px; }" - "QLineEdit:focus {" - "border: 1px solid #2A82DA; }"); + if (mw_searchbar) { + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #501e5a; color: #ffffff; border: 1px solid #ffffff; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); + } themePalette.setColor(QPalette::Window, QColor(100, 50, 120)); // Violet background themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(80, 30, 90)); // Darker violet base @@ -126,12 +136,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::Gruvbox: - mw_searchbar->setStyleSheet( - "QLineEdit {" - "background-color: #1d2021; color: #f9f5d7; border: 1px solid #f9f5d7; " - "border-radius: 4px; padding: 5px; }" - "QLineEdit:focus {" - "border: 1px solid #83A598; }"); + if (mw_searchbar) { + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #1d2021; color: #f9f5d7; border: 1px solid #f9f5d7; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #83A598; }"); + } themePalette.setColor(QPalette::Window, QColor(29, 32, 33)); themePalette.setColor(QPalette::WindowText, QColor(249, 245, 215)); themePalette.setColor(QPalette::Base, QColor(29, 32, 33)); @@ -148,12 +160,14 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::TokyoNight: - mw_searchbar->setStyleSheet( - "QLineEdit {" - "background-color: #1a1b26; color: #9d7cd8; border: 1px solid #9d7cd8; " - "border-radius: 4px; padding: 5px; }" - "QLineEdit:focus {" - "border: 1px solid #7aa2f7; }"); + if (mw_searchbar) { + mw_searchbar->setStyleSheet( + "QLineEdit {" + "background-color: #1a1b26; color: #9d7cd8; border: 1px solid #9d7cd8; " + "border-radius: 4px; padding: 5px; }" + "QLineEdit:focus {" + "border: 1px solid #7aa2f7; }"); + } themePalette.setColor(QPalette::Window, QColor(31, 35, 53)); themePalette.setColor(QPalette::WindowText, QColor(192, 202, 245)); themePalette.setColor(QPalette::Base, QColor(25, 28, 39)); @@ -170,8 +184,10 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { qApp->setPalette(themePalette); break; case Theme::Oled: - mw_searchbar->setStyleSheet("QLineEdit:focus {" - "border: 1px solid #2A82DA; }"); + if (mw_searchbar) { + mw_searchbar->setStyleSheet("QLineEdit:focus {" + "border: 1px solid #2A82DA; }"); + } themePalette.setColor(QPalette::Window, Qt::black); themePalette.setColor(QPalette::WindowText, Qt::white); themePalette.setColor(QPalette::Base, Qt::black); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 914cc5470..1f3913f5e 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -3,11 +3,21 @@ #include #include +#include #include +#include #include #include #include +#include +#include +#include +#include +#include #include +#include +#include + #include "common/config.h" #include "common/scm_rev.h" @@ -178,12 +188,29 @@ SettingsDialog::SettingsDialog(std::shared_ptr m_compat_ connect(ui->updateCheckBox, &QCheckBox::stateChanged, this, [](int state) { Config::setAutoUpdate(state == Qt::Checked); }); + connect(ui->startupUpdateCheckBox, &QCheckBox::stateChanged, this, [this](int state) { + Config::setStartupUpdate(state == Qt::Checked); + if (state == Qt::Checked) + AddUpdaterToStartup(); + else + RemoveUpdaterFromStartup(); + }); + connect(ui->changelogCheckBox, &QCheckBox::stateChanged, this, [](int state) { Config::setAlwaysShowChangelog(state == Qt::Checked); }); #else connect(ui->updateCheckBox, &QCheckBox::checkStateChanged, this, [](Qt::CheckState state) { Config::setAutoUpdate(state == Qt::Checked); }); + connect(ui->startupUpdateCheckBox, &QCheckBox::checkStateChanged, this, + [this](Qt::CheckState state) { + Config::setStartupUpdate(state == Qt::Checked); + if (state == Qt::Checked) + AddUpdaterToStartup(); + else + RemoveUpdaterFromStartup(); + }); + connect(ui->changelogCheckBox, &QCheckBox::checkStateChanged, this, [](Qt::CheckState state) { Config::setAlwaysShowChangelog(state == Qt::Checked); }); #endif @@ -502,6 +529,8 @@ void SettingsDialog::LoadValuesFromConfig() { #ifdef ENABLE_UPDATER ui->updateCheckBox->setChecked(toml::find_or(data, "General", "autoUpdate", false)); + ui->startupUpdateCheckBox->setChecked( + toml::find_or(data, "General", "startupUpdate", false)); ui->changelogCheckBox->setChecked( toml::find_or(data, "General", "alwaysShowChangelog", false)); @@ -785,6 +814,7 @@ void SettingsDialog::UpdateSettings() { Config::setCollectShaderForDebug(ui->collectShaderCheckBox->isChecked()); Config::setCopyGPUCmdBuffers(ui->copyGPUBuffersCheckBox->isChecked()); Config::setAutoUpdate(ui->updateCheckBox->isChecked()); + Config::setStartupUpdate(ui->startupUpdateCheckBox->isChecked()); Config::setAlwaysShowChangelog(ui->changelogCheckBox->isChecked()); Config::setUpdateChannel(channelMap.value(ui->updateComboBox->currentText()).toStdString()); Config::setChooseHomeTab( @@ -862,3 +892,89 @@ void SettingsDialog::ResetInstallFolders() { Config::setAllGameInstallDirs(settings_install_dirs_config); } } + +void SettingsDialog::AddUpdaterToStartup() { + +#ifdef _WIN32 + + QString tempDirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + + "/Temp/temp_download_update"; + QDir().mkpath(tempDirPath); + + QString scriptFileName = + tempDirPath + "/create_task_" + QUuid::createUuid().toString(QUuid::WithoutBraces) + ".ps1"; + + QString taskName = "ShadPS4Updater"; + QString exePath = QCoreApplication::applicationFilePath() + .replace("shadps4.exe", "shadps4_updater.exe") + .replace("/", "\\"); + + QString scriptContent = + QStringLiteral("$Action = New-ScheduledTaskAction -Execute '%1'\n" + "$Trigger = New-ScheduledTaskTrigger -AtLogOn\n" + "$Principal = New-ScheduledTaskPrincipal -UserId \"$env:USERNAME\" " + "-LogonType Interactive -RunLevel Highest\n" + "$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries " + "-DontStopIfGoingOnBatteries\n" + "Register-ScheduledTask -TaskName '%2' -Action $Action -Trigger $Trigger " + "-Principal $Principal -Settings $Settings -Force\n") + .arg(exePath, taskName); + + QFile scriptFile(scriptFileName); + if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&scriptFile); + out << scriptContent; + scriptFile.close(); + + // Elevate PowerShell to run the script + ShellExecuteW( + nullptr, + L"runas", // Triggers UAC prompt + L"powershell.exe", + (L"-ExecutionPolicy Bypass -File \"" + scriptFileName.toStdWString() + L"\"").c_str(), + nullptr, SW_HIDE); + } + +#endif +} + +void SettingsDialog::RemoveUpdaterFromStartup() { + +#ifdef _WIN32 + + QString tempDirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + + "/Temp/temp_download_update"; + QDir().mkpath(tempDirPath); + + QString scriptFileName = + tempDirPath + "/remove_task_" + QUuid::createUuid().toString(QUuid::WithoutBraces) + ".ps1"; + + QString taskName = "ShadPS4Updater"; + + QString scriptContent = + QStringLiteral( + "$taskName = '%1'\n" + "if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {\n" + " Unregister-ScheduledTask -TaskName $taskName -Confirm:$false\n" + "}\n" + "Remove-Item -LiteralPath $MyInvocation.MyCommand.Path -Force\n" // Optional: + // self-delete the + // script + ) + .arg(taskName); + + QFile scriptFile(scriptFileName); + if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&scriptFile); + out << scriptContent; + scriptFile.close(); + + // Run PowerShell script with admin privileges + ShellExecuteW( + nullptr, L"runas", L"powershell.exe", + (L"-ExecutionPolicy Bypass -File \"" + scriptFileName.toStdWString() + L"\"").c_str(), + nullptr, SW_HIDE); + } + +#endif +} diff --git a/src/qt_gui/settings_dialog.h b/src/qt_gui/settings_dialog.h index cdf9be80e..ff5f99c1e 100644 --- a/src/qt_gui/settings_dialog.h +++ b/src/qt_gui/settings_dialog.h @@ -42,6 +42,8 @@ private: void OnLanguageChanged(int index); void OnCursorStateChanged(s16 index); void closeEvent(QCloseEvent* event) override; + void AddUpdaterToStartup(); + void RemoveUpdaterFromStartup(); std::unique_ptr ui; diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 20e26775d..5dd73d544 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -415,6 +415,13 @@ + + + + Check for Updates at OS Startup + + + diff --git a/src/updater/main.cpp b/src/updater/main.cpp new file mode 100644 index 000000000..dcf197fc8 --- /dev/null +++ b/src/updater/main.cpp @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/config.h" +#include "common/path_util.h" +#include "core/file_sys/fs.h" +#include "qt_gui/check_update.h" +#include "qt_gui/main_window_themes.h" + +#ifdef _WIN32 +#include +#endif + +// Custom message handler to ignore Qt logs +void customMessageHandler(QtMsgType, const QMessageLogContext&, const QString&) {} + +int main(int argc, char* argv[]) { +#ifdef _WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + + QApplication u(argc, argv); + + QApplication::setDesktopFileName("net.shadps4.shadPS4_updater"); + QApplication::setStyle("Fusion"); + + // Load configurations + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + Config::load(user_dir / "config.toml"); + + WindowThemes m_window_themes; + + Theme lastTheme = static_cast(Config::getMainWindowTheme()); + m_window_themes.SetWindowTheme(lastTheme, nullptr); + + if (Config::startupUpdate()) { + auto checkUpdate = new CheckUpdate(false); + checkUpdate->exec(); + } + + // Ignore Qt logs + qInstallMessageHandler(customMessageHandler); + + return 0; +}