From 0ff7d6ef0c0a4e13217f7b35abe226785735c604 Mon Sep 17 00:00:00 2001 From: Squall Leonhart Date: Sat, 14 Mar 2026 07:38:33 +1100 Subject: [PATCH 01/24] input: Fix logging crash in WPADGetInfoAsync due to mismatching type (#1832) --- src/Cafe/OS/libs/padscore/padscore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cafe/OS/libs/padscore/padscore.cpp b/src/Cafe/OS/libs/padscore/padscore.cpp index 646ea17f..cad9193a 100644 --- a/src/Cafe/OS/libs/padscore/padscore.cpp +++ b/src/Cafe/OS/libs/padscore/padscore.cpp @@ -134,7 +134,7 @@ void padscoreExport_WPADGetInfoAsync(PPCInterpreter_t* hCPU) ppcDefineParamU32(channel, 0); ppcDefineParamStructPtr(wpadInfo, WPADInfo_t, 1); ppcDefineParamMPTR(callbackFunc, 2); - cemuLog_log(LogType::InputAPI, "WPADGetInfoAsync({}, 0x{:08x}, 0x{:08x})", channel, fmt::ptr(wpadInfo), callbackFunc); + cemuLog_log(LogType::InputAPI, "WPADGetInfoAsync({}, 0x{:08x}, 0x{:08x})", channel, memory_getVirtualOffsetFromPointer(wpadInfo), callbackFunc); if (channel < InputManager::kMaxWPADControllers) { From 6312fb936cbec3ed62f85dba7206313638aafe4c Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sat, 14 Mar 2026 16:24:20 +0000 Subject: [PATCH 02/24] ci: Update github actions (#1836) --- .github/workflows/build.yml | 20 +++++++++---------- .github/workflows/deploy_release.yml | 12 +++++------ .../workflows/determine_release_version.yml | 2 +- .github/workflows/generate_pot.yml | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af560d21..31b47e55 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: "Checkout repo" - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: "recursive" fetch-depth: 0 @@ -83,7 +83,7 @@ jobs: run: mv bin/Cemu_release bin/Cemu - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-bin-linux-${{ matrix.arch }} path: ./bin/Cemu @@ -103,9 +103,9 @@ jobs: needs: build-ubuntu steps: - name: Checkout Upstream Repo - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-bin-linux-${{ matrix.arch }} path: bin @@ -122,7 +122,7 @@ jobs: dist/linux/appimage.sh ${{ runner.arch }} - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-appimage-${{ matrix.arch }} path: artifacts @@ -131,7 +131,7 @@ jobs: runs-on: windows-2022 steps: - name: "Checkout repo" - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: "recursive" @@ -193,13 +193,13 @@ jobs: makensis /DPRODUCT_VERSION=${{ inputs.next_version_major }}.${{ inputs.next_version_minor }} installer.nsi - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-bin-windows-x64 path: ./bin/Cemu.exe - name: Upload NSIS Installer - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-installer-windows-x64 path: ./src/resource/cemu-${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}-windows-x64-installer.exe @@ -211,7 +211,7 @@ jobs: arch: [x86_64, arm64] steps: - name: "Checkout repo" - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: "recursive" @@ -289,7 +289,7 @@ jobs: rm bin/tmp.dmg - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cemu-bin-macos-${{ matrix.arch }} path: ./bin/Cemu.dmg diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml index dfcce040..caa4a57a 100644 --- a/.github/workflows/deploy_release.yml +++ b/.github/workflows/deploy_release.yml @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-22.04 needs: [call-release-build, calculate-version] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -78,27 +78,27 @@ jobs: if [ -n "${{ github.event.inputs.changelog9 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog9 }}\n"; fi echo -e "$CHANGELOG" echo "RELEASE_BODY=$CHANGELOG" >> $GITHUB_ENV - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-bin-linux-x64 path: cemu-bin-linux-x64 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-appimage-x64 path: cemu-appimage-x64 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-bin-windows-x64 path: cemu-bin-windows-x64 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: cemu-installer-windows-x64 path: cemu-installer-windows-x64 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: pattern: cemu-bin-macos* path: cemu-macos diff --git a/.github/workflows/determine_release_version.yml b/.github/workflows/determine_release_version.yml index be606941..a2efcd17 100644 --- a/.github/workflows/determine_release_version.yml +++ b/.github/workflows/determine_release_version.yml @@ -23,7 +23,7 @@ jobs: next_version_minor: ${{ steps.calculate_next_version.outputs.next_version_minor }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v6 - name: Get all releases id: get_all_releases diff --git a/.github/workflows/generate_pot.yml b/.github/workflows/generate_pot.yml index bd42de46..f8f1864c 100644 --- a/.github/workflows/generate_pot.yml +++ b/.github/workflows/generate_pot.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout repo" - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: "Install gettext" run: | @@ -36,7 +36,7 @@ jobs: -o cemu.pot - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: POT file path: ./cemu.pot From c1965c3d89a814c1659064a4e9ac69d5cfa9ccc8 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 4 Mar 2026 07:07:47 +0100 Subject: [PATCH 03/24] Enforce LF line endings --- .clang-format | 1 + .gitattributes | 1 + 2 files changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.clang-format b/.clang-format index 0cef9ae4..e569a158 100644 --- a/.clang-format +++ b/.clang-format @@ -64,3 +64,4 @@ SpacesInSquareBrackets: false Standard: Latest TabWidth: 4 UseTab: Always +LineEnding: LF diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..0b9c73ac --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +text=auto eol=lf \ No newline at end of file From 1a5546e4adf003187f5920d09707304d4578665d Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 4 Mar 2026 07:32:11 +0100 Subject: [PATCH 04/24] GfxPack: Add version (8) for new features --- src/Cafe/GraphicPack/GraphicPack2.h | 1 + src/gui/wxgui/GraphicPacksWindow2.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Cafe/GraphicPack/GraphicPack2.h b/src/Cafe/GraphicPack/GraphicPack2.h index e33f0946..54af7a5f 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.h +++ b/src/Cafe/GraphicPack/GraphicPack2.h @@ -26,6 +26,7 @@ public: GFXPACK_VERSION_5 = 5, GFXPACK_VERSION_6 = 6, // added memory extensions GFXPACK_VERSION_7 = 7, // added fine-grained origin control in patch format (no more forced 4 byte alignment), .string directive (an alias to .byte) and support for more than one constant per data directive + GFXPACK_VERSION_8 = 8, // (Cemu 2.7) added: titleId and rpx hash wildcards (*), added .callback entry to call a function when the main entrypoint is reached }; struct TextureRule diff --git a/src/gui/wxgui/GraphicPacksWindow2.cpp b/src/gui/wxgui/GraphicPacksWindow2.cpp index 90f5e17c..236a742f 100644 --- a/src/gui/wxgui/GraphicPacksWindow2.cpp +++ b/src/gui/wxgui/GraphicPacksWindow2.cpp @@ -131,7 +131,7 @@ void GraphicPacksWindow2::FillGraphicPackList() const auto tmp_text = m_graphic_pack_tree->GetItemText(node); m_graphic_pack_tree->SetItemText(node, tmp_text + " (may not be compatible with Vulkan)"); } - else if (p->GetVersion() != 3 && p->GetVersion() != 4 && p->GetVersion() != 5 && p->GetVersion() != 6 && p->GetVersion() != GraphicPack2::GFXPACK_VERSION_7) + else if (p->GetVersion() != 3 && p->GetVersion() != 4 && p->GetVersion() != 5 && p->GetVersion() != 6 && p->GetVersion() != GraphicPack2::GFXPACK_VERSION_7 && p->GetVersion() != GraphicPack2::GFXPACK_VERSION_8) { auto tmp_text = m_graphic_pack_tree->GetItemText(node); m_graphic_pack_tree->SetItemText(node, tmp_text + " (Unsupported version)"); From 8e2e2c44bbd2b6b2a47b4c344a2114534c5b669c Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 5 Mar 2026 17:43:55 +0100 Subject: [PATCH 05/24] build: Generate precompiled header only once (msvc only) --- src/CMakeLists.txt | 2 ++ src/Cafe/CMakeLists.txt | 2 ++ src/Cemu/CMakeLists.txt | 2 ++ src/Common/CMakeLists.txt | 14 ++++++++------ src/audio/CMakeLists.txt | 2 ++ src/config/CMakeLists.txt | 2 ++ src/gui/wxgui/CMakeLists.txt | 2 ++ src/imgui/CMakeLists.txt | 2 ++ src/input/CMakeLists.txt | 2 ++ src/resource/CMakeLists.txt | 2 ++ src/util/CMakeLists.txt | 2 ++ 11 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a4f0d19e..81d432ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,6 +55,8 @@ add_executable(CemuBin mainLLE.cpp ) +target_precompile_headers(CemuBin REUSE_FROM CemuCommon) + if(MSVC AND MSVC_VERSION EQUAL 1940) # workaround for an msvc issue on VS 17.10 where generated ILK files are too large # see https://developercommunity.visualstudio.com/t/After-updating-to-VS-1710-the-size-of-/10665511 diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 1835a860..653dea6e 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -614,6 +614,8 @@ endif() set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(CemuCafe REUSE_FROM CemuCommon) + target_include_directories(CemuCafe PUBLIC "../") if (glslang_VERSION VERSION_LESS "15.0.0") diff --git a/src/Cemu/CMakeLists.txt b/src/Cemu/CMakeLists.txt index 83ab0af6..eb658d29 100644 --- a/src/Cemu/CMakeLists.txt +++ b/src/Cemu/CMakeLists.txt @@ -40,6 +40,8 @@ endif() set_property(TARGET CemuComponents PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(CemuComponents REUSE_FROM CemuCommon) + target_include_directories(CemuComponents PUBLIC "../") target_link_libraries(CemuComponents PRIVATE diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 971dfd80..8b0a456c 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -53,14 +53,11 @@ if(LINUX) ) endif() -# All the targets wanting to use the precompiled.h header -# have to link to CemuCommon -target_precompile_headers(CemuCommon PUBLIC precompiled.h) +# the precompiled header is produced by CemuCommon and reused by all the other targets +target_precompile_headers(CemuCommon PRIVATE precompiled.h) target_include_directories(CemuCommon PUBLIC "../") target_link_libraries(CemuCommon PRIVATE - Boost::nowide - Boost::filesystem glm::glm ) @@ -69,6 +66,11 @@ if (UNIX AND NOT APPLE) endif() # PUBLIC because: +# - boost/nowide/convert.hpp is included in precompiled.h # - boost/predef/os.h is included in platform.h # - fmt/core.h is included in precompiled.h -target_link_libraries(CemuCommon PUBLIC Boost::headers fmt::fmt) +target_link_libraries(CemuCommon PUBLIC + Boost::nowide + Boost::headers + fmt::fmt +) diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 8d01499e..40c4ee22 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -7,6 +7,8 @@ add_library(CemuAudio set_property(TARGET CemuAudio PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(CemuAudio REUSE_FROM CemuCommon) + if(WIN32) target_sources(CemuAudio PRIVATE DirectSoundAPI.cpp diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index 183f124a..1c1f8cbc 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -13,6 +13,8 @@ add_library(CemuConfig set_property(TARGET CemuConfig PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(CemuConfig REUSE_FROM CemuCommon) + target_include_directories(CemuConfig PUBLIC "../") target_link_libraries(CemuConfig PRIVATE diff --git a/src/gui/wxgui/CMakeLists.txt b/src/gui/wxgui/CMakeLists.txt index 7db02c20..f625f614 100644 --- a/src/gui/wxgui/CMakeLists.txt +++ b/src/gui/wxgui/CMakeLists.txt @@ -132,6 +132,8 @@ endif() set_property(TARGET CemuWxGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(CemuWxGui REUSE_FROM CemuCommon) + target_include_directories(CemuWxGui PUBLIC "../") # PUBLIC because rapidjson/document.h is included in ChecksumTool.h diff --git a/src/imgui/CMakeLists.txt b/src/imgui/CMakeLists.txt index 57526c18..a77b807b 100644 --- a/src/imgui/CMakeLists.txt +++ b/src/imgui/CMakeLists.txt @@ -18,6 +18,8 @@ endif () set_property(TARGET imguiImpl PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(imguiImpl REUSE_FROM CemuCommon) + target_include_directories(imguiImpl PUBLIC "../") # imgui source files diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index cdf9cbba..7953c26b 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -43,6 +43,8 @@ add_library(CemuInput set_property(TARGET CemuInput PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(CemuInput REUSE_FROM CemuCommon) + if(WIN32) # XInput target_sources(CemuInput PRIVATE diff --git a/src/resource/CMakeLists.txt b/src/resource/CMakeLists.txt index c8833d01..f946a528 100644 --- a/src/resource/CMakeLists.txt +++ b/src/resource/CMakeLists.txt @@ -2,6 +2,8 @@ add_library(CemuResource) set_property(TARGET CemuResource PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(CemuResource REUSE_FROM CemuCommon) + enable_language(ASM) # icon resources diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index c0c45eba..e8e45ff9 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -89,6 +89,8 @@ endif() set_property(TARGET CemuUtil PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +target_precompile_headers(CemuUtil REUSE_FROM CemuCommon) + target_include_directories(CemuUtil PUBLIC "../") target_link_libraries(CemuUtil PRIVATE From a5ae41fdbe516e9eb1fd27ce3c58ebdb0cbb96b4 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 6 Mar 2026 07:49:26 +0100 Subject: [PATCH 06/24] build: Remove unnecessary includes to improve compile times Also revert PCH reuse for everything but MSVC. Other compilers/platforms have limitations that make reused PCH a bit too fragile. I got it to work but only after forcing certain flags globally (like -pthread) and I dont think its a good idea to do that --- CMakeLists.txt | 3 +- src/CMakeLists.txt | 11 ++++- src/Cafe/Account/Account.cpp | 2 +- src/Cafe/Account/Account.h | 4 -- src/Cafe/CMakeLists.txt | 3 +- src/Cafe/CafeSystem.h | 3 +- src/Cafe/GameProfile/GameProfile.h | 2 - src/Cafe/GraphicPack/GraphicPack2.cpp | 41 ++++++++++++++-- src/Cafe/GraphicPack/GraphicPack2.h | 48 ++----------------- src/Cafe/HW/Espresso/Debugger/GDBStub.cpp | 6 ++- src/Cafe/HW/Latte/Core/LatteOverlay.cpp | 7 ++- src/Cafe/HW/Latte/Core/LatteTextureView.cpp | 3 +- src/Cafe/IOSU/legacy/iosu_fpd.cpp | 2 +- src/Cafe/IOSU/legacy/iosu_nim.cpp | 2 + src/Cafe/IOSU/nn/boss/boss_service.cpp | 3 +- src/Cafe/OS/libs/nn_ac/nn_ac.cpp | 1 + src/Cafe/OS/libs/nn_idbe/nn_idbe.cpp | 1 + src/Cafe/OS/libs/nsysnet/nsysnet.cpp | 19 +------- src/Cafe/TitleList/GameInfo.cpp | 7 +++ src/Cafe/TitleList/GameInfo.h | 8 +--- src/Cemu/CMakeLists.txt | 3 +- src/Cemu/DiscordPresence/DiscordRPCLite.cpp | 5 +- src/Cemu/napi/napi.cpp | 12 +++++ src/Cemu/napi/napi.h | 12 ++--- src/Cemu/napi/napi_act.cpp | 6 +-- src/Cemu/ncrypto/ncrypto.cpp | 1 + src/Cemu/ncrypto/ncrypto.h | 4 +- src/Common/CMakeLists.txt | 3 +- .../ExceptionHandler/ELFSymbolTable.cpp | 4 +- src/Common/precompiled.h | 2 +- src/Common/socket.h | 22 ++++++++- src/audio/CMakeLists.txt | 2 +- src/config/ActiveSettings.cpp | 3 +- src/config/ActiveSettings.h | 2 - src/config/CMakeLists.txt | 2 +- src/config/CemuConfig.cpp | 5 +- src/config/CemuConfig.h | 4 +- src/gui/interface/WindowSystem.h | 1 - src/gui/wxgui/CMakeLists.txt | 2 +- src/gui/wxgui/GameUpdateWindow.cpp | 6 +-- src/gui/wxgui/wxCemuConfig.h | 1 - src/imgui/CMakeLists.txt | 2 +- src/input/CMakeLists.txt | 2 +- src/input/api/Wiimote/l2cap/L2CapWiimote.cpp | 4 +- src/resource/CMakeLists.txt | 2 +- src/util/CMakeLists.txt | 2 +- 46 files changed, 150 insertions(+), 140 deletions(-) create mode 100644 src/Cafe/TitleList/GameInfo.cpp create mode 100644 src/Cemu/napi/napi.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f71595f6..a00879fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ set(EMULATOR_VERSION_MAJOR "0" CACHE STRING "") set(EMULATOR_VERSION_MINOR "0" CACHE STRING "") set(EMULATOR_VERSION_PATCH "0" CACHE STRING "") +set(THREADS_PREFER_PTHREAD_FLAG true) # needs to be set before vcpkg + execute_process( COMMAND git log --format=%h -1 WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} @@ -136,7 +138,6 @@ option(ENABLE_CUBEB "Enabled cubeb backend" ON) option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON) -set(THREADS_PREFER_PTHREAD_FLAG true) find_package(Threads REQUIRED) find_package(SDL2 REQUIRED) find_package(CURL REQUIRED) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81d432ec..e73d5f1f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,6 +39,15 @@ add_compile_definitions(VK_NO_PROTOTYPES) set(CMAKE_INCLUDE_CURRENT_DIR ON) +# on msvc we can reuse the PCH from CemuCommon, on other platforms it's not straight forward to reuse the PCH so we opt out of it +function(cemu_use_precompiled_header target_name) + if(MSVC) + target_precompile_headers(${target_name} REUSE_FROM CemuCommon) + else() + target_precompile_headers(${target_name} PRIVATE "${CMAKE_SOURCE_DIR}/src/Common/precompiled.h") + endif() +endfunction() + add_subdirectory(Common) add_subdirectory(gui) add_subdirectory(Cafe) @@ -55,7 +64,7 @@ add_executable(CemuBin mainLLE.cpp ) -target_precompile_headers(CemuBin REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuBin) if(MSVC AND MSVC_VERSION EQUAL 1940) # workaround for an msvc issue on VS 17.10 where generated ILK files are too large diff --git a/src/Cafe/Account/Account.cpp b/src/Cafe/Account/Account.cpp index 729786b1..6f576ae1 100644 --- a/src/Cafe/Account/Account.cpp +++ b/src/Cafe/Account/Account.cpp @@ -1,6 +1,6 @@ #include "Account.h" +#include "AccountError.h" #include "util/helpers/helpers.h" -#include "util/helpers/SystemException.h" #include "util/helpers/StringHelpers.h" #include "config/ActiveSettings.h" #include "Cafe/IOSU/legacy/iosu_crypto.h" diff --git a/src/Cafe/Account/Account.h b/src/Cafe/Account/Account.h index ed32de5a..c9abd395 100644 --- a/src/Cafe/Account/Account.h +++ b/src/Cafe/Account/Account.h @@ -1,12 +1,8 @@ #pragma once -#include "AccountError.h" - #include #include #include -#include -#include enum class OnlineAccountError { diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 653dea6e..3ca34e35 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -525,6 +525,7 @@ add_library(CemuCafe OS/RPL/rpl_structs.h OS/RPL/rpl_symbol_storage.cpp OS/RPL/rpl_symbol_storage.h + TitleList/GameInfo.cpp TitleList/GameInfo.h TitleList/ParsedMetaXml.h TitleList/SaveInfo.cpp @@ -614,7 +615,7 @@ endif() set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(CemuCafe REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuCafe) target_include_directories(CemuCafe PUBLIC "../") diff --git a/src/Cafe/CafeSystem.h b/src/Cafe/CafeSystem.h index e9de8d7d..4003b393 100644 --- a/src/Cafe/CafeSystem.h +++ b/src/Cafe/CafeSystem.h @@ -1,11 +1,10 @@ #pragma once #include "Cafe/OS/RPL/rpl.h" -#include "util/helpers/Semaphore.h" #include "Cafe/TitleList/TitleId.h" -#include "config/CemuConfig.h" enum class CosCapabilityBits : uint64; enum class CosCapabilityGroup : uint32; +enum class CafeConsoleRegion; namespace CafeSystem { diff --git a/src/Cafe/GameProfile/GameProfile.h b/src/Cafe/GameProfile/GameProfile.h index 42424957..5155beac 100644 --- a/src/Cafe/GameProfile/GameProfile.h +++ b/src/Cafe/GameProfile/GameProfile.h @@ -1,6 +1,4 @@ #pragma once - -#include #include "config/CemuConfig.h" struct gameProfileIntegerOption_t diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp index 295d5dce..e79f9fc5 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2.cpp @@ -1,18 +1,18 @@ #include "Cafe/GraphicPack/GraphicPack2.h" +#include "Cafe/Filesystem/fsc.h" #include "config/CemuConfig.h" #include "config/ActiveSettings.h" #include "openssl/sha.h" -#include "Cafe/HW/Latte/Renderer/RendererOuputShader.h" -#include "Cafe/Filesystem/fsc.h" #include "boost/algorithm/string.hpp" #include "util/helpers/MapAdaptor.h" #include "util/helpers/StringParser.h" -#include "Cafe/HW/Latte/Core/LatteTiming.h" -#include "util/IniParser/IniParser.h" #include "util/helpers/StringHelpers.h" +#include "util/IniParser/IniParser.h" #include "Cafe/CafeSystem.h" #include "HW/Espresso/Debugger/Debugger.h" - +#include "Cafe/HW/Latte/Core/LatteTiming.h" +#include "Cafe/HW/Latte/Renderer/Renderer.h" +#include "Cafe/HW/Latte/Renderer/RendererOuputShader.h" #include std::vector GraphicPack2::s_graphic_packs; @@ -21,6 +21,37 @@ std::atomic_bool GraphicPack2::s_isReady; #define GP_LEGACY_VERSION (2) +template +bool ParseRule(const ExpressionParser& parser, IniParser& iniParser, const char* option_name, T* valueOut) +{ + auto optionValue = iniParser.FindOption(option_name); + if (optionValue) + { + *valueOut = parser.Evaluate(*optionValue); + return true; + } + return false; +} + +template +std::vector ParseList(const ExpressionParser& parser, IniParser& iniParser, const char* optionName) +{ + std::vector result; + auto optionText = iniParser.FindOption(optionName); + if (!optionText) + return result; + for (auto& token : Tokenize(*optionText, ',')) + { + try + { + result.emplace_back(parser.Evaluate(token)); + } + catch (const std::invalid_argument&) + {} + } + return result; +} + void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath) { fs::path rulesPath = graphicPackPath; diff --git a/src/Cafe/GraphicPack/GraphicPack2.h b/src/Cafe/GraphicPack/GraphicPack2.h index 54af7a5f..88d02017 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.h +++ b/src/Cafe/GraphicPack/GraphicPack2.h @@ -6,10 +6,10 @@ #include "util/helpers/Serializer.h" #include "Cafe/OS/RPL/rpl.h" #include "Cemu/PPCAssembler/ppcAssembler.h" -#include -#include "Cafe/HW/Latte/Renderer/Renderer.h" #include "GraphicPack2Patches.h" -#include "util/IniParser/IniParser.h" + +enum class RendererAPI; +enum class GfxVendor; class GraphicPack2 { @@ -99,7 +99,7 @@ public: }; using PresetPtr = std::shared_ptr; - GraphicPack2(fs::path rulesPath, IniParser& rules); + GraphicPack2(fs::path rulesPath, class IniParser& rules); bool IsEnabled() const { return m_enabled; } bool IsActivated() const { return m_activated; } @@ -253,12 +253,6 @@ private: std::string m_output_shader_source, m_upscaling_shader_source, m_downscaling_shader_source; std::unique_ptr m_output_shader, m_upscaling_shader, m_downscaling_shader, m_output_shader_ud, m_upscaling_shader_ud, m_downscaling_shader_ud; - template - bool ParseRule(const ExpressionParser& parser, IniParser& iniParser, const char* option_name, T* value_out) const; - - template - std::vector ParseList(const ExpressionParser& parser, IniParser& iniParser, const char* option_name) const; - std::unordered_map ParsePresetVars(IniParser& rules) const; std::vector ParseTitleIds(IniParser& rules, const char* option_name); @@ -307,37 +301,3 @@ public: }; using GraphicPackPtr = std::shared_ptr; - -template -bool GraphicPack2::ParseRule(const ExpressionParser& parser, IniParser& iniParser, const char* option_name, T* value_out) const -{ - auto option_value = iniParser.FindOption(option_name); - if (option_value) - { - *value_out = parser.Evaluate(*option_value); - return true; - } - - return false; -} - -template -std::vector GraphicPack2::ParseList(const ExpressionParser& parser, IniParser& iniParser, const char* option_name) const -{ - std::vector result; - - auto option_text = iniParser.FindOption(option_name); - if (!option_text) - return result; - - for(auto& token : Tokenize(*option_text, ',')) - { - try - { - result.emplace_back(parser.Evaluate(token)); - } - catch (const std::invalid_argument&) {} - } - - return result; -} diff --git a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp index e54fae1b..e7cb2257 100644 --- a/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp +++ b/src/Cafe/HW/Espresso/Debugger/GDBStub.cpp @@ -12,6 +12,10 @@ #include "Cafe/HW/Espresso/EspressoISA.h" #include "Common/socket.h" +#if BOOST_OS_UNIX +#include +#endif + #define GET_THREAD_ID(threadPtr) memory_getVirtualOffsetFromPointer(threadPtr) #define GET_THREAD_BY_ID(threadId) (OSThread_t*)memory_getPointerFromPhysicalOffset(threadId) @@ -985,4 +989,4 @@ void GDBServer::HandleEntryStop(uint32 entryAddress) { this->m_patchedInstructions.try_emplace(entryAddress, entryAddress, BreakpointType::BP_SINGLE, false, ""); m_entry_point = entryAddress; -} \ No newline at end of file +} diff --git a/src/Cafe/HW/Latte/Core/LatteOverlay.cpp b/src/Cafe/HW/Latte/Core/LatteOverlay.cpp index 7499d743..1a1a560c 100644 --- a/src/Cafe/HW/Latte/Core/LatteOverlay.cpp +++ b/src/Cafe/HW/Latte/Core/LatteOverlay.cpp @@ -1,11 +1,10 @@ #include "Cafe/HW/Latte/Core/LatteOverlay.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" -#include "WindowSystem.h" - -#include "config/CemuConfig.h" - #include "Cafe/HW/Latte/Renderer/Renderer.h" +#include "Cafe/Account/Account.h" +#include "config/CemuConfig.h" #include "config/ActiveSettings.h" +#include "WindowSystem.h" #include #include "resource/IconsFontAwesome5.h" diff --git a/src/Cafe/HW/Latte/Core/LatteTextureView.cpp b/src/Cafe/HW/Latte/Core/LatteTextureView.cpp index 2773a34d..a1a94e4d 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureView.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureView.cpp @@ -1,6 +1,7 @@ +#include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/HW/Latte/Core/LatteTexture.h" #include "Cafe/HW/Latte/Core/LatteTextureView.h" -#include "Cafe/HW/Latte/Core/Latte.h" +#include "LatteCachedFBO.h" #include "Cafe/GraphicPack/GraphicPack2.h" LatteTextureView::LatteTextureView(LatteTexture* texture, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount, Latte::E_DIM dim, Latte::E_GX2SURFFMT format, bool registerView) diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.cpp b/src/Cafe/IOSU/legacy/iosu_fpd.cpp index a667e61c..0445d309 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.cpp +++ b/src/Cafe/IOSU/legacy/iosu_fpd.cpp @@ -10,7 +10,7 @@ #include "util/helpers/StringHelpers.h" #include "Cafe/IOSU/iosu_types_common.h" #include "Cafe/IOSU/nn/iosu_nn_service.h" - +#include "Common/socket.h" #include "Common/CafeString.h" std::mutex g_friend_notification_mutex; diff --git a/src/Cafe/IOSU/legacy/iosu_nim.cpp b/src/Cafe/IOSU/legacy/iosu_nim.cpp index 5cab16f8..dcafb457 100644 --- a/src/Cafe/IOSU/legacy/iosu_nim.cpp +++ b/src/Cafe/IOSU/legacy/iosu_nim.cpp @@ -11,6 +11,8 @@ #include "Cemu/napi/napi.h" #include "Cemu/ncrypto/ncrypto.h" #include "Cafe/CafeSystem.h" +#include "Cafe/Account/Account.h" +#include "config/ActiveSettings.h" namespace iosu { diff --git a/src/Cafe/IOSU/nn/boss/boss_service.cpp b/src/Cafe/IOSU/nn/boss/boss_service.cpp index 48d6be52..3f710c40 100644 --- a/src/Cafe/IOSU/nn/boss/boss_service.cpp +++ b/src/Cafe/IOSU/nn/boss/boss_service.cpp @@ -1,7 +1,5 @@ #include "Cafe/OS/libs/nn_common.h" -#include "Cafe/OS/libs/coreinit/coreinit_Time.h" #include "util/helpers/helpers.h" -#include "Cafe/Filesystem/fsc.h" #include "Cafe/IOSU/iosu_types_common.h" #include "Cafe/IOSU/nn/iosu_nn_service.h" @@ -9,6 +7,7 @@ #include "Cafe/IOSU/legacy/iosu_act.h" #include "Cafe/CafeSystem.h" #include "config/ActiveSettings.h" +#include "Cafe/Account/Account.h" #include "boss_service.h" #include "boss_common.h" diff --git a/src/Cafe/OS/libs/nn_ac/nn_ac.cpp b/src/Cafe/OS/libs/nn_ac/nn_ac.cpp index 4fbfc166..61e7a677 100644 --- a/src/Cafe/OS/libs/nn_ac/nn_ac.cpp +++ b/src/Cafe/OS/libs/nn_ac/nn_ac.cpp @@ -1,6 +1,7 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/OS/libs/nn_common.h" #include "nn_ac.h" +#include "Common/socket.h" #if BOOST_OS_WINDOWS #include diff --git a/src/Cafe/OS/libs/nn_idbe/nn_idbe.cpp b/src/Cafe/OS/libs/nn_idbe/nn_idbe.cpp index 0ea0513a..67252b27 100644 --- a/src/Cafe/OS/libs/nn_idbe/nn_idbe.cpp +++ b/src/Cafe/OS/libs/nn_idbe/nn_idbe.cpp @@ -5,6 +5,7 @@ #include "util/crypto/aes128.h" #include "openssl/sha.h" #include "Cemu/napi/napi.h" +#include "config/ActiveSettings.h" namespace nn { diff --git a/src/Cafe/OS/libs/nsysnet/nsysnet.cpp b/src/Cafe/OS/libs/nsysnet/nsysnet.cpp index 72a64a7b..032ca5a4 100644 --- a/src/Cafe/OS/libs/nsysnet/nsysnet.cpp +++ b/src/Cafe/OS/libs/nsysnet/nsysnet.cpp @@ -8,23 +8,8 @@ #include "Common/socket.h" #if BOOST_OS_UNIX - -#define WSAEWOULDBLOCK EWOULDBLOCK -#define WSAEINPROGRESS EINPROGRESS -#define WSAESHUTDOWN ESHUTDOWN -#define WSAECONNABORTED ECONNABORTED -#define WSAHOST_NOT_FOUND EAI_NONAME -#define WSAENOTCONN ENOTCONN - -#define GETLASTERR errno - -#endif // BOOST_OS_UNIX - -#if BOOST_OS_WINDOWS - -#define GETLASTERR WSAGetLastError() - -#endif //BOOST_OS_WINDOWS +#include +#endif #define WU_AF_INET 2 diff --git a/src/Cafe/TitleList/GameInfo.cpp b/src/Cafe/TitleList/GameInfo.cpp new file mode 100644 index 00000000..ca14c931 --- /dev/null +++ b/src/Cafe/TitleList/GameInfo.cpp @@ -0,0 +1,7 @@ +#include "GameInfo.h" +#include "config/ActiveSettings.h" + +fs::path GameInfo2::GetSaveFolder() +{ + return ActiveSettings::GetMlcPath(fmt::format("usr/save/{:08x}/{:08x}", (GetBaseTitleId() >> 32), GetBaseTitleId() & 0xFFFFFFFF)); +} \ No newline at end of file diff --git a/src/Cafe/TitleList/GameInfo.h b/src/Cafe/TitleList/GameInfo.h index 6e922b93..72295ae8 100644 --- a/src/Cafe/TitleList/GameInfo.h +++ b/src/Cafe/TitleList/GameInfo.h @@ -1,8 +1,5 @@ #pragma once - -#include "config/CemuConfig.h" #include "TitleInfo.h" -#include "config/ActiveSettings.h" class GameInfo2 { @@ -116,10 +113,7 @@ public: return m_aoc.front().GetAppTitleVersion(); } - fs::path GetSaveFolder() - { - return ActiveSettings::GetMlcPath(fmt::format("usr/save/{:08x}/{:08x}", (GetBaseTitleId() >> 32), GetBaseTitleId() & 0xFFFFFFFF)); - } + fs::path GetSaveFolder(); private: bool IsPrioritizedVersionOrFormat(const TitleInfo& currentTitle, const TitleInfo& newTitle) diff --git a/src/Cemu/CMakeLists.txt b/src/Cemu/CMakeLists.txt index eb658d29..22dab274 100644 --- a/src/Cemu/CMakeLists.txt +++ b/src/Cemu/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(CemuComponents Logging/CemuLogging.h napi/napi_act.cpp napi/napi_ec.cpp + napi/napi.cpp napi/napi.h napi/napi_helper.cpp napi/napi_helper.h @@ -40,7 +41,7 @@ endif() set_property(TARGET CemuComponents PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(CemuComponents REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuComponents) target_include_directories(CemuComponents PUBLIC "../") diff --git a/src/Cemu/DiscordPresence/DiscordRPCLite.cpp b/src/Cemu/DiscordPresence/DiscordRPCLite.cpp index bd04e96f..5459d553 100644 --- a/src/Cemu/DiscordPresence/DiscordRPCLite.cpp +++ b/src/Cemu/DiscordPresence/DiscordRPCLite.cpp @@ -22,7 +22,10 @@ namespace shim #include #else #include +#include +#include #include +#include #include #endif @@ -680,4 +683,4 @@ void DiscordRPCLite::WorkerThread(std::string applicationId) pipe.SendPresenceUpdate({}); std::this_thread::sleep_for(std::chrono::milliseconds(75)); } -} \ No newline at end of file +} diff --git a/src/Cemu/napi/napi.cpp b/src/Cemu/napi/napi.cpp new file mode 100644 index 00000000..13812285 --- /dev/null +++ b/src/Cemu/napi/napi.cpp @@ -0,0 +1,12 @@ +#include "napi.h" +#include "config/CemuConfig.h" +#include "config/NetworkSettings.h" +#include "config/ActiveSettings.h" + +namespace NAPI +{ + NetworkService AuthInfo::GetService() const + { + return serviceOverwrite.value_or(ActiveSettings::GetNetworkService()); + } +} diff --git a/src/Cemu/napi/napi.h b/src/Cemu/napi/napi.h index e1397d62..47c843d0 100644 --- a/src/Cemu/napi/napi.h +++ b/src/Cemu/napi/napi.h @@ -1,7 +1,8 @@ #pragma once -#include "config/CemuConfig.h" // for ConsoleLanguage -#include "config/NetworkSettings.h" // for NetworkService -#include "config/ActiveSettings.h" // for GetNetworkService() + +enum class NetworkService; +enum class CafeConsoleRegion; +enum class CafeConsoleLanguage; enum class NAPI_RESULT { @@ -43,10 +44,7 @@ namespace NAPI // service selection, if not set fall back to global setting std::optional serviceOverwrite; - NetworkService GetService() const - { - return serviceOverwrite.value_or(ActiveSettings::GetNetworkService()); - } + NetworkService GetService() const; }; bool NAPI_MakeAuthInfoFromCurrentAccount(AuthInfo& authInfo); // helper function. Returns false if online credentials/dumped files are not available diff --git a/src/Cemu/napi/napi_act.cpp b/src/Cemu/napi/napi_act.cpp index c72d9f47..80f5d7b7 100644 --- a/src/Cemu/napi/napi_act.cpp +++ b/src/Cemu/napi/napi_act.cpp @@ -1,16 +1,12 @@ #include "Common/precompiled.h" +#include "Cafe/Account/Account.h" #include "Cemu/ncrypto/ncrypto.h" #include "napi.h" #include "napi_helper.h" - #include "curl/curl.h" -#include "pugixml.hpp" -#include "Cafe/IOSU/legacy/iosu_crypto.h" - #include "config/ActiveSettings.h" #include "util/helpers/StringHelpers.h" #include "util/highresolutiontimer/HighResolutionTimer.h" -#include "config/LaunchSettings.h" namespace NAPI { diff --git a/src/Cemu/ncrypto/ncrypto.cpp b/src/Cemu/ncrypto/ncrypto.cpp index bbda681f..28b3bac9 100644 --- a/src/Cemu/ncrypto/ncrypto.cpp +++ b/src/Cemu/ncrypto/ncrypto.cpp @@ -1,6 +1,7 @@ #include "Common/precompiled.h" #include "Cemu/ncrypto/ncrypto.h" #include "util/helpers/helpers.h" +#include "config/CemuConfig.h" #include "openssl/bn.h" #include "openssl/ec.h" diff --git a/src/Cemu/ncrypto/ncrypto.h b/src/Cemu/ncrypto/ncrypto.h index 1ed7e91b..5c5e5557 100644 --- a/src/Cemu/ncrypto/ncrypto.h +++ b/src/Cemu/ncrypto/ncrypto.h @@ -1,6 +1,6 @@ #pragma once -#include "Common/betype.h" -#include "config/CemuConfig.h" // for ConsoleRegion + +enum class CafeConsoleRegion; /* OpenSSL forward declarations */ typedef struct ec_key_st EC_KEY; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 8b0a456c..cb80eb69 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -53,7 +53,7 @@ if(LINUX) ) endif() -# the precompiled header is produced by CemuCommon and reused by all the other targets +# CemuCommon always builts the precompiled header target_precompile_headers(CemuCommon PRIVATE precompiled.h) target_include_directories(CemuCommon PUBLIC "../") @@ -70,6 +70,7 @@ endif() # - boost/predef/os.h is included in platform.h # - fmt/core.h is included in precompiled.h target_link_libraries(CemuCommon PUBLIC + Threads::Threads # necessary because some targets may need -pthreads Boost::nowide Boost::headers fmt::fmt diff --git a/src/Common/ExceptionHandler/ELFSymbolTable.cpp b/src/Common/ExceptionHandler/ELFSymbolTable.cpp index 0f297bdd..7258338e 100644 --- a/src/Common/ExceptionHandler/ELFSymbolTable.cpp +++ b/src/Common/ExceptionHandler/ELFSymbolTable.cpp @@ -2,6 +2,8 @@ #include "Common/FileStream.h" #include #include +#include +#include uint16 ELFSymbolTable::FindSection(int type, const std::string_view& name) { @@ -33,7 +35,7 @@ ELFSymbolTable::ELFSymbolTable() { // create file handle int fd = open("/proc/self/exe", O_RDONLY); - if (!fd) + if (fd < 0) return; // retrieve file size. diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index f48bba47..6a4f3f57 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -75,11 +75,11 @@ #include #include #include +#include #include #include #include -#include #include #include diff --git a/src/Common/socket.h b/src/Common/socket.h index 37d53c09..af0805cf 100644 --- a/src/Common/socket.h +++ b/src/Common/socket.h @@ -3,14 +3,32 @@ #if BOOST_OS_WINDOWS #include +#include typedef int socklen_t; -#else +#define GETLASTERR WSAGetLastError() + +#elif BOOST_OS_UNIX #include +#include +#include +#include +#include +#include + +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEINPROGRESS EINPROGRESS +#define WSAESHUTDOWN ESHUTDOWN +#define WSAECONNABORTED ECONNABORTED +#define WSAHOST_NOT_FOUND EAI_NONAME +#define WSAENOTCONN ENOTCONN + +#define GETLASTERR errno + #define SOCKET int #define closesocket close #define SOCKET_ERROR -1 #define INVALID_SOCKET -1 -#endif +#endif // BOOST_OS_UNIX diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 40c4ee22..7f2d6264 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -7,7 +7,7 @@ add_library(CemuAudio set_property(TARGET CemuAudio PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(CemuAudio REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuAudio) if(WIN32) target_sources(CemuAudio PRIVATE diff --git a/src/config/ActiveSettings.cpp b/src/config/ActiveSettings.cpp index 3ecd8855..862ac6c3 100644 --- a/src/config/ActiveSettings.cpp +++ b/src/config/ActiveSettings.cpp @@ -1,12 +1,13 @@ #include "Cafe/GameProfile/GameProfile.h" #include "Cafe/IOSU/legacy/iosu_crypto.h" +#include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" #include "Cafe/CafeSystem.h" #include "Cemu/Logging/CemuLogging.h" #include "config/ActiveSettings.h" #include "config/LaunchSettings.h" +#include "Cafe/Account/Account.h" #include "util/helpers/helpers.h" -#include "Cafe/HW/Latte/Core/Latte.h" void ActiveSettings::SetPaths(bool isPortableMode, const fs::path& executablePath, diff --git a/src/config/ActiveSettings.h b/src/config/ActiveSettings.h index 91555bb4..19cce79b 100644 --- a/src/config/ActiveSettings.h +++ b/src/config/ActiveSettings.h @@ -1,6 +1,4 @@ #pragma once - -#include #include "config/CemuConfig.h" #include "config/NetworkSettings.h" diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index 1c1f8cbc..34720b3f 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -13,7 +13,7 @@ add_library(CemuConfig set_property(TARGET CemuConfig PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(CemuConfig REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuConfig) target_include_directories(CemuConfig PUBLIC "../") diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 51f18a57..097587f8 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -1,10 +1,7 @@ #include "config/CemuConfig.h" -#include "WindowSystem.h" - #include "util/helpers/helpers.h" #include "config/ActiveSettings.h" - -#include "ActiveSettings.h" +#include "Cafe/Account/Account.h" void CemuConfig::SetMLCPath(fs::path path, bool save) { diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index 1f230093..6778ceb5 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -2,8 +2,6 @@ #include "ConfigValue.h" #include "XMLConfig.h" -#include "util/math/vector2.h" -#include "Cafe/Account/Account.h" enum class NetworkService; @@ -489,7 +487,7 @@ struct CemuConfig // account struct { - ConfigValueBounds m_persistent_id{ Account::kMinPersistendId, Account::kMinPersistendId, 0xFFFFFFFF }; + ConfigValueBounds m_persistent_id{0x80000001, 0x80000001, 0xFFFFFFFF}; ConfigValue legacy_online_enabled{false}; ConfigValue legacy_active_service{0}; std::unordered_map service_select; // per-account service index. Key is persistentId diff --git a/src/gui/interface/WindowSystem.h b/src/gui/interface/WindowSystem.h index 89e35ee6..b2346c17 100644 --- a/src/gui/interface/WindowSystem.h +++ b/src/gui/interface/WindowSystem.h @@ -1,6 +1,5 @@ #pragma once -#include "config/CemuConfig.h" #include "input/api/ControllerState.h" namespace WindowSystem diff --git a/src/gui/wxgui/CMakeLists.txt b/src/gui/wxgui/CMakeLists.txt index f625f614..12fc62d9 100644 --- a/src/gui/wxgui/CMakeLists.txt +++ b/src/gui/wxgui/CMakeLists.txt @@ -132,7 +132,7 @@ endif() set_property(TARGET CemuWxGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(CemuWxGui REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuWxGui) target_include_directories(CemuWxGui PUBLIC "../") diff --git a/src/gui/wxgui/GameUpdateWindow.cpp b/src/gui/wxgui/GameUpdateWindow.cpp index b50f1d38..253a7610 100644 --- a/src/gui/wxgui/GameUpdateWindow.cpp +++ b/src/gui/wxgui/GameUpdateWindow.cpp @@ -1,14 +1,10 @@ #include "wxgui/wxgui.h" #include "wxgui/GameUpdateWindow.h" #include "util/helpers/helpers.h" - -#include -#include #include "util/helpers/SystemException.h" -#include "wxgui/CemuApp.h" #include "Cafe/TitleList/GameInfo.h" +#include "config/ActiveSettings.h" #include "wxgui/helpers/wxHelpers.h" -#include "wxHelper.h" wxString _GetTitleIdTypeStr(TitleId titleId) { diff --git a/src/gui/wxgui/wxCemuConfig.h b/src/gui/wxgui/wxCemuConfig.h index 38802ab4..0be9ea2f 100644 --- a/src/gui/wxgui/wxCemuConfig.h +++ b/src/gui/wxgui/wxCemuConfig.h @@ -1,6 +1,5 @@ #pragma once -#include "config/CemuConfig.h" #include "config/XMLConfig.h" #include "util/math/vector2.h" diff --git a/src/imgui/CMakeLists.txt b/src/imgui/CMakeLists.txt index a77b807b..e2d669c5 100644 --- a/src/imgui/CMakeLists.txt +++ b/src/imgui/CMakeLists.txt @@ -18,7 +18,7 @@ endif () set_property(TARGET imguiImpl PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(imguiImpl REUSE_FROM CemuCommon) +cemu_use_precompiled_header(imguiImpl) target_include_directories(imguiImpl PUBLIC "../") diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 7953c26b..d24a5ca0 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -43,7 +43,7 @@ add_library(CemuInput set_property(TARGET CemuInput PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(CemuInput REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuInput) if(WIN32) # XInput diff --git a/src/input/api/Wiimote/l2cap/L2CapWiimote.cpp b/src/input/api/Wiimote/l2cap/L2CapWiimote.cpp index a6bdf574..07c74dd0 100644 --- a/src/input/api/Wiimote/l2cap/L2CapWiimote.cpp +++ b/src/input/api/Wiimote/l2cap/L2CapWiimote.cpp @@ -1,5 +1,7 @@ #include "L2CapWiimote.h" #include +#include +#include constexpr auto comparator = [](const bdaddr_t& a, const bdaddr_t& b) { return bacmp(&a, &b); @@ -145,4 +147,4 @@ bool L2CapWiimote::operator==(const WiimoteDevice& rhs) const if (!mote) return false; return bacmp(&m_addr, &mote->m_addr) == 0; -} \ No newline at end of file +} diff --git a/src/resource/CMakeLists.txt b/src/resource/CMakeLists.txt index f946a528..12415702 100644 --- a/src/resource/CMakeLists.txt +++ b/src/resource/CMakeLists.txt @@ -2,7 +2,7 @@ add_library(CemuResource) set_property(TARGET CemuResource PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(CemuResource REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuResource) enable_language(ASM) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index e8e45ff9..390e9c10 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -89,7 +89,7 @@ endif() set_property(TARGET CemuUtil PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -target_precompile_headers(CemuUtil REUSE_FROM CemuCommon) +cemu_use_precompiled_header(CemuUtil) target_include_directories(CemuUtil PUBLIC "../") From 5e0e3f42da6ebe86b4ea4b6b34e940c61b11daff Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:38:47 +0100 Subject: [PATCH 07/24] coreinit: Improve vcprintf implementation --- src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp | 471 +++++++++++++++----- 1 file changed, 369 insertions(+), 102 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp index 71a7d6e2..52639f3a 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp @@ -7,8 +7,279 @@ namespace coreinit { + sint32 ppc_vcprintf_pad(char* strOut, sint32 maxLength, sint32 padLength, char padChar) + { + padLength = std::min(padLength, maxLength); + sint32 paddedLength = padLength; + while ( padLength > 0) + { + *strOut = padChar; + strOut++; + padLength--; + } + return paddedLength; + } + + // handler for %s + sint32 ppc_vcprintf_str(char* strOut, sint32 maxLength, MEMPTR strPointer, std::optional width, std::optional precision) + { + if (maxLength <= 0) + return 0; + const char* stringData = "(null)"; + if (strPointer) + stringData = strPointer.GetPtr(); + else + { + if (precision.has_value() && *precision > 6) + precision = 6; + } + // get string length + sint32 stringLength = 0; + if (precision.has_value()) + { + stringLength = *precision; + for (sint32 i=0; i 0) + { + // left padding + sint32 paddedLength = *width; + paddedLength -= stringLength; + paddedLength = std::min(paddedLength, maxLength); + if (paddedLength > 0) + { + ppc_vcprintf_pad(strOut, maxLength, paddedLength, ' '); + strOut += paddedLength; + maxLength -= paddedLength; + outputLength += paddedLength; + } + } + stringLength = std::min(stringLength, maxLength); + cemu_assert(stringLength >= 0); + memcpy(strOut, stringData, stringLength); + outputLength += stringLength; + if (width.has_value() && width.value() < 0) + { + sint32 paddedLength = -*width; + paddedLength -= stringLength; + paddedLength = std::min(paddedLength, maxLength - stringLength); + if (paddedLength > 0) + { + ppc_vcprintf_pad(strOut + stringLength, maxLength - stringLength, paddedLength, ' '); + outputLength += paddedLength; + } + } + return outputLength; + } + + // handler for %c + sint32 ppc_vcprintf_char(char* strOut, sint32 maxLength, char character, std::optional width, std::optional precision) + { + if (maxLength <= 0) + return 0; + sint32 outputLength = 0; + if (width.has_value() && width.value() > 0) + { + // left padding + sint32 paddedLength = *width; + paddedLength -= 1; + paddedLength = std::min(paddedLength, maxLength); + if (paddedLength > 0) + { + ppc_vcprintf_pad(strOut, maxLength, paddedLength, ' '); + strOut += paddedLength; + maxLength -= paddedLength; + outputLength += paddedLength; + } + } + if (maxLength > 0) + { + *strOut = character; + outputLength++; + } + if (width.has_value() && width.value() < 0) + { + sint32 paddedLength = -*width; + paddedLength -= 1; + paddedLength = std::min(paddedLength, maxLength - 1); + if (paddedLength > 0) + { + ppc_vcprintf_pad(strOut + 1, maxLength - 1, paddedLength, ' '); + outputLength += paddedLength; + } + } + return outputLength; + } + + char GetHexDigit(uint8 nibble, bool isUppercase) + { + if (nibble >= 10) + { + return isUppercase ? (nibble - 10 + 'A') : (nibble - 10 + 'a'); + } + return nibble + '0'; + } + + // handler for integers + sint32 ppc_vcprintf_integer(char* strOut, sint32 maxLength, bool isNegative, uint64 absValue, bool isHex, bool uppercase, bool alternateForm, std::optional width, std::optional precision, char padChar) + { + if (maxLength <= 0) + return 0; + + // convert to digits string + char digitBuf[64]; + sint32 digitCount = 0; + uint64 tmpValue = absValue; + if (isHex) + { + do + { + digitBuf[digitCount++] = GetHexDigit(tmpValue&0xF, uppercase); + tmpValue >>= 4; + } + while (tmpValue != 0); + } + else + { + do + { + digitBuf[digitCount++] = (char)('0' + (tmpValue % 10)); + tmpValue /= 10; + } + while (tmpValue != 0); + } + + // handle "%.0d" + if (precision.has_value() && *precision == 0 && (!isNegative && absValue == 0)) + digitCount = 0; + + // precision adds leading zeroes for digits only + sint32 precisionZeroes = 0; + if (precision.has_value() && *precision > digitCount) + precisionZeroes = *precision - digitCount; + + sint32 numberLength = digitCount + precisionZeroes; + if (isNegative) + numberLength++; // count the sign + if (isHex && alternateForm && maxLength >= 2 && absValue != 0) + numberLength += 2; + + sint32 outputLength = 0; + + // zero padding is a special case because its between the sign and the number + bool widthZeroPad = width.has_value() && *width > 0 && padChar == '0' && !precision.has_value(); + + // handle space padding left + if (width.has_value() && *width > 0 && !widthZeroPad) + { + sint32 paddedLength = *width - numberLength; + paddedLength = std::min(paddedLength, maxLength); + if (paddedLength > 0) + { + ppc_vcprintf_pad(strOut, maxLength, paddedLength, ' '); + strOut += paddedLength; + maxLength -= paddedLength; + outputLength += paddedLength; + } + } + + // sign + if (isNegative && maxLength > 0) + { + *strOut = '-'; + strOut++; + maxLength--; + outputLength++; + } + // hex alternate form prefix (0x / 0X) + bool hasHexPrefix = false; + if (isHex && alternateForm && maxLength >= 2 && absValue != 0) + { + strOut[0] = '0'; + if (uppercase) + strOut[1] = 'X'; + else + strOut[1] = 'x'; + strOut += 2; + maxLength -= 2; + outputLength += 2; + hasHexPrefix = true; + } + // output zero padding after sign + if (widthZeroPad) + { + sint32 paddedLength = *width - (digitCount + (isNegative ? 1 : 0)); + if (hasHexPrefix) + paddedLength -= 2; + paddedLength = std::min(paddedLength, maxLength); + if (paddedLength > 0) + { + ppc_vcprintf_pad(strOut, maxLength, paddedLength, '0'); + strOut += paddedLength; + maxLength -= paddedLength; + outputLength += paddedLength; + } + } + else if (precisionZeroes > 0) + { + // precision zeroes + sint32 paddedLength = std::min(precisionZeroes, maxLength); + if (paddedLength > 0) + { + ppc_vcprintf_pad(strOut, maxLength, paddedLength, '0'); + strOut += paddedLength; + maxLength -= paddedLength; + outputLength += paddedLength; + } + } + + // digits (the buffer is in reverse order) + sint32 writableDigits = std::min(digitCount, maxLength); + for (sint32 i = 0; i < writableDigits; i++) + strOut[i] = digitBuf[digitCount - 1 - i]; + strOut += writableDigits; + maxLength -= writableDigits; + outputLength += writableDigits; + + // space padding right + if (width.has_value() && *width < 0) + { + sint32 paddedLength = (-*width) - numberLength; + paddedLength = std::min(paddedLength, maxLength); + if (paddedLength > 0) + { + ppc_vcprintf_pad(strOut, maxLength, paddedLength, ' '); + outputLength += paddedLength; + } + } + return outputLength; + } + + // handler for signed decimal integers (64-bit core) + sint32 ppc_vcprintf_decimal64(char* strOut, sint32 maxLength, bool isNegative, uint64 absValue, std::optional width, std::optional precision, char padChar) + { + return ppc_vcprintf_integer(strOut, maxLength, isNegative, absValue, false, false, false, width, precision, padChar); + } + sint32 ppc_vprintf(const char* formatStr, char* strOut, sint32 maxLength, ppc_va_list* vargs) { + if (maxLength <= 0) + return 0; char tempStr[4096]; sint32 writeIndex = 0; while (*formatStr) @@ -29,8 +300,9 @@ namespace coreinit continue; } // flags + char padChar = ' '; + bool alternateForm = false; bool flag_leftJustify = false; - bool flag_zeroPadding = false; if (*formatStr == '-') { flag_leftJustify = true; @@ -38,114 +310,143 @@ namespace coreinit } if (*formatStr == '+') { - // todo formatStr++; } if (*formatStr == ' ') { - // todo formatStr++; } if (*formatStr == '#') { - // todo + alternateForm = true; formatStr++; } if (*formatStr == '0') { - flag_zeroPadding = true; + padChar = '0'; formatStr++; } // width + std::optional width; if (*formatStr == '*') { - cemu_assert_debug(false); formatStr++; + width = *(sint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32); + if (flag_leftJustify) + width = -abs(*width); } - bool widthIsSpecified = false; - sint32 width = 0; - while (*formatStr >= '0' && *formatStr <= '9') + else { - width *= 10; - width += (*formatStr - '0'); - formatStr++; - widthIsSpecified = true; + width = 0; + while (*formatStr >= '0' && *formatStr <= '9') + { + *width *= 10; + *width += (*formatStr - '0'); + formatStr++; + } + if (flag_leftJustify) + width = -*width; } // precision + std::optional precision; if (*formatStr == '.') { formatStr++; if (*formatStr == '*') { - cemu_assert_debug(false); - } - while (*formatStr >= '0' && *formatStr <= '9') - { + precision = *(sint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32); + if (*precision < 0) + precision.reset(); formatStr++; } + else + { + precision = 0; + while (*formatStr >= '0' && *formatStr <= '9') + { + *precision *= 10; + *precision += (*formatStr - '0'); + formatStr++; + } + } } // length + specifier char tempFormat[64]; - if (*formatStr == 'X' || *formatStr == 'x' || *formatStr == 'u' || *formatStr == 'd' || *formatStr == 'p' || *formatStr == 'i' || - (formatStr[0] == 'l' && formatStr[1] == 'd')) + if (strncmp(formatStr, "lld", 3) == 0 || strncmp(formatStr, "lli", 3) == 0) { - // number - formatStr++; - strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); - if ((formatStr - formatStart) < sizeof(tempFormat)) - tempFormat[(formatStr - formatStart)] = '\0'; + // 64bit decimal (signed) + formatStr += 3; + sint64 v = *(sint64be*)_ppc_va_arg(vargs, ppc_va_type::INT64); + if (v >= 0) + writeIndex += ppc_vcprintf_decimal64(strOut + writeIndex, maxLength - writeIndex, false, (uint64)v, width, precision, padChar); else - tempFormat[sizeof(tempFormat) - 1] = '\0'; - sint32 tempLen = sprintf(tempStr, tempFormat, (uint32)*(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32)); - for (sint32 i = 0; i < tempLen; i++) - { - if (writeIndex >= maxLength) - break; - strOut[writeIndex] = tempStr[i]; - writeIndex++; - } + writeIndex += ppc_vcprintf_decimal64(strOut + writeIndex, maxLength - writeIndex, true, ~(uint64)v + 1, width, precision, padChar); + } + else if (strncmp(formatStr, "llu", 3) == 0) + { + // 64bit decimal (unsigned) + formatStr += 3; + uint64 v = *(uint64be*)_ppc_va_arg(vargs, ppc_va_type::INT64); + writeIndex += ppc_vcprintf_decimal64(strOut + writeIndex, maxLength - writeIndex, false, v, width, precision, padChar); + } + else if (*formatStr == 'd' || *formatStr == 'i' || strncmp(formatStr, "ld", 2) == 0 || strncmp(formatStr, "li", 2) == 0) + { + // 32bit decimal (signed) + if (formatStr[0] == 'l') + formatStr += 2; + else + formatStr++; + sint32 v = *(sint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32); + writeIndex += ppc_vcprintf_decimal64(strOut + writeIndex, maxLength - writeIndex, v < 0, (uint64)abs((sint64)v), width, precision, padChar); + } + else if (*formatStr == 'u' || strncmp(formatStr, "lu", 2) == 0) + { + // 32bit decimal (unsigned) + if (formatStr[0] == 'l') + formatStr += 2; + else + formatStr++; + uint32 v = *(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32); + writeIndex += ppc_vcprintf_decimal64(strOut + writeIndex, maxLength - writeIndex, false, (uint64)v, width, precision, padChar); + } + else if (strncmp(formatStr, "llx", 3) == 0 || strncmp(formatStr, "llX", 3) == 0) + { + // 64bit hexadecimal + bool isUppercase = formatStr[2] == 'X'; + formatStr += 3; + uint64 v = *(uint64be*)_ppc_va_arg(vargs, ppc_va_type::INT64); + writeIndex += ppc_vcprintf_integer(strOut + writeIndex, maxLength - writeIndex, false, (uint64)v, true, isUppercase, alternateForm, width, precision, padChar); + } + else if (*formatStr == 'x' || *formatStr == 'X') + { + // 32bit hexadecimal + bool isUppercase = *formatStr == 'X'; + formatStr++; + uint32 v = *(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32); + writeIndex += ppc_vcprintf_integer(strOut + writeIndex, maxLength - writeIndex, false, (uint64)v, true, isUppercase, alternateForm, width, precision, padChar); + } + else if (*formatStr == 'p') + { + // pointer + formatStr++; + uint32 v = *(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32); + if (!precision.has_value()) + precision = 8; + writeIndex += ppc_vcprintf_integer(strOut + writeIndex, maxLength - writeIndex, false, (uint64)v, true, false, true, width, precision, padChar); } else if (*formatStr == 's') { // string formatStr++; - strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); - if ((formatStr - formatStart) < sizeof(tempFormat)) - tempFormat[(formatStr - formatStart)] = '\0'; - else - tempFormat[sizeof(tempFormat) - 1] = '\0'; MPTR strOffset = *(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32); - sint32 tempLen = 0; - if (strOffset == MPTR_NULL) - tempLen = sprintf(tempStr, "NULL"); - else - tempLen = sprintf(tempStr, tempFormat, memory_getPointerFromVirtualOffset(strOffset)); - for (sint32 i = 0; i < tempLen; i++) - { - if (writeIndex >= maxLength) - break; - strOut[writeIndex] = tempStr[i]; - writeIndex++; - } - strOut[std::min(maxLength - 1, writeIndex)] = '\0'; + writeIndex += ppc_vcprintf_str(strOut + writeIndex, maxLength - writeIndex, MEMPTR(strOffset), width, precision); } else if (*formatStr == 'c') { // character formatStr++; - strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); - if ((formatStr - formatStart) < sizeof(tempFormat)) - tempFormat[(formatStr - formatStart)] = '\0'; - else - tempFormat[sizeof(tempFormat) - 1] = '\0'; - sint32 tempLen = sprintf(tempStr, tempFormat, (uint32)*(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32)); - for (sint32 i = 0; i < tempLen; i++) - { - if (writeIndex >= maxLength) - break; - strOut[writeIndex] = tempStr[i]; - writeIndex++; - } + char character = (char)(uint32)*(uint32be*)_ppc_va_arg(vargs, ppc_va_type::INT32); + writeIndex += ppc_vcprintf_char(strOut + writeIndex, maxLength - writeIndex, character, width, precision); } else if (*formatStr == 'f' || *formatStr == 'g' || *formatStr == 'G') { @@ -182,48 +483,13 @@ namespace coreinit writeIndex++; } } - else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && (formatStr[2] == 'x' || formatStr[2] == 'X'))) - { - formatStr += 3; - // 64bit int - strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); - if ((formatStr - formatStart) < sizeof(tempFormat)) - tempFormat[(formatStr - formatStart)] = '\0'; - else - tempFormat[sizeof(tempFormat) - 1] = '\0'; - sint32 tempLen = sprintf(tempStr, tempFormat, (uint64)*(uint64be*)_ppc_va_arg(vargs, ppc_va_type::INT64)); - for (sint32 i = 0; i < tempLen; i++) - { - if (writeIndex >= maxLength) - break; - strOut[writeIndex] = tempStr[i]; - writeIndex++; - } - } - else if ((formatStr[0] == 'l' && formatStr[1] == 'l' && formatStr[2] == 'd')) - { - formatStr += 3; - // signed integer (64bit) - strncpy(tempFormat, formatStart, std::min((std::ptrdiff_t)sizeof(tempFormat) - 1, formatStr - formatStart)); - if ((formatStr - formatStart) < sizeof(tempFormat)) - tempFormat[(formatStr - formatStart)] = '\0'; - else - tempFormat[sizeof(tempFormat) - 1] = '\0'; - sint32 tempLen = sprintf(tempStr, tempFormat, (sint64)*(sint64be*)_ppc_va_arg(vargs, ppc_va_type::INT64)); - for (sint32 i = 0; i < tempLen; i++) - { - if (writeIndex >= maxLength) - break; - strOut[writeIndex] = tempStr[i]; - writeIndex++; - } - } else { // unsupported / unknown specifier cemu_assert_debug(false); break; } + cemu_assert(writeIndex <= maxLength); // sanity check } else { @@ -234,8 +500,9 @@ namespace coreinit formatStr++; } } - strOut[std::min(writeIndex, maxLength - 1)] = '\0'; - return std::min(writeIndex, maxLength - 1); + writeIndex = std::min(writeIndex, maxLength-1); + strOut[writeIndex] = '\0'; + return writeIndex; } /* coreinit logging and string format */ From c92bef0352d1ca894e5c40d250b14218c97893c2 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 19 Mar 2026 20:49:02 +0100 Subject: [PATCH 08/24] coreinit: Refactor OSScreen --- .../OS/libs/coreinit/coreinit_OSScreen.cpp | 74 +++++++------------ src/Cafe/OS/libs/coreinit/coreinit_OSScreen.h | 14 +++- 2 files changed, 38 insertions(+), 50 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.cpp b/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.cpp index 7b51629e..a0ee1ac2 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.cpp @@ -5,9 +5,6 @@ #include "Cafe/OS/libs/coreinit/coreinit_OSScreen_font.h" #include "Cafe/OS/libs/coreinit/coreinit_OSScreen.h" -#define OSSCREEN_TV (0) -#define OSSCREEN_DRC (1) - namespace coreinit { @@ -39,19 +36,16 @@ namespace coreinit } } - void coreinitExport_OSScreenInit(PPCInterpreter_t* hCPU) + void OSScreenInit() { // todo - init VI registers? - - osLib_returnFromFunction(hCPU, 0); } - void coreinitExport_OSScreenGetBufferSizeEx(PPCInterpreter_t* hCPU) + uint32 OSScreenGetBufferSizeEx(uint32 screenIndex) { - ppcDefineParamU32(screenIndex, 0); cemu_assert(screenIndex < 2); uint32 bufferSize = screenSizes[screenIndex].pitch * screenSizes[screenIndex].y * 4 * 2; - osLib_returnFromFunction(hCPU, bufferSize); + return bufferSize; } void _updateCurrentDrawScreen(sint32 screenIndex) @@ -64,59 +58,40 @@ namespace coreinit currentScreenBasePtr[screenIndex] = memory_getPointerFromPhysicalOffset(LatteGPUState.osScreen.screen[screenIndex].physPtr); } - void coreinitExport_OSScreenSetBufferEx(PPCInterpreter_t* hCPU) + void OSScreenSetBufferEx(uint32 screenIndex, uint32 buffer) { - ppcDefineParamU32(screenIndex, 0); - ppcDefineParamU32(buffer, 1); cemu_assert(screenIndex < 2); LatteGPUState.osScreen.screen[screenIndex].physPtr = buffer; _updateCurrentDrawScreen(screenIndex); - osLib_returnFromFunction(hCPU, 0); } - void coreinitExport_OSScreenEnableEx(PPCInterpreter_t* hCPU) + void OSScreenEnableEx(uint32 screenIndex, uint32 isEnabled) { - ppcDefineParamU32(screenIndex, 0); - ppcDefineParamU32(isEnabled, 1); cemu_assert(screenIndex < 2); LatteGPUState.osScreen.screen[screenIndex].isEnabled = isEnabled != 0; - osLib_returnFromFunction(hCPU, 0); } - void coreinitExport_OSScreenClearBufferEx(PPCInterpreter_t* hCPU) + void OSScreenClearBufferEx(uint32 screenIndex, uint32 color) { - ppcDefineParamU32(screenIndex, 0); - ppcDefineParamU32(color, 1); cemu_assert(screenIndex < 2); _OSScreen_Clear(screenIndex, color); - osLib_returnFromFunction(hCPU, 0); } - void coreinitExport_OSScreenFlipBuffersEx(PPCInterpreter_t* hCPU) + void OSScreenFlipBuffersEx(uint32 screenIndex) { - ppcDefineParamU32(screenIndex, 0); cemu_assert(screenIndex < 2); LatteGPUState.osScreen.screen[screenIndex].flipRequestCount++; _updateCurrentDrawScreen(screenIndex); - osLib_returnFromFunction(hCPU, 0); } - void coreinitExport_OSScreenPutPixelEx(PPCInterpreter_t* hCPU) + void OSScreenPutPixelEx(uint32 screenIndex, sint32 x, sint32 y, uint32 color) { - ppcDefineParamU32(screenIndex, 0); - ppcDefineParamS32(x, 1); - ppcDefineParamS32(y, 2); - ppcDefineParamU32(color, 3); if (screenIndex >= 2) - { - osLib_returnFromFunction(hCPU, 0); return; - } if (x >= 0 && x < screenSizes[screenIndex].x && y >= 0 && y < screenSizes[screenIndex].y) { *(uint32*)((uint8*)currentScreenBasePtr[screenIndex] + (x + y * screenSizes[screenIndex].pitch) * 4) = _swapEndianS32(color); } - osLib_returnFromFunction(hCPU, 0); } const char* osScreenCharset = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; @@ -135,13 +110,8 @@ namespace coreinit return -1; } - void coreinitExport_OSScreenPutFontEx(PPCInterpreter_t* hCPU) + void OSScreenPutFontEx(uint32 screenIndex, sint32 x, sint32 y, const char* str) { - ppcDefineParamU32(screenIndex, 0); - ppcDefineParamS32(x, 1); - ppcDefineParamS32(y, 2); - ppcDefineParamStr(str, 3); - // characters are: // 16 x 32 (including the margin) // with a margin of 4 x 8 @@ -149,16 +119,23 @@ namespace coreinit if (y < 0) { debug_printf("OSScreenPutFontEx: y has invalid value\n"); - osLib_returnFromFunction(hCPU, 0); return; } sint32 px = x * 16; sint32 py = y * 24; + sint32 initialPx = px; while (*str) { sint32 charIndex = _getOSScreenFontCharIndex(*str); + if (*str == (uint8)'\n') + { + px = initialPx; + py += 24; + str++; + continue; + } if (charIndex >= 0) { const uint8* charBitmap = osscreenBitmapFont + charIndex * 50; @@ -175,18 +152,17 @@ namespace coreinit px += 16; str++; } - osLib_returnFromFunction(hCPU, 0); } void InitializeOSScreen() { - osLib_addFunction("coreinit", "OSScreenInit", coreinitExport_OSScreenInit); - osLib_addFunction("coreinit", "OSScreenGetBufferSizeEx", coreinitExport_OSScreenGetBufferSizeEx); - osLib_addFunction("coreinit", "OSScreenSetBufferEx", coreinitExport_OSScreenSetBufferEx); - osLib_addFunction("coreinit", "OSScreenEnableEx", coreinitExport_OSScreenEnableEx); - osLib_addFunction("coreinit", "OSScreenClearBufferEx", coreinitExport_OSScreenClearBufferEx); - osLib_addFunction("coreinit", "OSScreenFlipBuffersEx", coreinitExport_OSScreenFlipBuffersEx); - osLib_addFunction("coreinit", "OSScreenPutPixelEx", coreinitExport_OSScreenPutPixelEx); - osLib_addFunction("coreinit", "OSScreenPutFontEx", coreinitExport_OSScreenPutFontEx); + cafeExportRegister("coreinit", OSScreenInit, LogType::Placeholder); + cafeExportRegister("coreinit", OSScreenGetBufferSizeEx, LogType::Placeholder); + cafeExportRegister("coreinit", OSScreenSetBufferEx, LogType::Placeholder); + cafeExportRegister("coreinit", OSScreenEnableEx, LogType::Placeholder); + cafeExportRegister("coreinit", OSScreenClearBufferEx, LogType::Placeholder); + cafeExportRegister("coreinit", OSScreenFlipBuffersEx, LogType::Placeholder); + cafeExportRegister("coreinit", OSScreenPutPixelEx, LogType::Placeholder); + cafeExportRegister("coreinit", OSScreenPutFontEx, LogType::Placeholder); } } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.h b/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.h index 29011123..1d2b3b36 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_OSScreen.h @@ -1,6 +1,18 @@ #pragma once +#define OSSCREEN_TV (0) +#define OSSCREEN_DRC (1) + namespace coreinit { + void OSScreenInit(); + uint32 OSScreenGetBufferSizeEx(uint32 screenIndex); + void OSScreenSetBufferEx(uint32 screenIndex, uint32 buffer); + void OSScreenEnableEx(uint32 screenIndex, uint32 isEnabled); + void OSScreenClearBufferEx(uint32 screenIndex, uint32 color); + void OSScreenFlipBuffersEx(uint32 screenIndex); + void OSScreenPutPixelEx(uint32 screenIndex, sint32 x, sint32 y, uint32 color); + void OSScreenPutFontEx(uint32 screenIndex, sint32 x, sint32 y, const char* str); + void InitializeOSScreen(); -} \ No newline at end of file +} From 0121e63408d5f9913dae4dfbf8d7203d09911168 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 19 Mar 2026 20:49:25 +0100 Subject: [PATCH 09/24] coreinit: Implement OSFatal --- src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp | 28 +++++++++++++++++++++ src/Cafe/OS/libs/coreinit/coreinit_Misc.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp index 52639f3a..13ef6613 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Misc.cpp @@ -1,6 +1,7 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/OS/libs/coreinit/coreinit_Misc.h" #include "Cafe/OS/libs/coreinit/coreinit_MessageQueue.h" +#include "Cafe/OS/libs/coreinit/coreinit_OSScreen.h" #include "Cafe/CafeSystem.h" #include "Cafe/Filesystem/fsc.h" #include @@ -618,6 +619,32 @@ namespace coreinit WriteCafeConsole(CafeLogType::OSCONSOLE, strPtr, length); } + void OSFatal(const char* msg) + { + // print to log + ppc_define_va_list(1, 0); + COSVReport(COSReportModule::coreinit, COSReportLevel::Error, msg, &vargs); + + // print to screen + OSScreenInit(); + uint32 tvBufferSize = OSScreenGetBufferSizeEx(OSSCREEN_TV); + OSScreenSetBufferEx(OSSCREEN_TV, MEMORY_MEM1_AREA_ADDR); + OSScreenSetBufferEx(OSSCREEN_DRC, MEMORY_MEM1_AREA_ADDR + tvBufferSize); + OSScreenEnableEx(OSSCREEN_TV, 1); + OSScreenEnableEx(OSSCREEN_DRC, 1); + + while ( true ) + { + OSScreenClearBufferEx(OSSCREEN_TV, 0); + OSScreenClearBufferEx(OSSCREEN_DRC, 0); + OSScreenPutFontEx(OSSCREEN_TV, 0, 0, msg); + OSScreenPutFontEx(OSSCREEN_DRC, 0, 0, msg); + OSScreenFlipBuffersEx(OSSCREEN_TV); + OSScreenFlipBuffersEx(OSSCREEN_DRC); + OSSleepTicks(ESPRESSO_TIMER_CLOCK / 30); + } + } + /* home button menu */ bool g_homeButtonMenuEnabled = false; @@ -830,6 +857,7 @@ namespace coreinit cafeExportRegister("coreinit", OSVReport, LogType::Placeholder); cafeExportRegister("coreinit", OSLogPrintf, LogType::Placeholder); cafeExportRegister("coreinit", OSConsoleWrite, LogType::Placeholder); + cafeExportRegister("coreinit", OSFatal, LogType::Placeholder); cafeExportRegister("coreinit", OSGetPFID, LogType::Placeholder); cafeExportRegister("coreinit", OSGetUPID, LogType::Placeholder); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Misc.h b/src/Cafe/OS/libs/coreinit/coreinit_Misc.h index 36f6b06a..5e7d52b8 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Misc.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Misc.h @@ -38,6 +38,8 @@ namespace coreinit Info = 2 }; + void OSFatal(const char* msg); + sint32 ppc_vprintf(const char* formatStr, char* strOut, sint32 maxLength, ppc_va_list* vargs); void miscInit(); From 8065520583accbeeec750141de769d5712f275ba Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:48:48 +0100 Subject: [PATCH 10/24] PPCRec: Fix eflags optimization --- src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp b/src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp index 7671a163..604fc571 100644 --- a/src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/IML/IMLOptimizer.cpp @@ -678,7 +678,8 @@ void IMLOptimizerX86_SubstituteCJumpForEflagsJump(IMLOptimizerRegIOAnalysis& reg // we can turn the jump into an eflags jump cjumpInstr.make_x86_eflags_jcc(cond, invertedCondition); - if (IMLUtil_CountRegisterReadsInRange(seg, cmpInstrIndex, cjmpInstIndex, regCondBool.GetRegID()) > 1 || regIoAnalysis.IsRegisterNeededAtEndOfSegment(seg, regCondBool.GetRegID())) + // note: x86_eflags_jcc doesn't count towards cond reg reads, so we have to check > 0 here instead of > 1 + if (IMLUtil_CountRegisterReadsInRange(seg, cmpInstrIndex, cjmpInstIndex, regCondBool.GetRegID()) > 0 || regIoAnalysis.IsRegisterNeededAtEndOfSegment(seg, regCondBool.GetRegID())) return; // bool register is used beyond the CMP, we can't drop it auto& cmpInstr = seg.imlList[cmpInstrIndex]; From f893e2928c47b8c0e3fcd19ab7739cf290b3de48 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Sat, 21 Mar 2026 12:25:46 +0100 Subject: [PATCH 11/24] GX2: Fix return type of GX2DrawDone --- src/Cafe/OS/libs/gx2/GX2_Event.cpp | 4 ++-- src/Cafe/OS/libs/gx2/GX2_Event.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Cafe/OS/libs/gx2/GX2_Event.cpp b/src/Cafe/OS/libs/gx2/GX2_Event.cpp index 645f0a79..3bb8e6a2 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Event.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Event.cpp @@ -214,7 +214,7 @@ namespace GX2 __OSUnlockScheduler(); } - void GX2DrawDone() + bool GX2DrawDone() { // optional force full sync (texture readback and occlusion queries) bool forceFullSync = false; @@ -231,7 +231,7 @@ namespace GX2 GX2Command_Flush(0x100, true); uint64 ts = GX2GetLastSubmittedTimeStamp(); - GX2WaitTimeStamp(ts); + return GX2WaitTimeStamp(ts); } void GX2Init_event() diff --git a/src/Cafe/OS/libs/gx2/GX2_Event.h b/src/Cafe/OS/libs/gx2/GX2_Event.h index 09cb073c..8795aaa3 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Event.h +++ b/src/Cafe/OS/libs/gx2/GX2_Event.h @@ -8,7 +8,7 @@ namespace GX2 void GX2EventInit(); void GX2WaitForVsync(); void GX2WaitForFlip(); - void GX2DrawDone(); + bool GX2DrawDone(); enum class GX2CallbackEventType { From f2f70e5df8c8252d3f6860541d50e176a74619aa Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 23 Mar 2026 17:11:49 +0100 Subject: [PATCH 12/24] GX2: Fix several missing/incorrect cmd reserve calls --- src/Cafe/OS/libs/gx2/GX2.cpp | 2 ++ src/Cafe/OS/libs/gx2/GX2_Draw.cpp | 2 +- src/Cafe/OS/libs/gx2/GX2_Event.cpp | 2 +- src/Cafe/OS/libs/gx2/GX2_Misc.cpp | 1 + src/Cafe/OS/libs/gx2/GX2_State.cpp | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Cafe/OS/libs/gx2/GX2.cpp b/src/Cafe/OS/libs/gx2/GX2.cpp index b5e75b90..dcaa240b 100644 --- a/src/Cafe/OS/libs/gx2/GX2.cpp +++ b/src/Cafe/OS/libs/gx2/GX2.cpp @@ -294,6 +294,8 @@ void gx2Export_GX2SetSemaphore(PPCInterpreter_t* hCPU) } uint32 semaphoreControl = (SEM_SEL << 29); semaphoreControl |= 0x1000; // WAIT_ON_SIGNAL + + GX2::GX2ReserveCmdSpace(3); gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_MEM_SEMAPHORE, 2)); gx2WriteGather_submitU32AsBE(memory_virtualToPhysical(semaphoreMPTR)); // semaphore physical address gx2WriteGather_submitU32AsBE(semaphoreControl); // control diff --git a/src/Cafe/OS/libs/gx2/GX2_Draw.cpp b/src/Cafe/OS/libs/gx2/GX2_Draw.cpp index 958978e1..3a5fff5b 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Draw.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Draw.cpp @@ -56,7 +56,7 @@ namespace GX2 void GX2DrawIndexedEx2(GX2PrimitiveMode2 primitiveMode, uint32 count, GX2IndexType indexType, void* indexData, uint32 baseVertex, uint32 numInstances, uint32 baseInstance) { - GX2ReserveCmdSpace(3 + 3 + 2 + 2 + 6); + GX2ReserveCmdSpace(3 + 3 + 3 + 2 + 2 + 6 + 3); gx2WriteGather_submit( // IT_SET_CTL_CONST pm4HeaderType3(IT_SET_CTL_CONST, 2), 0, diff --git a/src/Cafe/OS/libs/gx2/GX2_Event.cpp b/src/Cafe/OS/libs/gx2/GX2_Event.cpp index 3bb8e6a2..338b7d79 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Event.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Event.cpp @@ -38,7 +38,7 @@ namespace GX2 void GX2SubmitUserTimeStamp(uint64* timestampOut, uint64 value, GX2PipeEventType eventType, uint32 triggerInterrupt) { - GX2ReserveCmdSpace(7); + GX2ReserveCmdSpace(11); MPTR physTimestampAddr = memory_virtualToPhysical(memory_getVirtualOffsetFromPointer(timestampOut)); uint32 valHigh = (uint32)(value >> 32); diff --git a/src/Cafe/OS/libs/gx2/GX2_Misc.cpp b/src/Cafe/OS/libs/gx2/GX2_Misc.cpp index 564b787e..ada03b2d 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Misc.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Misc.cpp @@ -69,6 +69,7 @@ void gx2Export_GX2SampleBottomGPUCycle(PPCInterpreter_t* hCPU) // Whats a good solution here? Should we implement it correctly and instead rely on graphic pack patches to patch out the dynamic scaling? // some known affected games: Wind Waker HD, Super Mario 3D World + GX2::GX2ReserveCmdSpace(2); gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_SAMPLE_TIMER, 1)); gx2WriteGather_submitU32AsBE(hCPU->gpr[3]); osLib_returnFromFunction(hCPU, 0); diff --git a/src/Cafe/OS/libs/gx2/GX2_State.cpp b/src/Cafe/OS/libs/gx2/GX2_State.cpp index 795ff527..a85b5dce 100644 --- a/src/Cafe/OS/libs/gx2/GX2_State.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_State.cpp @@ -648,6 +648,7 @@ namespace GX2 else { // legacy style + GX2ReserveCmdSpace(3); gx2WriteGather_submitU32AsBE(pm4HeaderType3(IT_HLE_SPECIAL_STATE, 2)); gx2WriteGather_submitU32AsBE((uint32)stateId); // state id gx2WriteGather_submitU32AsBE(isEnabled); // enable/disable bool From 6648a9c225b67a5aa647a31e131fc47828293a03 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Tue, 24 Mar 2026 03:33:53 +0100 Subject: [PATCH 13/24] coreinit: Log errors in OSUnlockMutex instead of debug asserts --- .../OS/libs/coreinit/coreinit_Synchronization.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp index e81cc577..4f85537b 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Synchronization.cpp @@ -300,8 +300,16 @@ namespace coreinit void OSUnlockMutexInternal(OSMutex* mutex) { OSThread_t* currentThread = OSGetCurrentThread(); - cemu_assert_debug(mutex->owner == currentThread); - cemu_assert_debug(mutex->lockCount > 0); + if (mutex->lockCount == 0) + { + cemuLog_log(LogType::APIErrors, "OSUnlockMutex called on a mutex which is not locked"); + return; + } + if (mutex->owner != currentThread) + { + cemuLog_log(LogType::APIErrors, "OSUnlockMutex called by a different thread than the one owning it (owner=0x{:08x} currentThread=0x{:08x})", mutex->owner.GetMPTR(), MEMPTR(currentThread).GetMPTR()); + return; + } mutex->lockCount = mutex->lockCount - 1; if (mutex->lockCount == 0) { From 2913a8ca7aa5568f4b7b572a57cef83384f69f06 Mon Sep 17 00:00:00 2001 From: Squall Leonhart Date: Sun, 29 Mar 2026 17:56:12 +1100 Subject: [PATCH 14/24] input: Fix bitwise operation for button mask (#1851) Fixes classic controller buttons which were shifted beyond 32bit range --- src/input/api/Wiimote/NativeWiimoteController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/api/Wiimote/NativeWiimoteController.cpp b/src/input/api/Wiimote/NativeWiimoteController.cpp index 9aa56d9c..75025e09 100644 --- a/src/input/api/Wiimote/NativeWiimoteController.cpp +++ b/src/input/api/Wiimote/NativeWiimoteController.cpp @@ -232,7 +232,7 @@ ControllerState NativeWiimoteController::raw_state() for (int i = 0; i < std::numeric_limits::digits; i++) { // OR with base buttons - if((buttons & (1 << i))) + if((buttons & (1ULL << i))) result.buttons.SetButtonState(i, true); } result.axis = classic.left_axis; From a1d47c37b670452acb2484045dc18c37f772d2ef Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:07:37 +0200 Subject: [PATCH 15/24] unix: handle edge case where path does not exist fixes crash when passing an invalid path with the "-g" launch option the algorithm would recurse up the tree infinitely looking for blank parent directories probably fixes more things --- src/Common/unix/FileStream_unix.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Common/unix/FileStream_unix.cpp b/src/Common/unix/FileStream_unix.cpp index 0e9f1189..33ce5f10 100644 --- a/src/Common/unix/FileStream_unix.cpp +++ b/src/Common/unix/FileStream_unix.cpp @@ -7,6 +7,9 @@ fs::path findPathCI(const fs::path& path) fs::path fName = path.filename(); fs::path parentPath = path.parent_path(); + if (parentPath.empty()) + return path; + if (!fs::exists(parentPath)) { auto CIParent = findPathCI(parentPath); From 72b5740ed47575ffc92b977cba455ffd5842e3fa Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Fri, 3 Apr 2026 02:30:48 +0200 Subject: [PATCH 16/24] unix: check current working directory for case-insensitive relative paths when no more parents --- src/Common/unix/FileStream_unix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/unix/FileStream_unix.cpp b/src/Common/unix/FileStream_unix.cpp index 33ce5f10..e7b6cf7b 100644 --- a/src/Common/unix/FileStream_unix.cpp +++ b/src/Common/unix/FileStream_unix.cpp @@ -8,7 +8,7 @@ fs::path findPathCI(const fs::path& path) fs::path fName = path.filename(); fs::path parentPath = path.parent_path(); if (parentPath.empty()) - return path; + parentPath = "."; if (!fs::exists(parentPath)) { From fd1a1d16885783c0a5c3527b59254bd17220ff31 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Fri, 3 Apr 2026 02:47:52 +0200 Subject: [PATCH 17/24] unix: simplify the case-insensitivity algorithm --- src/Common/unix/FileStream_unix.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Common/unix/FileStream_unix.cpp b/src/Common/unix/FileStream_unix.cpp index e7b6cf7b..53880527 100644 --- a/src/Common/unix/FileStream_unix.cpp +++ b/src/Common/unix/FileStream_unix.cpp @@ -9,13 +9,8 @@ fs::path findPathCI(const fs::path& path) fs::path parentPath = path.parent_path(); if (parentPath.empty()) parentPath = "."; - - if (!fs::exists(parentPath)) - { - auto CIParent = findPathCI(parentPath); - if (fs::exists(CIParent)) - return findPathCI(CIParent / fName); - } + else if (!fs::exists(parentPath)) + parentPath = findPathCI(parentPath); std::error_code listErr; for (auto&& dirEntry : fs::directory_iterator(parentPath, listErr)) From c16936d381431b2463665cd1cb990982cb455022 Mon Sep 17 00:00:00 2001 From: GaryOderNichts <12049776+GaryOderNichts@users.noreply.github.com> Date: Sat, 4 Apr 2026 16:32:12 +0200 Subject: [PATCH 18/24] build: Link zstd explicitly (#1860) --- src/Cafe/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 3ca34e35..78a8cce0 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -634,6 +634,7 @@ target_link_libraries(CemuCafe PRIVATE ZLIB::ZLIB CURL::libcurl ih264d + zstd::zstd ${glslang_target} PUBLIC OpenSSL::SSL From b3e310220e53a39d32dc85bb4d6a028d90fe070d Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Sat, 4 Apr 2026 16:45:00 +0200 Subject: [PATCH 19/24] vulkan: use the right exit function when vulkan fails to initialize. (fixes: #1833) --- src/gui/wxgui/canvas/VulkanCanvas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/wxgui/canvas/VulkanCanvas.cpp b/src/gui/wxgui/canvas/VulkanCanvas.cpp index df80b000..5b0a0a33 100644 --- a/src/gui/wxgui/canvas/VulkanCanvas.cpp +++ b/src/gui/wxgui/canvas/VulkanCanvas.cpp @@ -43,7 +43,7 @@ VulkanCanvas::VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_wi auto msg = formatWxString(_("Error when initializing Vulkan renderer:\n{}"), ex.what()); wxMessageDialog dialog(this, msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); dialog.ShowModal(); - exit(0); + _exit(0); } wxWindow::EnableTouchEvents(wxTOUCH_PAN_GESTURES); From b9b46ecb65e2eb1c15793380048309df73d2084e Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Sat, 4 Apr 2026 19:14:49 +0200 Subject: [PATCH 20/24] Vulkan: Use LLVM shader compiler for BOTW on Linux + Mesa (#1857) Workaround for GPU crashes when using certain runes in BOTW Can be overridden by setting RADV_DEBUG=aco --- src/gui/wxgui/MainWindow.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/gui/wxgui/MainWindow.cpp b/src/gui/wxgui/MainWindow.cpp index 4ef4fe08..5de19f69 100644 --- a/src/gui/wxgui/MainWindow.cpp +++ b/src/gui/wxgui/MainWindow.cpp @@ -621,6 +621,35 @@ bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATE cemuLog_log(LogType::Force, "GameMode has been started."); } } +#endif + + // Workaround for BOTW + Mesa. Runes like Magnesis and the camera cause GPU crashes. + // Using the LLVM shader compiler prevents crashes which points to an issue with the default ACO compiler. + // Either that or Cemu is violating a specification (GLSL?) causing the shaders to be broken after compilation. +#if BOOST_OS_LINUX + uint64 currentTitleId = CafeSystem::GetForegroundTitleId(); + if (currentTitleId == 0x00050000101c9500 || currentTitleId == 0x00050000101c9400 || currentTitleId == 0x00050000101c9300) + { + // if the variable is empty set it to llvm, otherwise check if it contains llvm/aco and if not append it + if (const char* value; (value = getenv("RADV_DEBUG")) != NULL && strlen(value) != 0) + { + std::string valueStr{value}; + // only append ,llvm when llvm or aco are not already passed as flags. + // "aco" is not a mesa flag (anymore, it used to be when llvm was default) + // but it will provide users with a way to override this workaround by setting RADV_DEBUG=aco + // should parse the flag list but there are currently no other flags containing llvm or aco as a substring + if (valueStr.find("llvm") == std::string::npos && valueStr.find("aco") == std::string::npos) + { + valueStr.append(",llvm"); + setenv("RADV_DEBUG", valueStr.c_str(), 1); + } + } + else + { + setenv("RADV_DEBUG", "llvm", 1); + } + } + #endif CreateCanvas(); From 1644fd2e096fa876637bb1af8b082a8e6fb46936 Mon Sep 17 00:00:00 2001 From: iPixzl <99772330+iPixzl@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:45:27 +0100 Subject: [PATCH 21/24] GamePatch: Add additional FFL endian-swap patch signature for Mario Kart 8 (#1867) --- src/Cafe/GamePatch.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Cafe/GamePatch.cpp b/src/Cafe/GamePatch.cpp index 3f90e94a..f197d011 100644 --- a/src/Cafe/GamePatch.cpp +++ b/src/Cafe/GamePatch.cpp @@ -245,6 +245,10 @@ uint8 botw_crashFuncMask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF uint8 ffl_floatArrayEndianSwap[] = { 0x7C,0x08,0x02,0xA6,0x94,0x21,0xFF,0xE8,0x93,0xC1,0x00,0x10,0x7C,0x7E,0x1B,0x78,0x93,0xE1,0x00,0x14,0x93,0x81,0x00,0x08,0x7C,0x9F,0x23,0x78,0x93,0xA1,0x00,0x0C,0x90,0x01,0x00,0x1C,0x3B,0xA0,0x00,0x00,0x7C,0x1D,0xF8,0x40,0x40,0x80,0x00,0x20,0x57,0xBC,0x10,0x3A,0x7C,0x3E,0xE4,0x2E }; +// Alternative FFL endian swap signature (Mario Kart 8 and possibly other titles) +// Same algorithm as above but with a different compiler-scheduled prologue ordering +uint8 ffl_floatArrayEndianSwap_v2[] = { 0x7C,0x08,0x02,0xA6,0x94,0x21,0xFF,0xE8,0x93,0xE1,0x00,0x14,0x93,0xA1,0x00,0x0C,0x7C,0x9F,0x23,0x78,0x93,0x81,0x00,0x08,0x93,0xC1,0x00,0x10,0x90,0x01,0x00,0x1C,0x3B,0xA0,0x00,0x00,0x7C,0x1D,0xF8,0x40,0x7C,0x7E,0x1B,0x78,0x40,0x80,0x00,0x20,0x57,0xBC,0x10,0x3A,0x7C,0x3E,0xE4,0x2E }; + uint8 xcx_enterCriticalSectionSignature[] = { 0x94,0x21,0xFF,0xE0,0xBF,0x41,0x00,0x08,0x7C,0x08,0x02,0xA6,0x90,0x01,0x00,0x24,0x7C,0x7E,0x1B,0x78,0x80,0x1E,0x00,0x08,0x2C,0x00,0x00,0x00,0x41,0x82,0x00,0xC0,0x48,0x01,0xD7,0xA1,0x7C,0x7A,0x1B,0x79,0x41,0x82,0x00,0xB4,0x81,0x3E,0x00,0x04,0x7C,0x09,0xD0,0x40,0x40,0x82,0x00,0x2C,0x7D,0x20,0xF0,0x28,0x7C,0x00,0xF0,0x6C }; uint8 xcx_enterCriticalSectionMask[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }; @@ -361,11 +365,23 @@ void GamePatch_scan() hleAddr = hle_locate(ffl_floatArrayEndianSwap, NULL, sizeof(ffl_floatArrayEndianSwap)); if (hleAddr) { - cemuLog_logDebug(LogType::Force, "HLE: Hook FFL float array endian swap function at 0x{:08x}", hleAddr); + cemuLog_logDebug(LogType::Force, "HLE: Hook FFL float array endian swap function (v1) at 0x{:08x}", hleAddr); sint32 functionIndex = hleIndex_h000000003; uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex memory_write(hleAddr, opcode); } + else + { + // Try v2 signature (Mario Kart 8 and possibly other titles with reordered prologue) + hleAddr = hle_locate(ffl_floatArrayEndianSwap_v2, NULL, sizeof(ffl_floatArrayEndianSwap_v2)); + if (hleAddr) + { + cemuLog_logDebug(LogType::Force, "HLE: Hook FFL float array endian swap function (v2) at 0x{:08x}", hleAddr); + sint32 functionIndex = hleIndex_h000000003; + uint32 opcode = (1 << 26) | (functionIndex); // opcode for HLE: 0x1000 + FunctionIndex + memory_write(hleAddr, opcode); + } + } // XCX freeze workaround //hleAddr = hle_locate(xcx_enterCriticalSectionSignature, xcx_enterCriticalSectionMask, sizeof(xcx_enterCriticalSectionSignature)); From 391478b307a22f5e337cceb5a6eac5b71e8d7558 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Fri, 10 Apr 2026 22:46:51 +0200 Subject: [PATCH 22/24] vulkan: Improve the Breath of the Wild RADV/LLVM workaround (#1862) --- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 137 ++++++++++++++++++ src/gui/wxgui/MainWindow.cpp | 29 ---- src/main.cpp | 6 + 3 files changed, 143 insertions(+), 29 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 18fd1000..8d149288 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -336,6 +336,134 @@ void VulkanRenderer::GetDeviceFeatures() cemuLog_log(LogType::Force, fmt::format("VulkanLimits: UBAlignment {0} nonCoherentAtomSize {1}", prop2.properties.limits.minUniformBufferOffsetAlignment, prop2.properties.limits.nonCoherentAtomSize)); } +#if BOOST_OS_LINUX +#include +#include "resource/IconsFontAwesome5.h" + +int BreathOfTheWildChildProcessMain() +{ + InitializeGlobalVulkan(); + struct sigaction sa{.sa_handler = [](int unused){_exit(1);}}; + int ret = sigaction(SIGABRT, &sa, nullptr); + + freopen("/dev/null", "w", stderr); + + setenv("RADV_DEBUG", "llvm", 1); + + VkInstanceCreateInfo create_info{}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + VkInstance instance = VK_NULL_HANDLE; + if (vkCreateInstance(&create_info, nullptr, &instance) != VK_SUCCESS) + return 1; + InitializeInstanceVulkan(instance); + + // this function will abort() when LLVM is absent + uint32_t count = 0; + vkEnumeratePhysicalDevices(instance, &count, nullptr); + + vkDestroyInstance(instance, nullptr); + return 0; +} + +static void LinuxBreathOfTheWildWorkaround(VkInstance& instance, const VkInstanceCreateInfo* create_info) +{ + + // if the user specified either shader backend, do nothing. + // should parse the flag list but there are currently no other flags containing llvm or aco as a substring + const char* debugEnvC = getenv("RADV_DEBUG"); + std::string_view debugEnv = debugEnvC != nullptr ? debugEnvC : ""; + if (debugEnv.find("aco") != std::string_view::npos || debugEnv.find("llvm") != std::string_view::npos) + return; + + uint32_t count = 0; + vkEnumeratePhysicalDevices(instance, &count, nullptr); + + std::vector physicalDevices{count}; + vkEnumeratePhysicalDevices(instance, &count, physicalDevices.data()); + + // Find the first AMD device using a RADV driver and store its version + int version = 0; + for (auto& i : physicalDevices) + { + VkPhysicalDeviceDriverProperties driverProps{}; + driverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + VkPhysicalDeviceProperties2 prop{}; + prop.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + prop.pNext = &driverProps; + vkGetPhysicalDeviceProperties2(i, &prop); + if (prop.properties.vendorID != 0x1002 || driverProps.driverID != VK_DRIVER_ID_MESA_RADV) + continue; + + version = prop.properties.driverVersion; + break; + } + + if (version == 0) + return; + + + int major = VK_API_VERSION_MAJOR(version); + int minor = VK_API_VERSION_MINOR(version); + int patch = VK_API_VERSION_PATCH(version); + + // If the driver is unaffected skip the workaround. + // affected drivers: + // 25.3.0 - 26.0.4 + if ((major <= 25 && minor < 3) || (major == 26 && (minor > 0 || patch >= 5)) || major > 26) + return; + + // check if running with LLVM would crash because mesa is LLVM-less. + int childID = fork(); + if (childID == 0) // inside this if statement runs in child + { + setenv("CEMU_DETECT_RADV","1", 1); + execl("/proc/self/exe", "/proc/self/exe", nullptr); + _exit(2); // exec failed so err on the safe side and signal failure + } + + int childStatus = 0; + waitpid(childID, &childStatus, 0); + + // if the process didn't exit cleanly or failed to determine LLVM status + if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) == 2) + { + cemuLog_log(LogType::Force, "BOTW/RADV workaround not applied because detecting LLVM presence failed unexpectedly"); + return; + } + + if (WEXITSTATUS(childStatus) == 1) + cemuLog_log(LogType::Force, "BOTW/RADV workaround not applied because mesa was built without LLVM"); + + // only continue if the process exits with code zero, which means it didn't crash + if (WEXITSTATUS(childStatus) != 0) + return; + + cemuLog_log(LogType::Force, "BOTW/RADV workaround active. Adding \"llvm\" to RADV_DEBUG environment variable"); + if (debugEnv.empty()) + { + setenv("RADV_DEBUG", "llvm", 1); + } + else + { + std::string appendedDebugEnv{debugEnv}; + appendedDebugEnv.append(",llvm"); + setenv("RADV_DEBUG", appendedDebugEnv.c_str(), 1); + } + + // recreate the vulkan instance to update debug setting + vkDestroyInstance(instance, nullptr); + VkResult err = vkCreateInstance(create_info, nullptr, &instance); + // re-check for errors just in case. + if (err != VK_SUCCESS) + throw std::runtime_error(fmt::format("Unable to re-create a Vulkan instance after RADV/LLVM workaround: {}", err)); + InitializeInstanceVulkan(instance); + + LatteOverlay_pushNotification(std::string{(const char*)ICON_FA_EXCLAMATION_TRIANGLE} + "RADV_DEBUG=llvm set automatically to avoid crashing due to a driver bug. If possible update mesa to 26.0.5 or newer", 10'000); + +} + +#endif + VulkanRenderer::VulkanRenderer() { glslang::InitializeProcess(); @@ -395,6 +523,15 @@ VulkanRenderer::VulkanRenderer() if (!InitializeInstanceVulkan(m_instance)) throw std::runtime_error("Unable to load instanced Vulkan functions"); + // Workaround for BOTW + RADV. Runes like Magnesis and the camera cause GPU crashes. +#if BOOST_OS_LINUX + uint64 currentTitleId = CafeSystem::GetForegroundTitleId(); + if (currentTitleId == 0x00050000101c9500 || currentTitleId == 0x00050000101c9400 || currentTitleId == 0x00050000101c9300) + { + LinuxBreathOfTheWildWorkaround(m_instance, &create_info); + } +#endif + uint32_t device_count = 0; vkEnumeratePhysicalDevices(m_instance, &device_count, nullptr); if (device_count == 0) diff --git a/src/gui/wxgui/MainWindow.cpp b/src/gui/wxgui/MainWindow.cpp index 5de19f69..4ef4fe08 100644 --- a/src/gui/wxgui/MainWindow.cpp +++ b/src/gui/wxgui/MainWindow.cpp @@ -621,35 +621,6 @@ bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATE cemuLog_log(LogType::Force, "GameMode has been started."); } } -#endif - - // Workaround for BOTW + Mesa. Runes like Magnesis and the camera cause GPU crashes. - // Using the LLVM shader compiler prevents crashes which points to an issue with the default ACO compiler. - // Either that or Cemu is violating a specification (GLSL?) causing the shaders to be broken after compilation. -#if BOOST_OS_LINUX - uint64 currentTitleId = CafeSystem::GetForegroundTitleId(); - if (currentTitleId == 0x00050000101c9500 || currentTitleId == 0x00050000101c9400 || currentTitleId == 0x00050000101c9300) - { - // if the variable is empty set it to llvm, otherwise check if it contains llvm/aco and if not append it - if (const char* value; (value = getenv("RADV_DEBUG")) != NULL && strlen(value) != 0) - { - std::string valueStr{value}; - // only append ,llvm when llvm or aco are not already passed as flags. - // "aco" is not a mesa flag (anymore, it used to be when llvm was default) - // but it will provide users with a way to override this workaround by setting RADV_DEBUG=aco - // should parse the flag list but there are currently no other flags containing llvm or aco as a substring - if (valueStr.find("llvm") == std::string::npos && valueStr.find("aco") == std::string::npos) - { - valueStr.append(",llvm"); - setenv("RADV_DEBUG", valueStr.c_str(), 1); - } - } - else - { - setenv("RADV_DEBUG", "llvm", 1); - } - } - #endif CreateCanvas(); diff --git a/src/main.cpp b/src/main.cpp index df2b57fb..adce2680 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -255,8 +255,14 @@ int main(int argc, char* argv[]) #else +int BreathOfTheWildChildProcessMain(); int main(int argc, char *argv[]) { +#if BOOST_OS_LINUX + if (getenv("CEMU_DETECT_RADV") != nullptr) + return BreathOfTheWildChildProcessMain(); +#endif + #if BOOST_OS_LINUX || BOOST_OS_BSD XInitThreads(); #endif From 7e5516f94d9dc47db56cbbf24f788faf2c6a05eb Mon Sep 17 00:00:00 2001 From: Sapphire Date: Sat, 11 Apr 2026 22:16:07 -0500 Subject: [PATCH 23/24] Vulkan: Fix compile error on glibc 2.43 (#1870) --- src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 8d149288..d70cb065 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -343,7 +343,9 @@ void VulkanRenderer::GetDeviceFeatures() int BreathOfTheWildChildProcessMain() { InitializeGlobalVulkan(); - struct sigaction sa{.sa_handler = [](int unused){_exit(1);}}; + struct sigaction sa{}; + sa.sa_handler = [](int unused) { _exit(1); }; + int ret = sigaction(SIGABRT, &sa, nullptr); freopen("/dev/null", "w", stderr); From 9e28d6d3efb12017f46d63994a11a624697b3ab0 Mon Sep 17 00:00:00 2001 From: Emma Date: Mon, 13 Apr 2026 21:24:30 +0000 Subject: [PATCH 24/24] build.MD: Update macos build instructions (#1872) --- BUILD.md | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/BUILD.md b/BUILD.md index 0bedb8f0..d7b0de78 100644 --- a/BUILD.md +++ b/BUILD.md @@ -149,44 +149,31 @@ If you are getting a different error than any of the errors listed above, you ma To compile Cemu, a recent enough compiler and STL with C++20 support is required! LLVM 13 and below don't support the C++20 feature set required, so either install LLVM from Homebrew or make sure that -you have a recent enough version of Xcode. Xcode 15 is known to work. The OpenGL graphics API isn't -supported on macOS, so Vulkan must be used through the Molten-VK compatibility layer. +you have a recent enough version of Xcode. Xcode 15 is known to work. ### Installing brew 1. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` 2. Set up the Homebrew shell environment: - 1. **On an Intel Mac:** `eval "$(/usr/local/Homebrew/bin/brew shellenv)"` - 2. **On an Apple Silicon Mac:** eval `"$(/opt/homebrew/bin/brew shellenv)"` + - **On an Apple Silicon Mac:** eval `"$(/opt/homebrew/bin/brew shellenv)"` + - **On an Intel Mac:** `eval "$(/usr/local/Homebrew/bin/brew shellenv)"` -### Installing Tool Dependencies +### Dependencies -The native versions of these can be used regardless of what type of Mac you have. - -`brew install git cmake ninja nasm automake libtool` - -### Installing Library Dependencies - -**On Apple Silicon Macs, Rosetta 2 and the x86_64 version of Homebrew must be used to install these dependencies:** -1. `softwareupdate --install-rosetta` # Install Rosetta 2 if you don't have it. This only has to be done once -2. `arch -x86_64 zsh` # run an x64 shell -3. `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` -4. `eval "$(/usr/local/Homebrew/bin/brew shellenv)"` - -Then install the dependencies: - -`brew install boost molten-vk` +`brew install git cmake ninja nasm automake libtool boost molten-vk` ### Build Cemu using CMake 1. `git clone --recursive https://github.com/cemu-project/Cemu` 2. `cd Cemu` -3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DCMAKE_OSX_ARCHITECTURES=x86_64 -G Ninja` +3. `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -G Ninja` 4. `cmake --build build` 5. You should now have a Cemu executable file in the /bin folder, which you can run using `./bin/Cemu_release`. #### Troubleshooting steps -- If step 3 gives you an error about not being able to find ninja, try appending `-DCMAKE_MAKE_PROGRAM=/usr/local/bin/ninja` to the command and running it again. +- If step 3 gives you an error about not being able to find ninja, try appending the following to the command and try again: + - **On an Apple Silicon Mac:** `-DCMAKE_MAKE_PROGRAM=/opt/homebrew/bin/ninja` + - **On an Intel Mac:** `-DCMAKE_MAKE_PROGRAM=/usr/local/bin/ninja` ## FreeBSD