diff options
50 files changed, 523 insertions, 170 deletions
diff --git a/.ci/scripts/linux/upload.sh b/.ci/scripts/linux/upload.sh index 8173c5728..e0f336427 100755 --- a/.ci/scripts/linux/upload.sh +++ b/.ci/scripts/linux/upload.sh @@ -5,21 +5,24 @@ . .ci/scripts/common/pre-upload.sh -APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}.AppImage" -REV_NAME="yuzu-linux-${GITDATE}-${GITREV}" +APPIMAGE_NAME="yuzu-${RELEASE_NAME}-${GITDATE}-${GITREV}.AppImage" +BASE_NAME="yuzu-linux" +REV_NAME="${BASE_NAME}-${GITDATE}-${GITREV}" ARCHIVE_NAME="${REV_NAME}.tar.xz" COMPRESSION_FLAGS="-cJvf" -if [ "${RELEASE_NAME}" = "mainline" ]; then - DIR_NAME="${REV_NAME}" +if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then + DIR_NAME="${BASE_NAME}-${RELEASE_NAME}" else - DIR_NAME="${REV_NAME}_${RELEASE_NAME}" + DIR_NAME="${REV_NAME}-${RELEASE_NAME}" fi mkdir "$DIR_NAME" cp build/bin/yuzu-cmd "$DIR_NAME" -cp build/bin/yuzu "$DIR_NAME" +if [ "${RELEASE_NAME}" != "early-access" ] && [ "${RELEASE_NAME}" != "mainline" ]; then + cp build/bin/yuzu "$DIR_NAME" +fi # Build an AppImage cd build @@ -32,6 +35,11 @@ if ! ./appimagetool-x86_64.AppImage --version; then export APPIMAGE_EXTRACT_AND_RUN=1 fi +# Don't let AppImageLauncher ask to integrate EA +if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then + echo "X-AppImage-Integrate=false" >> AppDir/org.yuzu_emu.yuzu.desktop +fi + if [ "${RELEASE_NAME}" = "mainline" ]; then # Generate update information if releasing to mainline ./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}" @@ -46,4 +54,9 @@ if [ -f "build/${APPIMAGE_NAME}.zsync" ]; then cp "build/${APPIMAGE_NAME}.zsync" "${ARTIFACTS_DIR}/" fi +# Copy the AppImage to the general release directory and remove git revision info +if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ]; then + cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage" +fi + . .ci/scripts/common/post-upload.sh diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh index 790ba8218..6f522feed 100755 --- a/.ci/scripts/windows/docker.sh +++ b/.ci/scripts/windows/docker.sh @@ -21,6 +21,7 @@ cmake .. \ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \ -DENABLE_QT_TRANSLATION=ON \ -DUSE_CCACHE=ON \ + -DYUZU_CRASH_DUMPS=ON \ -DYUZU_USE_BUNDLED_SDL2=OFF \ -DYUZU_USE_EXTERNAL_SDL2=OFF \ -DYUZU_TESTS=OFF \ diff --git a/.ci/scripts/windows/upload.ps1 b/.ci/scripts/windows/upload.ps1 index d463281de..21abcd752 100644 --- a/.ci/scripts/windows/upload.ps1 +++ b/.ci/scripts/windows/upload.ps1 @@ -65,8 +65,8 @@ if ("$env:GITHUB_ACTIONS" -eq "true") { # None of the other GHA builds are including source, so commenting out today #Copy-Item $MSVC_SOURCE_TARXZ -Destination "artifacts" - # Are debug symbols important? - # cp .\build\bin\yuzu*.pdb .\pdb\ + # Debugging symbols + cp .\build\bin\yuzu*.pdb .\artifacts\ # Write out a tag BUILD_TAG to environment for the Upload step # We're getting ${{ github.event.number }} as $env:PR_NUMBER" diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index a2ee71bd8..ea405e5dc 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml @@ -9,7 +9,7 @@ parameters: steps: - script: choco install vulkan-sdk displayName: 'Install vulkan-sdk' -- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release .. && cd .. +- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 17 2022" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd .. displayName: 'Configure CMake' - task: MSBuild@1 displayName: 'Build' diff --git a/.ci/yuzu-patreon-step2.yml b/.ci/yuzu-patreon-step2.yml index 5d5b140fd..71a23ebe6 100644 --- a/.ci/yuzu-patreon-step2.yml +++ b/.ci/yuzu-patreon-step2.yml @@ -11,9 +11,30 @@ stages: - stage: build displayName: 'build' jobs: - - job: build + - job: linux timeoutInMinutes: 120 - displayName: 'windows-msvc' + displayName: 'linux' + pool: + vmImage: ubuntu-latest + strategy: + maxParallel: 10 + matrix: + linux: + BuildSuffix: 'linux' + ScriptFolder: 'linux' + steps: + - template: ./templates/sync-source.yml + parameters: + artifactSource: $(parameters.artifactSource) + needSubmodules: 'true' + - template: ./templates/build-single.yml + parameters: + artifactSource: 'false' + cache: $(parameters.cache) + version: $(DisplayVersion) + - job: msvc + timeoutInMinutes: 120 + displayName: 'windows' pool: vmImage: windows-2022 steps: diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 733a52764..7cde8380b 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -104,7 +104,7 @@ jobs: run: | glslangValidator --version mkdir build - cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DGIT_BRANCH=pr-verify + cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DGIT_BRANCH=pr-verify -DYUZU_CRASH_DUMPS=ON - name: Build run: cmake --build build - name: Cache Summary diff --git a/.gitignore b/.gitignore index cdf37962a..a5f7248c7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Build directory -[Bb]uild/ +[Bb]uild*/ doc-build/ # Generated source files diff --git a/.reuse/dep5 b/.reuse/dep5 index 5ba017494..9a90f9eb6 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -52,6 +52,8 @@ Files: dist/qt_themes/colorful/icons/16x16/lock.png dist/qt_themes/colorful/icons/48x48/chip.png dist/qt_themes/colorful/icons/48x48/folder.png dist/qt_themes/colorful_dark/icons/16x16/lock.png + dist/qt_themes/colorful/icons/16x16/info.png + dist/qt_themes/colorful/icons/16x16/sync.png Copyright: Icons8 License: MIT Comment: https://github.com/icons8/flat-color-icons @@ -68,8 +70,8 @@ Copyright: Ionic (http://ionic.io/) License: MIT Files: dist/qt_themes/colorful/icons/48x48/star.png - dist/qt_themes/default/icons/16x16/checked.png - dist/qt_themes/default/icons/16x16/failed.png + dist/qt_themes/colorful/icons/16x16/checked.png + dist/qt_themes/colorful/icons/16x16/failed.png Copyright: SVG Repo License: CC0-1.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ab0ea589..20dd1383f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,8 @@ option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON) option(YUZU_TESTS "Compile tests" ON) +CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF) + option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}") option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON) @@ -46,6 +48,9 @@ if (YUZU_USE_BUNDLED_VCPKG) if (YUZU_TESTS) list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests") endif() + if (YUZU_CRASH_DUMPS) + list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp") + endif() include(${CMAKE_SOURCE_DIR}/externals/vcpkg/scripts/buildsystems/vcpkg.cmake) elseif(NOT "$ENV{VCPKG_TOOLCHAIN_FILE}" STREQUAL "") @@ -447,6 +452,13 @@ elseif (WIN32) # PSAPI is the Process Status API set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version) endif() + + if (YUZU_CRASH_DUMPS) + find_library(DBGHELP_LIBRARY dbghelp) + if ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND") + message(FATAL_ERROR "YUZU_CRASH_DUMPS enabled but dbghelp library not found") + endif() + endif() elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$") set(PLATFORM_LIBRARIES rt) endif() diff --git a/dist/qt_themes/default/icons/16x16/checked.png b/dist/qt_themes/colorful/icons/16x16/checked.png Binary files differindex b9e64e9e0..b9e64e9e0 100644 --- a/dist/qt_themes/default/icons/16x16/checked.png +++ b/dist/qt_themes/colorful/icons/16x16/checked.png diff --git a/dist/qt_themes/default/icons/16x16/failed.png b/dist/qt_themes/colorful/icons/16x16/failed.png Binary files differindex a1872835d..a1872835d 100644 --- a/dist/qt_themes/default/icons/16x16/failed.png +++ b/dist/qt_themes/colorful/icons/16x16/failed.png diff --git a/dist/qt_themes/colorful/icons/16x16/info.png b/dist/qt_themes/colorful/icons/16x16/info.png Binary files differnew file mode 100644 index 000000000..8b9330f4c --- /dev/null +++ b/dist/qt_themes/colorful/icons/16x16/info.png diff --git a/dist/qt_themes/colorful/icons/16x16/sync.png b/dist/qt_themes/colorful/icons/16x16/sync.png Binary files differnew file mode 100644 index 000000000..0d57789c3 --- /dev/null +++ b/dist/qt_themes/colorful/icons/16x16/sync.png diff --git a/dist/qt_themes/default/icons/16x16/view-refresh.png b/dist/qt_themes/colorful/icons/16x16/view-refresh.png Binary files differindex 69f9474ac..69f9474ac 100644 --- a/dist/qt_themes/default/icons/16x16/view-refresh.png +++ b/dist/qt_themes/colorful/icons/16x16/view-refresh.png diff --git a/dist/qt_themes/default/icons/48x48/no_avatar.png b/dist/qt_themes/colorful/icons/48x48/no_avatar.png Binary files differindex 76f812349..76f812349 100644 --- a/dist/qt_themes/default/icons/48x48/no_avatar.png +++ b/dist/qt_themes/colorful/icons/48x48/no_avatar.png diff --git a/dist/qt_themes/colorful/icons/index.theme b/dist/qt_themes/colorful/icons/index.theme index b452aca16..6eb3c6949 100644 --- a/dist/qt_themes/colorful/icons/index.theme +++ b/dist/qt_themes/colorful/icons/index.theme @@ -1,7 +1,6 @@ [Icon Theme] Name=colorful Comment=Colorful theme -Inherits=default Directories=16x16,48x48,256x256 [16x16] diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc index 507e0e58b..82cd367be 100644 --- a/dist/qt_themes/colorful/style.qrc +++ b/dist/qt_themes/colorful/style.qrc @@ -6,14 +6,20 @@ SPDX-License-Identifier: GPL-2.0-or-later <RCC> <qresource prefix="icons/colorful"> <file alias="index.theme">icons/index.theme</file> + <file alias="16x16/checked.png">icons/16x16/checked.png</file> <file alias="16x16/connected.png">icons/16x16/connected.png</file> <file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file> <file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file> + <file alias="16x16/failed.png">icons/16x16/failed.png</file> + <file alias="16x16/info.png">icons/16x16/info.png</file> <file alias="16x16/lock.png">icons/16x16/lock.png</file> + <file alias="16x16/sync.png">icons/16x16/sync.png</file> + <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file> <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> <file alias="48x48/chip.png">icons/48x48/chip.png</file> <file alias="48x48/folder.png">icons/48x48/folder.png</file> <file alias="48x48/list-add.png">icons/48x48/list-add.png</file> + <file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file> <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> <file alias="48x48/star.png">icons/48x48/star.png</file> <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> diff --git a/dist/qt_themes/colorful_dark/style.qrc b/dist/qt_themes/colorful_dark/style.qrc index 9853fd438..72451ef02 100644 --- a/dist/qt_themes/colorful_dark/style.qrc +++ b/dist/qt_themes/colorful_dark/style.qrc @@ -5,19 +5,9 @@ SPDX-License-Identifier: GPL-2.0-or-later <RCC> <qresource prefix="icons/colorful_dark"> - <file alias="16x16/connected.png">../colorful/icons/16x16/connected.png</file> - <file alias="16x16/connected_notification.png">../colorful/icons/16x16/connected_notification.png</file> - <file alias="16x16/disconnected.png">../colorful/icons/16x16/disconnected.png</file> <file alias="index.theme">icons/index.theme</file> <file alias="16x16/lock.png">icons/16x16/lock.png</file> <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file> - <file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file> - <file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file> - <file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file> - <file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file> - <file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file> - <file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file> - <file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file> </qresource> <qresource prefix="qss_icons"> diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc index a07f2a9c1..2e01a3434 100644 --- a/dist/qt_themes/default/default.qrc +++ b/dist/qt_themes/default/default.qrc @@ -5,23 +5,20 @@ SPDX-License-Identifier: GPL-2.0-or-later <RCC> <qresource prefix="icons/default"> + <!-- "colorful" is now the default theme, add new icons there --> <file alias="index.theme">icons/index.theme</file> - <file alias="16x16/checked.png">icons/16x16/checked.png</file> - <file alias="16x16/failed.png">icons/16x16/failed.png</file> - <file alias="16x16/lock.png">icons/16x16/lock.png</file> <file alias="16x16/connected.png">icons/16x16/connected.png</file> - <file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file> <file alias="16x16/connected_notification.png">icons/16x16/connected_notification.png</file> - <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file> + <file alias="16x16/disconnected.png">icons/16x16/disconnected.png</file> + <file alias="16x16/lock.png">icons/16x16/lock.png</file> <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> <file alias="48x48/chip.png">icons/48x48/chip.png</file> <file alias="48x48/folder.png">icons/48x48/folder.png</file> - <file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file> <file alias="48x48/list-add.png">icons/48x48/list-add.png</file> <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> <file alias="48x48/star.png">icons/48x48/star.png</file> - <file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file> <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> + <file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file> </qresource> <qresource prefix="default"> <file>style.qss</file> diff --git a/dist/qt_themes/default/icons/index.theme b/dist/qt_themes/default/icons/index.theme index 1edbe6408..21b35e3e3 100644 --- a/dist/qt_themes/default/icons/index.theme +++ b/dist/qt_themes/default/icons/index.theme @@ -1,6 +1,7 @@ [Icon Theme] Name=default Comment=default theme +Inherits=colorful Directories=16x16,48x48,256x256 [16x16] @@ -10,4 +11,4 @@ Size=16 Size=48 [256x256] -Size=256
\ No newline at end of file +Size=256 diff --git a/dist/qt_themes/qdarkstyle/icons/index.theme b/dist/qt_themes/qdarkstyle/icons/index.theme index d1e12f3ef..502717617 100644 --- a/dist/qt_themes/qdarkstyle/icons/index.theme +++ b/dist/qt_themes/qdarkstyle/icons/index.theme @@ -1,7 +1,7 @@ [Icon Theme] Name=qdarkstyle Comment=dark theme -Inherits=default +Inherits=colorful Directories=16x16,48x48,256x256 [16x16] @@ -11,4 +11,4 @@ Size=16 Size=48 [256x256] -Size=256
\ No newline at end of file +Size=256 diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/index.theme b/dist/qt_themes/qdarkstyle_midnight_blue/icons/index.theme index 447a6c8be..20f9f6d63 100644 --- a/dist/qt_themes/qdarkstyle_midnight_blue/icons/index.theme +++ b/dist/qt_themes/qdarkstyle_midnight_blue/icons/index.theme @@ -1,7 +1,7 @@ [Icon Theme] Name=qdarkstyle_midnight_blue Comment=dark theme -Inherits=default +Inherits=colorful Directories=16x16,48x48,256x256 [16x16] diff --git a/src/audio_core/audio_in_manager.cpp b/src/audio_core/audio_in_manager.cpp index 4aadb7fd6..f39fb4002 100644 --- a/src/audio_core/audio_in_manager.cpp +++ b/src/audio_core/audio_in_manager.cpp @@ -82,7 +82,7 @@ u32 Manager::GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceN auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)}; if (input_devices.size() > 1) { - names.push_back(AudioRenderer::AudioDevice::AudioDeviceName("Uac")); + names.emplace_back("Uac"); return 1; } return 0; diff --git a/src/audio_core/audio_out_manager.cpp b/src/audio_core/audio_out_manager.cpp index 71d67de64..1766efde1 100644 --- a/src/audio_core/audio_out_manager.cpp +++ b/src/audio_core/audio_out_manager.cpp @@ -74,7 +74,7 @@ void Manager::BufferReleaseAndRegister() { u32 Manager::GetAudioOutDeviceNames( std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const { - names.push_back({"DeviceOut"}); + names.emplace_back("DeviceOut"); return 1; } diff --git a/src/audio_core/renderer/audio_device.cpp b/src/audio_core/renderer/audio_device.cpp index b2d34e45a..0d9d8f6ce 100644 --- a/src/audio_core/renderer/audio_device.cpp +++ b/src/audio_core/renderer/audio_device.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <array> +#include <span> + #include "audio_core/audio_core.h" #include "audio_core/common/feature_support.h" #include "audio_core/renderer/audio_device.h" @@ -9,14 +12,33 @@ namespace AudioCore::AudioRenderer { +constexpr std::array usb_device_names{ + AudioDevice::AudioDeviceName{"AudioStereoJackOutput"}, + AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"}, + AudioDevice::AudioDeviceName{"AudioTvOutput"}, + AudioDevice::AudioDeviceName{"AudioUsbDeviceOutput"}, +}; + +constexpr std::array device_names{ + AudioDevice::AudioDeviceName{"AudioStereoJackOutput"}, + AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"}, + AudioDevice::AudioDeviceName{"AudioTvOutput"}, +}; + +constexpr std::array output_device_names{ + AudioDevice::AudioDeviceName{"AudioBuiltInSpeakerOutput"}, + AudioDevice::AudioDeviceName{"AudioTvOutput"}, + AudioDevice::AudioDeviceName{"AudioExternalOutput"}, +}; + AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_, const u32 revision) : output_sink{system.AudioCore().GetOutputSink()}, applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {} u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, - const size_t max_count) { - std::span<AudioDeviceName> names{}; + const size_t max_count) const { + std::span<const AudioDeviceName> names{}; if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) { names = usb_device_names; @@ -24,7 +46,7 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, names = device_names; } - u32 out_count{static_cast<u32>(std::min(max_count, names.size()))}; + const u32 out_count{static_cast<u32>(std::min(max_count, names.size()))}; for (u32 i = 0; i < out_count; i++) { out_buffer.push_back(names[i]); } @@ -32,8 +54,8 @@ u32 AudioDevice::ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, } u32 AudioDevice::ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, - const size_t max_count) { - u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))}; + const size_t max_count) const { + const u32 out_count{static_cast<u32>(std::min(max_count, output_device_names.size()))}; for (u32 i = 0; i < out_count; i++) { out_buffer.push_back(output_device_names[i]); diff --git a/src/audio_core/renderer/audio_device.h b/src/audio_core/renderer/audio_device.h index 16522ad2f..dd6be70ee 100644 --- a/src/audio_core/renderer/audio_device.h +++ b/src/audio_core/renderer/audio_device.h @@ -3,7 +3,7 @@ #pragma once -#include <span> +#include <string_view> #include "audio_core/audio_render_manager.h" @@ -23,21 +23,13 @@ namespace AudioRenderer { class AudioDevice { public: struct AudioDeviceName { - std::array<char, 0x100> name; + std::array<char, 0x100> name{}; - AudioDeviceName(const char* name_) { - std::strncpy(name.data(), name_, name.size()); + constexpr AudioDeviceName(std::string_view name_) { + name_.copy(name.data(), name.size() - 1); } }; - std::array<AudioDeviceName, 4> usb_device_names{"AudioStereoJackOutput", - "AudioBuiltInSpeakerOutput", "AudioTvOutput", - "AudioUsbDeviceOutput"}; - std::array<AudioDeviceName, 3> device_names{"AudioStereoJackOutput", - "AudioBuiltInSpeakerOutput", "AudioTvOutput"}; - std::array<AudioDeviceName, 3> output_device_names{"AudioBuiltInSpeakerOutput", "AudioTvOutput", - "AudioExternalOutput"}; - explicit AudioDevice(Core::System& system, u64 applet_resource_user_id, u32 revision); /** @@ -47,7 +39,7 @@ public: * @param max_count - Maximum number of devices to write (count of out_buffer). * @return Number of device names written. */ - u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count); + u32 ListAudioDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const; /** * Get a list of the available output devices. @@ -57,7 +49,7 @@ public: * @param max_count - Maximum number of devices to write (count of out_buffer). * @return Number of device names written. */ - u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count); + u32 ListAudioOutputDeviceName(std::vector<AudioDeviceName>& out_buffer, size_t max_count) const; /** * Set the volume of all streams in the backend sink. diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index b1e0ba6cc..68436a4bc 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -19,7 +19,7 @@ find_package(Git QUIET) add_custom_command(OUTPUT scm_rev.cpp COMMAND ${CMAKE_COMMAND} - -DSRC_DIR=${CMAKE_SOURCE_DIR} + -DSRC_DIR=${PROJECT_SOURCE_DIR} -DBUILD_REPOSITORY=${BUILD_REPOSITORY} -DTITLE_BAR_FORMAT_IDLE=${TITLE_BAR_FORMAT_IDLE} -DTITLE_BAR_FORMAT_RUNNING=${TITLE_BAR_FORMAT_RUNNING} @@ -31,13 +31,13 @@ add_custom_command(OUTPUT scm_rev.cpp -DGIT_BRANCH=${GIT_BRANCH} -DBUILD_FULLNAME=${BUILD_FULLNAME} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} - -P ${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake + -P ${PROJECT_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake DEPENDS # Check that the scm_rev files haven't changed "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h" # technically we should regenerate if the git version changed, but its not worth the effort imo - "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" + "${PROJECT_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake" VERBATIM ) diff --git a/src/common/settings.h b/src/common/settings.h index 13651de57..851812f28 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -530,6 +530,7 @@ struct Values { Setting<bool> use_debug_asserts{false, "use_debug_asserts"}; Setting<bool> use_auto_stub{false, "use_auto_stub"}; Setting<bool> enable_all_controllers{false, "enable_all_controllers"}; + Setting<bool> create_crash_dumps{false, "create_crash_dumps"}; // Miscellaneous Setting<std::string> log_filter{"*:Info", "log_filter"}; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 806e7ff6c..405a2f993 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -4,12 +4,6 @@ add_library(core STATIC arm/arm_interface.h arm/arm_interface.cpp - arm/dynarmic/arm_dynarmic_32.cpp - arm/dynarmic/arm_dynarmic_32.h - arm/dynarmic/arm_dynarmic_64.cpp - arm/dynarmic/arm_dynarmic_64.h - arm/dynarmic/arm_dynarmic_cp15.cpp - arm/dynarmic/arm_dynarmic_cp15.h arm/dynarmic/arm_exclusive_monitor.cpp arm/dynarmic/arm_exclusive_monitor.h arm/exclusive_monitor.cpp diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index a44dd842a..49c092301 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -246,9 +246,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { const auto write_count = static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName)); std::vector<AudioDevice::AudioDeviceName> device_names{}; - std::string print_names{}; if (write_count > 0) { - device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut")); + device_names.emplace_back("DeviceOut"); LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut"); } else { LOG_DEBUG(Service_Audio, "called. Empty buffer passed in."); diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index bc69117c6..6fb07c37d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -252,7 +252,7 @@ private: std::vector<AudioDevice::AudioDeviceName> out_names{}; - u32 out_count = impl->ListAudioDeviceName(out_names, in_count); + const u32 out_count = impl->ListAudioDeviceName(out_names, in_count); std::string out{}; for (u32 i = 0; i < out_count; i++) { @@ -365,7 +365,7 @@ private: std::vector<AudioDevice::AudioDeviceName> out_names{}; - u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); + const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count); std::string out{}; for (u32 i = 0; i < out_count; i++) { diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 50007338f..29d506c47 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -208,6 +208,16 @@ add_executable(yuzu yuzu.rc ) +if (WIN32 AND YUZU_CRASH_DUMPS) + target_sources(yuzu PRIVATE + mini_dump.cpp + mini_dump.h + ) + + target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY}) + target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP) +endif() + file(GLOB COMPAT_LIST ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 8be311fcb..1d8072243 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -63,7 +63,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( InputCommon::InputSubsystem* input_subsystem_, Core::System& system_) : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()), parameters(std::move(parameters_)), input_subsystem{input_subsystem_}, - input_profiles(std::make_unique<InputProfiles>(system_)), system{system_} { + input_profiles(std::make_unique<InputProfiles>()), system{system_} { ui->setupUi(this); player_widgets = { diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 8ecd87150..a4ed68422 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -15,8 +15,7 @@ namespace FS = Common::FS; -Config::Config(Core::System& system_, const std::string& config_name, ConfigType config_type) - : type(config_type), system{system_} { +Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) { global = config_type == ConfigType::GlobalConfig; Initialize(config_name); @@ -546,6 +545,7 @@ void Config::ReadDebuggingValues() { ReadBasicSetting(Settings::values.use_debug_asserts); ReadBasicSetting(Settings::values.use_auto_stub); ReadBasicSetting(Settings::values.enable_all_controllers); + ReadBasicSetting(Settings::values.create_crash_dumps); qt_config->endGroup(); } @@ -1161,6 +1161,7 @@ void Config::SaveDebuggingValues() { WriteBasicSetting(Settings::values.use_debug_asserts); WriteBasicSetting(Settings::values.disable_macro_jit); WriteBasicSetting(Settings::values.enable_all_controllers); + WriteBasicSetting(Settings::values.create_crash_dumps); qt_config->endGroup(); } @@ -1547,7 +1548,6 @@ void Config::Reload() { ReadValues(); // To apply default value changes SaveValues(); - system.ApplySettings(); } void Config::Save() { diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 486ceea94..06fa7d2d0 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -25,7 +25,7 @@ public: InputProfile, }; - explicit Config(Core::System& system_, const std::string& config_name = "qt-config", + explicit Config(const std::string& config_name = "qt-config", ConfigType config_type = ConfigType::GlobalConfig); ~Config(); @@ -194,8 +194,6 @@ private: std::unique_ptr<QSettings> qt_config; std::string qt_config_loc; bool global; - - Core::System& system; }; // These metatype declarations cannot be in common/settings.h because core is devoid of QT diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 04d397750..622808e94 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <QDesktopServices> +#include <QMessageBox> #include <QUrl> #include "common/fs/path_util.h" #include "common/logging/backend.h" @@ -26,6 +27,16 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent) connect(ui->toggle_gdbstub, &QCheckBox::toggled, [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); }); + + connect(ui->create_crash_dumps, &QCheckBox::stateChanged, [&](int) { + if (crash_dump_warning_shown) { + return; + } + QMessageBox::warning(this, tr("Restart Required"), + tr("yuzu is required to restart in order to apply this setting."), + QMessageBox::Ok, QMessageBox::Ok); + crash_dump_warning_shown = true; + }); } ConfigureDebug::~ConfigureDebug() = default; @@ -71,7 +82,14 @@ void ConfigureDebug::SetConfiguration() { ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue()); #else ui->disable_web_applet->setEnabled(false); - ui->disable_web_applet->setText(QString::fromUtf8("Web applet not compiled")); + ui->disable_web_applet->setText(tr("Web applet not compiled")); +#endif + +#ifdef YUZU_DBGHELP + ui->create_crash_dumps->setChecked(Settings::values.create_crash_dumps.GetValue()); +#else + ui->create_crash_dumps->setEnabled(false); + ui->create_crash_dumps->setText(tr("MiniDump creation not compiled")); #endif } @@ -84,6 +102,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked(); Settings::values.reporting_services = ui->reporting_services->isChecked(); Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked(); + Settings::values.create_crash_dumps = ui->create_crash_dumps->isChecked(); Settings::values.quest_flag = ui->quest_flag->isChecked(); Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); diff --git a/src/yuzu/configuration/configure_debug.h b/src/yuzu/configuration/configure_debug.h index 42d30f170..030a0b7f7 100644 --- a/src/yuzu/configuration/configure_debug.h +++ b/src/yuzu/configuration/configure_debug.h @@ -32,4 +32,6 @@ private: std::unique_ptr<Ui::ConfigureDebug> ui; const Core::System& system; + + bool crash_dump_warning_shown{false}; }; diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 47b8b80f1..314d47af5 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -7,60 +7,60 @@ </property> <widget class="QWidget"> <layout class="QVBoxLayout" name="verticalLayout_1"> - <item> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QGroupBox" name="groupBox"> - <property name="title"> - <string>Debugger</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Debugger</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_11"> + <item> + <widget class="QCheckBox" name="toggle_gdbstub"> + <property name="text"> + <string>Enable GDB Stub</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_11"> + <property name="text"> + <string>Port:</string> + </property> + </widget> + </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_11"> - <item> - <widget class="QCheckBox" name="toggle_gdbstub"> - <property name="text"> - <string>Enable GDB Stub</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_11"> - <property name="text"> - <string>Port:</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="gdbport_spinbox"> - <property name="minimum"> - <number>1024</number> - </property> - <property name="maximum"> - <number>65535</number> - </property> - </widget> - </item> - </layout> + <widget class="QSpinBox" name="gdbport_spinbox"> + <property name="minimum"> + <number>1024</number> + </property> + <property name="maximum"> + <number>65535</number> + </property> + </widget> </item> </layout> - </widget> - </item> - </layout> + </item> + </layout> + </widget> </item> + </layout> + </item> <item> <widget class="QGroupBox" name="groupBox_2"> <property name="title"> @@ -231,6 +231,13 @@ <string>Debugging</string> </property> <layout class="QGridLayout" name="gridLayout_3"> + <item row="2" column="0"> + <widget class="QCheckBox" name="reporting_services"> + <property name="text"> + <string>Enable Verbose Reporting Services**</string> + </property> + </widget> + </item> <item row="0" column="0"> <widget class="QCheckBox" name="fs_access_log"> <property name="text"> @@ -238,20 +245,20 @@ </property> </widget> </item> - <item row="1" column="0"> + <item row="0" column="1"> <widget class="QCheckBox" name="dump_audio_commands"> - <property name="text"> - <string>Dump Audio Commands To Console**</string> - </property> <property name="toolTip"> <string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string> </property> + <property name="text"> + <string>Dump Audio Commands To Console**</string> + </property> </widget> </item> - <item row="2" column="0"> - <widget class="QCheckBox" name="reporting_services"> + <item row="2" column="1"> + <widget class="QCheckBox" name="create_crash_dumps"> <property name="text"> - <string>Enable Verbose Reporting Services**</string> + <string>Create Minidump After Crash</string> </property> </widget> </item> @@ -340,7 +347,6 @@ <tabstop>disable_loop_safety_checks</tabstop> <tabstop>fs_access_log</tabstop> <tabstop>reporting_services</tabstop> - <tabstop>dump_audio_commands</tabstop> <tabstop>quest_flag</tabstop> <tabstop>enable_cpu_debugging</tabstop> <tabstop>use_debug_asserts</tabstop> diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 16fba3deb..cb55472c9 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -65,7 +65,7 @@ void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system) ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), - profiles(std::make_unique<InputProfiles>(system_)), system{system_} { + profiles(std::make_unique<InputProfiles>()), system{system_} { ui->setupUi(this); } diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index af8343b2e..c3cb8f61d 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -42,8 +42,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); - game_config = - std::make_unique<Config>(system, config_file_name, Config::ConfigType::PerGameConfig); + game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); audio_tab = std::make_unique<ConfigureAudio>(system_, this); diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp index d668c992b..ab526e4ca 100644 --- a/src/yuzu/configuration/configure_web.cpp +++ b/src/yuzu/configuration/configure_web.cpp @@ -128,20 +128,25 @@ void ConfigureWeb::RefreshTelemetryID() { void ConfigureWeb::OnLoginChanged() { if (ui->edit_token->text().isEmpty()) { user_verified = true; - - const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16); - ui->label_token_verified->setPixmap(pixmap); + // Empty = no icon + ui->label_token_verified->setPixmap(QPixmap()); + ui->label_token_verified->setToolTip(QString()); } else { user_verified = false; - const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16); + // Show an info icon if it's been changed, clearer than showing failure + const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("info")).pixmap(16); ui->label_token_verified->setPixmap(pixmap); + ui->label_token_verified->setToolTip( + tr("Unverified, please click Verify before saving configuration", "Tooltip")); } } void ConfigureWeb::VerifyLogin() { ui->button_verify_login->setDisabled(true); ui->button_verify_login->setText(tr("Verifying...")); + ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("sync")).pixmap(16)); + ui->label_token_verified->setToolTip(tr("Verifying...")); verify_watcher.setFuture(QtConcurrent::run( [username = UsernameFromDisplayToken(ui->edit_token->text().toStdString()), token = TokenFromDisplayToken(ui->edit_token->text().toStdString())] { @@ -155,13 +160,13 @@ void ConfigureWeb::OnLoginVerified() { if (verify_watcher.result()) { user_verified = true; - const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("checked")).pixmap(16); - ui->label_token_verified->setPixmap(pixmap); + ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("checked")).pixmap(16)); + ui->label_token_verified->setToolTip(tr("Verified", "Tooltip")); ui->username->setText( QString::fromStdString(UsernameFromDisplayToken(ui->edit_token->text().toStdString()))); } else { - const QPixmap pixmap = QIcon::fromTheme(QStringLiteral("failed")).pixmap(16); - ui->label_token_verified->setPixmap(pixmap); + ui->label_token_verified->setPixmap(QIcon::fromTheme(QStringLiteral("failed")).pixmap(16)); + ui->label_token_verified->setToolTip(tr("Verification failed", "Tooltip")); ui->username->setText(tr("Unspecified")); QMessageBox::critical(this, tr("Verification failed"), tr("Verification failed. Check that you have entered your token " diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index 20b22e7de..807afbeb2 100644 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp @@ -27,7 +27,7 @@ std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) { } // namespace -InputProfiles::InputProfiles(Core::System& system_) : system{system_} { +InputProfiles::InputProfiles() { const auto input_profile_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input"; if (!FS::IsDir(input_profile_loc)) { @@ -43,8 +43,8 @@ InputProfiles::InputProfiles(Core::System& system_) : system{system_} { if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { map_profiles.insert_or_assign( - name_without_ext, std::make_unique<Config>(system, name_without_ext, - Config::ConfigType::InputProfile)); + name_without_ext, + std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile)); } return true; @@ -80,8 +80,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p } map_profiles.insert_or_assign( - profile_name, - std::make_unique<Config>(system, profile_name, Config::ConfigType::InputProfile)); + profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile)); return SaveProfile(profile_name, player_index); } diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h index 65fc9e62c..2bf3e4250 100644 --- a/src/yuzu/configuration/input_profiles.h +++ b/src/yuzu/configuration/input_profiles.h @@ -15,7 +15,7 @@ class Config; class InputProfiles { public: - explicit InputProfiles(Core::System& system_); + explicit InputProfiles(); virtual ~InputProfiles(); std::vector<std::string> GetInputProfileNames(); @@ -31,6 +31,4 @@ private: bool ProfileExistsInMap(const std::string& profile_name) const; std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles; - - Core::System& system; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a85adc072..bda9986e1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -138,6 +138,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/uisettings.h" #include "yuzu/util/clickable_label.h" +#ifdef YUZU_DBGHELP +#include "yuzu/mini_dump.h" +#endif + using namespace Common::Literals; #ifdef USE_DISCORD_PRESENCE @@ -269,10 +273,9 @@ bool GMainWindow::CheckDarkMode() { #endif // __linux__ } -GMainWindow::GMainWindow(bool has_broken_vulkan) +GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, - input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, - config{std::make_unique<Config>(*system)}, + input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, provider{std::make_unique<FileSys::ManualContentProvider>()} { #ifdef __linux__ @@ -1637,7 +1640,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); - Config per_game_config(*system, config_file_name, Config::ConfigType::PerGameConfig); + Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); + system->ApplySettings(); } // Save configurations @@ -2981,7 +2985,7 @@ void GMainWindow::OnConfigure() { Settings::values.disabled_addons.clear(); - config = std::make_unique<Config>(*system); + config = std::make_unique<Config>(); UISettings::values.reset_to_defaults = false; UISettings::values.game_dirs = std::move(old_game_dirs); @@ -3042,6 +3046,7 @@ void GMainWindow::OnConfigure() { UpdateStatusButtons(); controller_dialog->refreshConfiguration(); + system->ApplySettings(); } void GMainWindow::OnConfigureTas() { @@ -4082,7 +4087,24 @@ void GMainWindow::changeEvent(QEvent* event) { #endif int main(int argc, char* argv[]) { + std::unique_ptr<Config> config = std::make_unique<Config>(); bool has_broken_vulkan = false; + bool is_child = false; + if (CheckEnvVars(&is_child)) { + return 0; + } + +#ifdef YUZU_DBGHELP + PROCESS_INFORMATION pi; + if (!is_child && Settings::values.create_crash_dumps.GetValue() && + MiniDump::SpawnDebuggee(argv[0], pi)) { + // Delete the config object so that it doesn't save when the program exits + config.reset(nullptr); + MiniDump::DebugDebuggee(pi); + return 0; + } +#endif + if (StartupChecks(argv[0], &has_broken_vulkan)) { return 0; } @@ -4135,7 +4157,7 @@ int main(int argc, char* argv[]) { // generating shaders setlocale(LC_ALL, "C"); - GMainWindow main_window{has_broken_vulkan}; + GMainWindow main_window{std::move(config), has_broken_vulkan}; // After settings have been loaded by GMainWindow, apply the filter main_window.show(); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1ae2b93d9..716aef063 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -120,7 +120,7 @@ class GMainWindow : public QMainWindow { public: void filterBarSetChecked(bool state); void UpdateUITheme(); - explicit GMainWindow(bool has_broken_vulkan); + explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan); ~GMainWindow() override; bool DropAction(QDropEvent* event); diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp new file mode 100644 index 000000000..a34dc6a9c --- /dev/null +++ b/src/yuzu/mini_dump.cpp @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <cstdio> +#include <cstring> +#include <ctime> +#include <filesystem> +#include <fmt/format.h> +#include <windows.h> +#include "yuzu/mini_dump.h" +#include "yuzu/startup_checks.h" + +// dbghelp.h must be included after windows.h +#include <dbghelp.h> + +namespace MiniDump { + +void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, + EXCEPTION_POINTERS* pep) { + char file_name[255]; + const std::time_t the_time = std::time(nullptr); + std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time)); + + // Open the file + HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) { + fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError()); + return; + } + + // Create the minidump + const MINIDUMP_TYPE dump_type = MiniDumpNormal; + + const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle, + dump_type, (pep != 0) ? info : 0, 0, 0); + + if (write_dump_status) { + fmt::print(stderr, "MiniDump created: {}", file_name); + } else { + fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError()); + } + + // Close the file + CloseHandle(file_handle); +} + +void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) { + EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; + + HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId); + if (thread_handle == nullptr) { + fmt::print(stderr, "OpenThread failed ({})", GetLastError()); + return; + } + + // Get child process context + CONTEXT context = {}; + context.ContextFlags = CONTEXT_ALL; + if (!GetThreadContext(thread_handle, &context)) { + fmt::print(stderr, "GetThreadContext failed ({})", GetLastError()); + return; + } + + // Create exception pointers for minidump + EXCEPTION_POINTERS ep; + ep.ExceptionRecord = &record; + ep.ContextRecord = &context; + + MINIDUMP_EXCEPTION_INFORMATION info; + info.ThreadId = deb_ev.dwThreadId; + info.ExceptionPointers = &ep; + info.ClientPointers = false; + + CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep); + + if (CloseHandle(thread_handle) == 0) { + fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError()); + } +} + +bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) { + std::memset(&pi, 0, sizeof(pi)); + + // Don't debug if we are already being debugged + if (IsDebuggerPresent()) { + return false; + } + + if (!SpawnChild(arg0, &pi, 0)) { + fmt::print(stderr, "warning: continuing without crash dumps"); + return false; + } + + const bool can_debug = DebugActiveProcess(pi.dwProcessId); + if (!can_debug) { + fmt::print(stderr, + "warning: DebugActiveProcess failed ({}), continuing without crash dumps", + GetLastError()); + return false; + } + + return true; +} + +static const char* ExceptionName(DWORD exception) { + switch (exception) { + case EXCEPTION_ACCESS_VIOLATION: + return "EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_DATATYPE_MISALIGNMENT: + return "EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_BREAKPOINT: + return "EXCEPTION_BREAKPOINT"; + case EXCEPTION_SINGLE_STEP: + return "EXCEPTION_SINGLE_STEP"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_FLT_DENORMAL_OPERAND: + return "EXCEPTION_FLT_DENORMAL_OPERAND"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INEXACT_RESULT: + return "EXCEPTION_FLT_INEXACT_RESULT"; + case EXCEPTION_FLT_INVALID_OPERATION: + return "EXCEPTION_FLT_INVALID_OPERATION"; + case EXCEPTION_FLT_OVERFLOW: + return "EXCEPTION_FLT_OVERFLOW"; + case EXCEPTION_FLT_STACK_CHECK: + return "EXCEPTION_FLT_STACK_CHECK"; + case EXCEPTION_FLT_UNDERFLOW: + return "EXCEPTION_FLT_UNDERFLOW"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return "EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_INT_OVERFLOW: + return "EXCEPTION_INT_OVERFLOW"; + case EXCEPTION_PRIV_INSTRUCTION: + return "EXCEPTION_PRIV_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: + return "EXCEPTION_IN_PAGE_ERROR"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; + case EXCEPTION_STACK_OVERFLOW: + return "EXCEPTION_STACK_OVERFLOW"; + case EXCEPTION_INVALID_DISPOSITION: + return "EXCEPTION_INVALID_DISPOSITION"; + case EXCEPTION_GUARD_PAGE: + return "EXCEPTION_GUARD_PAGE"; + case EXCEPTION_INVALID_HANDLE: + return "EXCEPTION_INVALID_HANDLE"; + default: + return "unknown exception type"; + } +} + +void DebugDebuggee(PROCESS_INFORMATION& pi) { + DEBUG_EVENT deb_ev = {}; + + while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) { + const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE); + if (!wait_success) { + fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError()); + return; + } + + switch (deb_ev.dwDebugEventCode) { + case OUTPUT_DEBUG_STRING_EVENT: + case CREATE_PROCESS_DEBUG_EVENT: + case CREATE_THREAD_DEBUG_EVENT: + case EXIT_PROCESS_DEBUG_EVENT: + case EXIT_THREAD_DEBUG_EVENT: + case LOAD_DLL_DEBUG_EVENT: + case RIP_EVENT: + case UNLOAD_DLL_DEBUG_EVENT: + // Continue on all other debug events + ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE); + break; + case EXCEPTION_DEBUG_EVENT: + EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord; + + // We want to generate a crash dump if we are seeing the same exception again. + if (!deb_ev.u.Exception.dwFirstChance) { + fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n", + record.ExceptionCode, ExceptionName(record.ExceptionCode)); + DumpFromDebugEvent(deb_ev, pi); + } + + // Continue without handling the exception. + // Lets the debuggee use its own exception handler. + // - If one does not exist, we will see the exception once more where we make a minidump + // for. Then when it reaches here again, yuzu will probably crash. + // - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an + // infinite loop of exceptions. + ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); + break; + } + } +} + +} // namespace MiniDump diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h new file mode 100644 index 000000000..d6b6cca84 --- /dev/null +++ b/src/yuzu/mini_dump.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <windows.h> + +#include <dbghelp.h> + +namespace MiniDump { + +void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info, + EXCEPTION_POINTERS* pep); + +void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi); +bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi); +void DebugDebuggee(PROCESS_INFORMATION& pi); + +} // namespace MiniDump diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp index 8421280bf..29b87da05 100644 --- a/src/yuzu/startup_checks.cpp +++ b/src/yuzu/startup_checks.cpp @@ -31,19 +31,36 @@ void CheckVulkan() { } } -bool StartupChecks(const char* arg0, bool* has_broken_vulkan) { +bool CheckEnvVars(bool* is_child) { #ifdef _WIN32 // Check environment variable to see if we are the child char variable_contents[8]; const DWORD startup_check_var = GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8); - if (startup_check_var > 0 && std::strncmp(variable_contents, "ON", 8) == 0) { + if (startup_check_var > 0 && std::strncmp(variable_contents, ENV_VAR_ENABLED_TEXT, 8) == 0) { CheckVulkan(); return true; } + // Don't perform startup checks if we are a child process + char is_child_s[8]; + const DWORD is_child_len = GetEnvironmentVariableA(IS_CHILD_ENV_VAR, is_child_s, 8); + if (is_child_len > 0 && std::strncmp(is_child_s, ENV_VAR_ENABLED_TEXT, 8) == 0) { + *is_child = true; + return false; + } else if (!SetEnvironmentVariableA(IS_CHILD_ENV_VAR, ENV_VAR_ENABLED_TEXT)) { + std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", + IS_CHILD_ENV_VAR, GetLastError()); + return true; + } +#endif + return false; +} + +bool StartupChecks(const char* arg0, bool* has_broken_vulkan) { +#ifdef _WIN32 // Set the startup variable for child processes - const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON"); + const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, ENV_VAR_ENABLED_TEXT); if (!env_var_set) { std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n", STARTUP_CHECK_ENV_VAR, GetLastError()); @@ -53,7 +70,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) { PROCESS_INFORMATION process_info; std::memset(&process_info, '\0', sizeof(process_info)); - if (!SpawnChild(arg0, &process_info)) { + if (!SpawnChild(arg0, &process_info, 0)) { return false; } @@ -106,7 +123,7 @@ bool StartupChecks(const char* arg0, bool* has_broken_vulkan) { } #ifdef _WIN32 -bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) { +bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags) { STARTUPINFOA startup_info; std::memset(&startup_info, '\0', sizeof(startup_info)); @@ -120,7 +137,7 @@ bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) { nullptr, // lpProcessAttributes nullptr, // lpThreadAttributes false, // bInheritHandles - 0, // dwCreationFlags + flags, // dwCreationFlags nullptr, // lpEnvironment nullptr, // lpCurrentDirectory &startup_info, // lpStartupInfo diff --git a/src/yuzu/startup_checks.h b/src/yuzu/startup_checks.h index 096dd54a8..f2fc2d9d4 100644 --- a/src/yuzu/startup_checks.h +++ b/src/yuzu/startup_checks.h @@ -7,11 +7,14 @@ #include <windows.h> #endif +constexpr char IS_CHILD_ENV_VAR[] = "YUZU_IS_CHILD"; constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS"; +constexpr char ENV_VAR_ENABLED_TEXT[] = "ON"; void CheckVulkan(); +bool CheckEnvVars(bool* is_child); bool StartupChecks(const char* arg0, bool* has_broken_vulkan); #ifdef _WIN32 -bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi); +bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi, int flags); #endif diff --git a/vcpkg.json b/vcpkg.json index c4413e22a..3c92510d6 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -31,6 +31,10 @@ "yuzu-tests": { "description": "Compile tests", "dependencies": [ "catch2" ] + }, + "dbghelp": { + "description": "Compile Windows crash dump (Minidump) support", + "dependencies": [ "dbghelp" ] } }, "overrides": [ |