Merge branch 'main' into metal

This commit is contained in:
SamoZ256 2025-12-05 14:10:35 +01:00 committed by GitHub
commit bdecf3c00a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
99 changed files with 3886 additions and 2698 deletions

View file

@ -13,10 +13,22 @@ on:
env:
VCPKG_ROOT: "${{github.workspace}}/dependencies/vcpkg"
VCPKG_BINARY_SOURCES: 'clear;nuget,GitHub,readwrite'
VCPKG_FORCE_DOWNLOADED_BINARIES: true
jobs:
build-ubuntu:
runs-on: ubuntu-22.04
continue-on-error: true
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
arch: x64
- os: ubuntu-22.04-arm
arch: arm
name: build-ubuntu-${{ matrix.arch }}
runs-on: ${{ matrix.os }}
steps:
- name: "Checkout repo"
uses: actions/checkout@v4
@ -41,11 +53,6 @@ jobs:
sudo apt update -qq
sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libudev-dev nasm ninja-build libbluetooth-dev
- name: "Setup cmake"
uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: '3.29.0'
- name: "Bootstrap vcpkg"
run: |
bash ./dependencies/vcpkg/bootstrap-vcpkg.sh
@ -78,11 +85,21 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: cemu-bin-linux-x64
name: cemu-bin-linux-${{ matrix.arch }}
path: ./bin/Cemu
build-appimage:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
arch: x64
- os: ubuntu-22.04-arm
arch: arm
name: build-appimage-${{ matrix.arch }}
runs-on: ${{ matrix.os }}
needs: build-ubuntu
steps:
- name: Checkout Upstream Repo
@ -90,7 +107,7 @@ jobs:
- uses: actions/download-artifact@v4
with:
name: cemu-bin-linux-x64
name: cemu-bin-linux-${{ matrix.arch }}
path: bin
- name: "Install system dependencies"
@ -102,12 +119,12 @@ jobs:
run: |
export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"
export DEPLOY_GTK_VERSION=3
dist/linux/appimage.sh
dist/linux/appimage.sh ${{ runner.arch }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: cemu-appimage-x64
name: cemu-appimage-${{ matrix.arch }}
path: artifacts
build-windows:

View file

@ -5,6 +5,7 @@ project(wxwidgets-example)
add_executable(main WIN32 popup.cpp)
find_package(wxWidgets REQUIRED)
target_compile_features(main PRIVATE cxx_std_11)
target_compile_definitions(main PRIVATE ${wxWidgets_DEFINITIONS} "$<$<CONFIG:DEBUG>:${wxWidgets_DEFINITIONS_DEBUG}>")
target_include_directories(main PRIVATE ${wxWidgets_INCLUDE_DIRS})
target_link_libraries(main PRIVATE ${wxWidgets_LIBRARIES})
@ -13,6 +14,7 @@ add_executable(main2 WIN32 popup.cpp)
find_package(wxWidgets CONFIG REQUIRED)
target_link_libraries(main2 PRIVATE wx::core wx::base)
target_compile_features(main2 PRIVATE cxx_std_11)
option(USE_WXRC "Use the wxrc resource compiler" ON)
if(USE_WXRC)

View file

@ -1,25 +0,0 @@
diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp
index 2b9e0d7f7e..7eab8b37df 100644
--- a/src/msw/listctrl.cpp
+++ b/src/msw/listctrl.cpp
@@ -3110,6 +3110,11 @@ bool HandleSubItemPrepaint(wxListCtrl* listctrl, LPNMLVCUSTOMDRAW pLVCD, HFONT h
if ( it.iImage != -1 )
{
+ if ( !listctrl->HasCheckBoxes() )
+ {
+ rc.left -= 6;
+ }
+
const int yImage = rc.top + ((rc.bottom - rc.top) / 2 - hImage / 2);
ImageList_Draw(himl, it.iImage, hdc, rc.left, yImage,
nmcd.uItemState & CDIS_SELECTED ? ILD_SELECTED
@@ -3235,7 +3240,7 @@ void HandleItemPaint(wxListCtrl* listctrl, LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
// do not draw item background colour under the checkbox/image
RECT rcIcon;
wxGetListCtrlItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, LVIR_ICON, rcIcon);
- if ( !::IsRectEmpty(&rcIcon) )
+ if ( !::IsRectEmpty(&rcIcon) && listctrl->HasCheckBoxes() )
rc.left = rcIcon.right + listctrl->FromDIP(GAP_BETWEEN_CHECKBOX_AND_TEXT);
}

View file

@ -1,39 +1,45 @@
diff --git a/build/cmake/init.cmake b/build/cmake/init.cmake
index f044d22d4d..48fc5ad072 100644
index 4ed7b264ef..195d776aeb 100644
--- a/build/cmake/init.cmake
+++ b/build/cmake/init.cmake
@@ -198,7 +198,7 @@ if(WIN32)
endif()
endif()
-if(WIN32_MSVC_NAMING)
+if(0)
@@ -201,11 +201,11 @@ endif()
wx_get_install_dir(include)
if(WIN32_MSVC_NAMING)
if(wxBUILD_SHARED)
set(lib_suffix "_dll")
- set(lib_suffix "_dll")
+ # set(lib_suffix "_dll")
else()
- set(lib_suffix "_lib")
+ # set(lib_suffix "_lib")
endif()
- set(wxPLATFORM_LIB_DIR "${wxCOMPILER_PREFIX}${wxARCH_SUFFIX}${lib_suffix}")
+ # set(wxPLATFORM_LIB_DIR "${wxCOMPILER_PREFIX}${wxARCH_SUFFIX}${lib_suffix}")
set(wxINSTALL_INCLUDE_DIR "${include_dir}")
else()
wx_get_flavour(lib_flavour "-")
diff --git a/build/cmake/install.cmake b/build/cmake/install.cmake
index a373983043..2e1ace7bf9 100644
index cbbefdebe9..8f2759d5ad 100644
--- a/build/cmake/install.cmake
+++ b/build/cmake/install.cmake
@@ -63,7 +63,7 @@ else()
install(DIRECTORY DESTINATION "bin")
@@ -66,7 +66,7 @@ else()
wx_get_install_platform_dir(runtime)
install(DIRECTORY DESTINATION "${runtime_dir}")
install(CODE "execute_process( \
- COMMAND ${CMAKE_COMMAND} -E create_symlink \
+ COMMAND ${CMAKE_COMMAND} -E copy \
\"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/lib/wx/config/${wxBUILD_FILE_ID}\" \
\"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/wx-config\" \
\"${CMAKE_INSTALL_PREFIX}/${library_dir}/wx/config/${wxBUILD_FILE_ID}\" \
\"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${runtime_dir}/wx-config\" \
)"
diff --git a/build/cmake/utils/CMakeLists.txt b/build/cmake/utils/CMakeLists.txt
index 15f4339ef9..f93849e025 100644
index 608351ac42..d8110f9148 100644
--- a/build/cmake/utils/CMakeLists.txt
+++ b/build/cmake/utils/CMakeLists.txt
@@ -39,7 +39,7 @@ if(wxUSE_XRC)
@@ -40,7 +40,7 @@ if(wxUSE_XRC)
# Don't use wx_install() here to preserve escaping.
install(CODE "execute_process( \
- COMMAND ${CMAKE_COMMAND} -E create_symlink \
+ COMMAND ${CMAKE_COMMAND} -E copy \
\"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/${wxrc_output_name}${EXE_SUFFIX}\" \
\"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/wxrc${EXE_SUFFIX}\" \
\"${CMAKE_INSTALL_PREFIX}/${runtime_dir}/${wxrc_output_name}${EXE_SUFFIX}\" \
\"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${runtime_dir}/wxrc${EXE_SUFFIX}\" \
)"

View file

@ -1,13 +1,12 @@
diff --git a/build/cmake/wxWidgetsConfig.cmake.in b/build/cmake/wxWidgetsConfig.cmake.in
index b251109..60cf762 100644
index 2f3a735d92..c134900764 100644
--- a/build/cmake/wxWidgetsConfig.cmake.in
+++ b/build/cmake/wxWidgetsConfig.cmake.in
@@ -1,5 +1,8 @@
@PACKAGE_INIT@
@@ -155,6 +155,7 @@ foreach(libname @wxLIB_TARGETS@)
endforeach()
+include(CMakeFindDependencyMacro)
include(CMakeFindDependencyMacro)
+find_dependency(NanoSVG CONFIG)
+
cmake_policy(PUSH)
# Set policies to prevent warnings
if(POLICY CMP0057)
if(TARGET wx::wxnet AND @wxUSE_WEBREQUEST_CURL@)
# make sure CURL targets are available:

View file

@ -1,8 +1,8 @@
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO wxWidgets/wxWidgets
REF "v${VERSION}"
SHA512 8ad17582c4ba721ffe76ada4bb8bd7bc4b050491220aca335fd0506a51354fb789d5bc3d965f0f459dc81784d6427c88272e2acc2099cddf73730231b5a16f62
REF "bfd436b"
SHA512 bd3fd6d0d0db3b6fa34eceae1119e21ffd2f62221dcd249f8b8b82a6e65d83a05101e4e1e4ca9b9c4d7937add73b113bb029b03b05d2c3d87d17c1922d800a24
HEAD_REF master
PATCHES
install-layout.patch
@ -12,9 +12,26 @@ vcpkg_from_github(
fix-pcre2.patch
gtk3-link-libraries.patch
sdl2.patch
fix-dark-mode-offset.patch
)
# Submodule dependencies
vcpkg_from_github(
OUT_SOURCE_PATH lexilla_SOURCE_PATH
REPO wxWidgets/lexilla
REF "27c20a6ae5eebf418debeac0166052ed6fb653bc"
SHA512 7e5de7f664509473b691af8261fca34c2687772faca7260eeba5f2984516e6f8edf88c27192e056c9dda996e2ad2c20f6d1dff1c4bd2f3c0d74852cb50ca424a
HEAD_REF wx
)
file(COPY "${lexilla_SOURCE_PATH}/" DESTINATION "${SOURCE_PATH}/src/stc/lexilla")
vcpkg_from_github(
OUT_SOURCE_PATH scintilla_SOURCE_PATH
REPO wxWidgets/scintilla
REF "0b90f31ced23241054e8088abb50babe9a44ae67"
SHA512 db1f3007f4bd8860fad0817b6cf87980a4b713777025128cf5caea8d6d17b6fafe23fd22ff6886d7d5a420f241d85b7502b85d7e52b4ddb0774edc4b0a0203e7
HEAD_REF wx
)
file(COPY "${scintilla_SOURCE_PATH}/" DESTINATION "${SOURCE_PATH}/src/stc/scintilla")
vcpkg_check_features(
OUT_FEATURE_OPTIONS FEATURE_OPTIONS
FEATURES
@ -52,14 +69,20 @@ if(VCPKG_TARGET_IS_WINDOWS)
endif()
endif()
if("webview" IN_LIST FEATURES AND VCPKG_LIBRARY_LINKAGE STREQUAL "static")
list(APPEND OPTIONS -DwxUSE_WEBVIEW_EDGE_STATIC=ON)
endif()
vcpkg_find_acquire_program(PKGCONFIG)
# This may be set to ON by users in a custom triplet.
# The use of 'wxUSE_STL' and 'WXWIDGETS_USE_STD_CONTAINERS' (ON or OFF) are not API compatible
# which is why they must be set in a custom triplet rather than a port feature.
# The use of 'WXWIDGETS_USE_STD_CONTAINERS' (ON or OFF) is not API compatible
# which is why it must be set in a custom triplet rather than a port feature.
# For backwards compatibility, we also replace 'wxUSE_STL' (which no longer
# exists) with 'wxUSE_STD_STRING_CONV_IN_WXSTRING' which still exists and was
# set by `wxUSE_STL` previously.
if(NOT DEFINED WXWIDGETS_USE_STL)
set(WXWIDGETS_USE_STL OFF)
set(WXWIDGETS_USE_STD_STRING_CONV_IN_WXSTRING OFF)
endif()
if(NOT DEFINED WXWIDGETS_USE_STD_CONTAINERS)
@ -70,19 +93,18 @@ vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS
${FEATURE_OPTIONS}
-DwxUSE_STC=OFF
-DwxUSE_REGEX=sys
-DwxUSE_ZLIB=sys
-DwxUSE_EXPAT=sys
-DwxUSE_LIBJPEG=sys
-DwxUSE_LIBPNG=sys
-DwxUSE_LIBTIFF=sys
-DwxUSE_LIBWEBP=sys
-DwxUSE_NANOSVG=sys
-DwxUSE_LIBWEBP=sys
-DwxUSE_GLCANVAS=ON
-DwxUSE_LIBGNOMEVFS=OFF
-DwxUSE_LIBNOTIFY=OFF
-DwxUSE_STD_STRING_CONV_IN_WXSTRING=${WXWIDGETS_USE_STD_STRING_CONV_IN_WXSTRING}
-DwxUSE_STD_STRING_CONV_IN_WXSTRING=${WXWIDGETS_USE_STL}
-DwxUSE_STD_CONTAINERS=${WXWIDGETS_USE_STD_CONTAINERS}
-DwxUSE_UIACTIONSIMULATOR=OFF
-DCMAKE_DISABLE_FIND_PACKAGE_GSPELL=ON
@ -101,7 +123,7 @@ vcpkg_cmake_configure(
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/wxWidgets)
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/wxWidgets-3.3)
# The CMake export is not ready for use: It lacks a config file.
file(REMOVE_RECURSE
@ -126,6 +148,11 @@ vcpkg_copy_pdbs()
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/include/msvc")
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/mswu")
if(VCPKG_BUILD_TYPE STREQUAL "release")
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/lib/mswud")
endif()
file(GLOB_RECURSE INCLUDES "${CURRENT_PACKAGES_DIR}/include/*.h")
if(EXISTS "${CURRENT_PACKAGES_DIR}/lib/mswu/wx/setup.h")
list(APPEND INCLUDES "${CURRENT_PACKAGES_DIR}/lib/mswu/wx/setup.h")
@ -191,12 +218,30 @@ endif()
if("example" IN_LIST FEATURES)
file(INSTALL
"${CMAKE_CURRENT_LIST_DIR}/example/CMakeLists.txt"
"${SOURCE_PATH}/samples/popup/popup.cpp"
"${SOURCE_PATH}/samples/sample.xpm"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}/example"
"${CMAKE_CURRENT_LIST_DIR}/minimal_example/CMakeLists.txt"
"${SOURCE_PATH}/samples/minimal/minimal.cpp"
"${SOURCE_PATH}/samples/sample.xpm"
"${SOURCE_PATH}/samples/sample.rc"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}/minimal_example"
)
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/share/${PORT}/example/popup.cpp" "../sample.xpm" "sample.xpm")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/share/${PORT}/minimal_example/minimal.cpp" "../sample.xpm" "sample.xpm")
endif()
if("example" IN_LIST FEATURES)
file(INSTALL
"${CMAKE_CURRENT_LIST_DIR}/listctrl_example/CMakeLists.txt"
"${SOURCE_PATH}/samples/listctrl/listtest.cpp"
"${SOURCE_PATH}/samples/listctrl/listtest.h"
"${SOURCE_PATH}/samples/listctrl/listtest.rc"
"${SOURCE_PATH}/samples/sample.xpm"
"${SOURCE_PATH}/samples/sample.rc"
"${SOURCE_PATH}/samples/listctrl/bitmaps"
DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}/listctrl_example"
)
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/share/${PORT}/listctrl_example/listtest.cpp" "../sample.xpm" "sample.xpm")
vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/share/${PORT}/listctrl_example/listtest.rc" "../sample.rc" "sample.rc")
endif()
configure_file("${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-cmake-wrapper.cmake" @ONLY)

View file

@ -1,8 +1,8 @@
diff --git a/build/cmake/init.cmake b/build/cmake/init.cmake
index 5447d33..f5440b4 100644
index 4ed7b264ef..9abf7c9d5d 100644
--- a/build/cmake/init.cmake
+++ b/build/cmake/init.cmake
@@ -530,7 +530,9 @@ if(wxUSE_GUI)
@@ -667,7 +667,9 @@ if(wxUSE_GUI)
endif()
if(wxUSE_SOUND AND wxUSE_LIBSDL AND UNIX AND NOT APPLE)
@ -12,12 +12,12 @@ index 5447d33..f5440b4 100644
+ set(SDL2_LIBRARY SDL2::SDL2 CACHE INTERNAL "")
if(NOT SDL2_FOUND)
find_package(SDL)
endif()
mark_as_advanced(SDL_INCLUDE_DIR SDLMAIN_LIBRARY)
diff --git a/build/cmake/wxWidgetsConfig.cmake.in b/build/cmake/wxWidgetsConfig.cmake.in
index 60cf762..202a8c3 100644
index c134900764..7bcb691074 100644
--- a/build/cmake/wxWidgetsConfig.cmake.in
+++ b/build/cmake/wxWidgetsConfig.cmake.in
@@ -2,6 +2,9 @@
@@ -156,6 +156,9 @@ endforeach()
include(CMakeFindDependencyMacro)
find_dependency(NanoSVG CONFIG)
@ -25,5 +25,5 @@ index 60cf762..202a8c3 100644
+ find_dependency(SDL2 CONFIG)
+endif()
cmake_policy(PUSH)
# Set policies to prevent warnings
if(TARGET wx::wxnet AND @wxUSE_WEBREQUEST_CURL@)
# make sure CURL targets are available:

View file

@ -9,7 +9,7 @@ set(WX_ROOT_DIR "${_vcpkg_wx_root}" CACHE INTERNAL "")
unset(_vcpkg_wx_root)
if(WIN32)
# Find all libs with "32" infix which is unknown to FindwxWidgets.cmake
# Find all libs with "33" infix which is unknown to FindwxWidgets.cmake
function(z_vcpkg_wxwidgets_find_base_library BASENAME)
find_library(WX_${BASENAME}d wx${BASENAME}33ud NAMES wx${BASENAME}d PATHS "${wxWidgets_ROOT_DIR}/debug/lib" NO_DEFAULT_PATH)
find_library(WX_${BASENAME} wx${BASENAME}33u NAMES wx${BASENAME} PATHS "${wxWidgets_ROOT_DIR}/lib" NO_DEFAULT_PATH REQUIRED)

View file

@ -1,20 +1,22 @@
#!/bin/bash
if [ "$1" == 'X64' ]; then CPU_ARCH="x86_64"; else CPU_ARCH="aarch64"; fi
if [[ -z "${GITHUB_WORKSPACE}" ]]; then
export GITHUB_WORKSPACE="."
fi
curl -sSfLO "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
curl -sSfLO "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-"${CPU_ARCH}".AppImage"
chmod a+x linuxdeploy*.AppImage
curl -sSfL https://github.com"$(curl https://github.com/probonopd/go-appimage/releases/expanded_assets/continuous | grep "mkappimage-.*-x86_64.AppImage" | head -n 1 | cut -d '"' -f 2)" -o mkappimage.AppImage
curl -sSfL https://github.com"$(curl https://github.com/probonopd/go-appimage/releases/expanded_assets/continuous | grep "mkappimage-.*-"${CPU_ARCH}".AppImage" | head -n 1 | cut -d '"' -f 2)" -o mkappimage.AppImage
chmod a+x mkappimage.AppImage
curl -sSfLO "https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh"
chmod a+x linuxdeploy-plugin-gtk.sh
curl -sSfLO "https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh"
chmod a+x linuxdeploy-plugin-checkrt.sh
if [[ ! -e /usr/lib/x86_64-linux-gnu ]]; then
sed -i 's#lib\/x86_64-linux-gnu#lib64#g' linuxdeploy-plugin-gtk.sh
if [[ ! -e /usr/lib/"${CPU_ARCH}"-linux-gnu ]]; then
sed -i 's#lib\/"${CPU_ARCH}"-linux-gnu#lib64#g' linuxdeploy-plugin-gtk.sh
fi
mkdir -p AppDir/usr/bin
@ -32,11 +34,11 @@ cp -r bin/* AppDir/usr/share/Cemu
mv AppDir/usr/share/Cemu/Cemu AppDir/usr/bin/
chmod +x AppDir/usr/bin/Cemu
cp /usr/lib/x86_64-linux-gnu/{libsepol.so.1,libffi.so.7,libpcre.so.3,libGLU.so.1,libthai.so.0} AppDir/usr/lib
cp /usr/lib/"${CPU_ARCH}"-linux-gnu/{libsepol.so.1,libffi.so.7,libpcre.so.3,libGLU.so.1,libthai.so.0} AppDir/usr/lib
export UPD_INFO="gh-releases-zsync|cemu-project|Cemu|ci|Cemu.AppImage.zsync"
export NO_STRIP=1
./linuxdeploy-x86_64.AppImage --appimage-extract-and-run \
./linuxdeploy-"${CPU_ARCH}".AppImage --appimage-extract-and-run \
--appdir="${GITHUB_WORKSPACE}"/AppDir/ \
-d "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.desktop \
-i "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.png \
@ -54,4 +56,4 @@ echo -e "export LC_ALL=C\nexport FONTCONFIG_PATH=/etc/fonts" >> AppDir/apprun-ho
VERSION="${GITVERSION}" ./mkappimage.AppImage --appimage-extract-and-run "${GITHUB_WORKSPACE}"/AppDir
mkdir -p "${GITHUB_WORKSPACE}"/artifacts/
mv Cemu-"${GITVERSION}"-x86_64.AppImage "${GITHUB_WORKSPACE}"/artifacts/
mv Cemu-"${GITVERSION}"-"${CPU_ARCH}".AppImage "${GITHUB_WORKSPACE}"/artifacts/

View file

@ -138,7 +138,6 @@ target_link_libraries(CemuBin PRIVATE
CemuGui
CemuInput
CemuUtil
OpenGL::GL
SDL2::SDL2
)

View file

@ -238,8 +238,6 @@ add_library(CemuCafe
IOSU/legacy/iosu_acp.h
IOSU/legacy/iosu_act.cpp
IOSU/legacy/iosu_act.h
IOSU/legacy/iosu_boss.cpp
IOSU/legacy/iosu_boss.h
IOSU/legacy/iosu_crypto.cpp
IOSU/legacy/iosu_crypto.h
IOSU/legacy/iosu_fpd.cpp
@ -252,6 +250,10 @@ add_library(CemuCafe
IOSU/legacy/iosu_nim.h
IOSU/nn/iosu_nn_service.cpp
IOSU/nn/iosu_nn_service.h
IOSU/nn/boss/boss_common.cpp
IOSU/nn/boss/boss_common.h
IOSU/nn/boss/boss_service.cpp
IOSU/nn/boss/boss_service.h
IOSU/PDM/iosu_pdm.cpp
IOSU/PDM/iosu_pdm.h
IOSU/ODM/iosu_odm.cpp
@ -418,6 +420,7 @@ add_library(CemuCafe
OS/libs/nn_ccr/nn_ccr.h
OS/libs/nn_cmpt/nn_cmpt.cpp
OS/libs/nn_cmpt/nn_cmpt.h
OS/libs/nn_client_service.h
OS/libs/nn_common.h
OS/libs/nn_ec/nn_ec.cpp
OS/libs/nn_ec/nn_ec.h
@ -618,28 +621,17 @@ else()
endif()
target_link_libraries(CemuCafe PRIVATE
CemuAudio
CemuCommon
CemuComponents
CemuConfig
CemuGui
CemuInput
CemuResource
CemuUtil
imguiImpl
Boost::headers
Boost::nowide
CURL::libcurl
fmt::fmt
${glslang_target}
ih264d
OpenSSL::Crypto
OpenSSL::SSL
PNG::PNG
pugixml::pugixml
ZArchive::zarchive
ZLIB::ZLIB
zstd::zstd
CemuGui
ZArchive::zarchive
imguiImpl
pugixml::pugixml
ZLIB::ZLIB
CURL::libcurl
ih264d
${glslang_target}
PUBLIC
OpenSSL::SSL
)
if (ENABLE_WAYLAND)

View file

@ -33,10 +33,10 @@
#include "Cafe/IOSU/legacy/iosu_crypto.h"
#include "Cafe/IOSU/legacy/iosu_mcp.h"
#include "Cafe/IOSU/legacy/iosu_acp.h"
#include "Cafe/IOSU/legacy/iosu_boss.h"
#include "Cafe/IOSU/legacy/iosu_nim.h"
#include "Cafe/IOSU/PDM/iosu_pdm.h"
#include "Cafe/IOSU/ccr_nfc/iosu_ccr_nfc.h"
#include "Cafe/IOSU/nn/boss/boss_service.h"
// IOSU initializer functions
#include "Cafe/IOSU/kernel/iosu_kernel.h"
@ -561,6 +561,7 @@ namespace CafeSystem
iosu::fpd::GetModule(),
iosu::pdm::GetModule(),
iosu::ccr_nfc::GetModule(),
iosu::boss::GetModule()
};
// initialize all subsystems which are persistent and don't depend on a game running
@ -599,7 +600,6 @@ namespace CafeSystem
iosu::iosuMcp_init();
iosu::mcp::Init();
iosu::iosuAcp_init();
iosu::boss_init();
iosu::nim::Initialize();
iosu::odm::Initialize();
// init Cafe OS

View file

@ -27,11 +27,6 @@ struct LatteGPUState_t
uint32 contextControl1;
// optional features
bool allowFramebufferSizeOptimization{false}; // allow using scissor box as size hint to determine non-padded rendertarget size
// draw context
struct
{
uint32 numInstances;
}drawContext;
// stats
uint32 frameCounter;
uint32 flipCounter; // increased by one everytime a vsync + flip happens
@ -54,6 +49,8 @@ struct LatteGPUState_t
// temporary (replace with proper solution later)
bool tvBufferUsesSRGB;
bool drcBufferUsesSRGB;
float tvGamma = 0.0f;
float drcGamma = 0.0f;
// draw state
bool activeShaderHasError; // if try, at least one currently bound shader stage has an error and cannot be used for drawing
bool repeatTextureInitialization; // if set during rendertarget or texture initialization, repeat the process (textures likely have been invalidated)

View file

@ -62,16 +62,7 @@ public:
{
uint32 baseVertex = LatteGPUState.contextRegister[mmSQ_VTX_BASE_VTX_LOC];
uint32 baseInstance = LatteGPUState.contextRegister[mmSQ_VTX_START_INST_LOC];
uint32 numInstances = LatteGPUState.drawContext.numInstances;
if (numInstances == 0)
return;
/*
if (GetAsyncKeyState('B'))
{
cemuLog_force("[executeDraw] {} Count {} BaseVertex {} BaseInstance {}", m_isFirstDraw?"Init":"Fast", count, baseVertex, baseInstance);
}
*/
uint32 numInstances = LatteGPUState.contextNew.VGT_DMA_NUM_INSTANCES.get_NUM_INSTANCES();
if (!isAutoIndex)
{
@ -391,7 +382,10 @@ LatteCMDPtr LatteCP_itIndexType(LatteCMDPtr cmd, uint32 nWords)
LatteCMDPtr LatteCP_itNumInstances(LatteCMDPtr cmd, uint32 nWords)
{
cemu_assert_debug(nWords == 1);
LatteGPUState.drawContext.numInstances = LatteReadCMD();
uint32 numInstances = LatteReadCMD();
if (numInstances == 0)
numInstances = 1;
LatteGPUState.contextNew.VGT_DMA_NUM_INSTANCES.set_NUM_INSTANCES(numInstances);
return cmd;
}
@ -690,9 +684,6 @@ LatteCMDPtr LatteCP_itDrawIndexAuto(LatteCMDPtr cmd, uint32 nWords, DrawPassCont
cemu_assert_debug(nWords == 2);
uint32 count = LatteReadCMD();
uint32 ukn = LatteReadCMD();
if (LatteGPUState.drawContext.numInstances == 0)
return cmd;
LatteGPUState.currentDrawCallTick = GetTickCount();
// todo - better way to identify compute drawcalls
if ((LatteGPUState.contextRegister[mmSQ_CONFIG] >> 24) == 0xE4)
@ -750,13 +741,7 @@ LatteCMDPtr LatteCP_itDrawImmediate(LatteCMDPtr cmd, uint32 nWords, DrawPassCont
cemuLog_log(LogType::Force, "itDrawImmediate - Unsupported index type");
return cmd;
}
// verify packet size
if (nWords != (2 + numIndexU32s))
debugBreakpoint();
uint32 baseVertex = LatteGPUState.contextRegister[mmSQ_VTX_BASE_VTX_LOC];
uint32 baseInstance = LatteGPUState.contextRegister[mmSQ_VTX_START_INST_LOC];
uint32 numInstances = LatteGPUState.drawContext.numInstances;
cemu_assert_debug(nWords == (2 + numIndexU32s)); // verify packet size
drawPassCtx.executeDraw(count, false, _tempIndexArrayMPTR);
return cmd;

View file

@ -120,16 +120,6 @@ uint8* LatteTextureLoader_getInputLinearOptimized(LatteTextureLoaderCtx* texture
#define LatteTextureLoader_getInputLinearOptimized_(__textureLoader,__x,__y,__stepX,__stepY,__bpp,__sliceIndex,__numSlices,__sample,__pitch,__height) (textureLoader->inputData+((__x/__stepX) + __pitch * (__y/__stepY) + (__sliceIndex + __numSlices * __sample) * __height * __pitch)*(__bpp/8))
float SRGB_to_RGB(float cs)
{
float cl;
if (cs <= 0.04045f)
cl = cs / 12.92f;
else
cl = powf(((cs + 0.055f) / 1.055f), 2.4f);
return cl;
}
void decodeBC1Block(uint8* inputData, float* output4x4RGBA)
{
// read colors

View file

@ -30,6 +30,7 @@ void Latte_LoadInitialRegisters()
{
LatteGPUState.contextNew.CB_TARGET_MASK.set_MASK(0xFFFFFFFF);
LatteGPUState.contextNew.VGT_MULTI_PRIM_IB_RESET_INDX.set_RESTART_INDEX(0xFFFFFFFF);
LatteGPUState.contextNew.VGT_DMA_NUM_INSTANCES.set_NUM_INSTANCES(1);
LatteGPUState.contextRegister[Latte::REGADDR::PA_CL_CLIP_CNTL] = 0;
*(float*)&LatteGPUState.contextRegister[mmDB_DEPTH_CLEAR] = 1.0f;
}

View file

@ -445,6 +445,7 @@ namespace Latte
VGT_DMA_INDEX_TYPE = 0xA29F, // todo - verify offset
VGT_PRIMITIVEID_EN = 0xA2A1,
VGT_DMA_NUM_INSTANCES = 0xA2A2,
VGT_MULTI_PRIM_IB_RESET_EN = 0xA2A5,
@ -977,6 +978,11 @@ float get_##__regname() const \
LATTE_BITFIELD_BOOL(PRIMITIVEID_EN, 0);
};
struct LATTE_VGT_DMA_NUM_INSTANCES : LATTEREG // 0xA2A2
{
LATTE_BITFIELD_FULL_TYPED(NUM_INSTANCES, uint32);
};
struct LATTE_VGT_MULTI_PRIM_IB_RESET_EN : LATTEREG // 0xA2A5
{
LATTE_BITFIELD_BOOL(RESET_EN, 0);
@ -1541,7 +1547,7 @@ struct LatteContextRegister
/* +0x28A7C */ Latte::LATTE_VGT_DMA_INDEX_TYPE VGT_DMA_INDEX_TYPE;
/* +0x28A80 */ uint32 ukn28A80;
/* +0x28A84 */ Latte::LATTE_VGT_PRIMITIVEID_EN VGT_PRIMITIVEID_EN;
/* +0x28A88 */ uint32 ukn28A88;
/* +0x28A88 */ Latte::LATTE_VGT_DMA_NUM_INSTANCES VGT_DMA_NUM_INSTANCES;
/* +0x28A8C */ uint32 ukn28A8C;
/* +0x28A90 */ uint32 ukn28A90;
/* +0x28A94 */ Latte::LATTE_VGT_MULTI_PRIM_IB_RESET_EN VGT_MULTI_PRIM_IB_RESET_EN;
@ -1629,6 +1635,7 @@ static_assert(offsetof(LatteContextRegister, PA_SC_GENERIC_SCISSOR_TL) == Latte:
static_assert(offsetof(LatteContextRegister, PA_SC_GENERIC_SCISSOR_BR) == Latte::REGADDR::PA_SC_GENERIC_SCISSOR_BR * 4);
static_assert(offsetof(LatteContextRegister, VGT_MULTI_PRIM_IB_RESET_INDX) == Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_INDX * 4);
static_assert(offsetof(LatteContextRegister, VGT_PRIMITIVEID_EN) == Latte::REGADDR::VGT_PRIMITIVEID_EN * 4);
static_assert(offsetof(LatteContextRegister, VGT_DMA_NUM_INSTANCES) == Latte::REGADDR::VGT_DMA_NUM_INSTANCES * 4);
static_assert(offsetof(LatteContextRegister, VGT_MULTI_PRIM_IB_RESET_EN) == Latte::REGADDR::VGT_MULTI_PRIM_IB_RESET_EN * 4);
static_assert(offsetof(LatteContextRegister, VGT_INSTANCE_STEP_RATE_0) == Latte::REGADDR::VGT_INSTANCE_STEP_RATE_0 * 4);
static_assert(offsetof(LatteContextRegister, VGT_INSTANCE_STEP_RATE_1) == Latte::REGADDR::VGT_INSTANCE_STEP_RATE_1 * 4);

View file

@ -603,7 +603,7 @@ void OpenGLRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
shader_unbind(RendererShader::ShaderType::kGeometry);
shader_bind(shader->GetVertexShader());
shader_bind(shader->GetFragmentShader());
shader->SetUniformParameters(*texView, {imageWidth, imageHeight});
shader->SetUniformParameters(*texView, {imageWidth, imageHeight}, padView);
// set viewport
glViewportIndexedf(0, imageX, imageY, imageWidth, imageHeight);
@ -620,14 +620,12 @@ void OpenGLRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, useLinearTexFilter ? GL_LINEAR : GL_NEAREST);
texViewGL->samplerState.filterMag = 0xFFFFFFFF;
if ((!padView && !LatteGPUState.tvBufferUsesSRGB) || (padView && !LatteGPUState.drcBufferUsesSRGB))
glDisable(GL_FRAMEBUFFER_SRGB);
glDisable(GL_FRAMEBUFFER_SRGB);
uint16 indexData[6] = { 0,1,2,3,4,5 };
glDrawRangeElements(GL_TRIANGLES, 0, 5, 6, GL_UNSIGNED_SHORT, indexData);
if ((!padView && !LatteGPUState.tvBufferUsesSRGB) || (padView && !LatteGPUState.drcBufferUsesSRGB))
glEnable(GL_FRAMEBUFFER_SRGB);
glEnable(GL_FRAMEBUFFER_SRGB);
// unbind texture
texture_bindAndActivate(nullptr, 0);

View file

@ -227,6 +227,16 @@ sint32 RendererShaderGL::GetUniformLocation(const char* name)
return glGetUniformLocation(m_program, name);
}
void RendererShaderGL::SetUniform1i(sint32 location, sint32 value)
{
glProgramUniform1i(m_program, location, value);
}
void RendererShaderGL::SetUniform1f(sint32 location, float value)
{
glProgramUniform1f(m_program, location, value);
}
void RendererShaderGL::SetUniform2fv(sint32 location, void* data, sint32 count)
{
glProgramUniform2fv(m_program, location, count, (const GLfloat*)data);

View file

@ -18,6 +18,9 @@ public:
GLuint GetShaderObject() const { cemu_assert_debug(m_isCompiled); return m_shader_object; }
sint32 GetUniformLocation(const char* name) override;
void SetUniform1i(sint32 location, sint32 value) override;
void SetUniform1f(sint32 location, float value) override;
void SetUniform2fv(sint32 location, void* data, sint32 count) override;
void SetUniform4iv(sint32 location, void* data, sint32 count) override;

View file

@ -1,9 +1,10 @@
#include "Cafe/HW/Latte/Renderer/RendererOuputShader.h"
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h"
#include "config/ActiveSettings.h"
const std::string RendererOutputShader::s_copy_shader_source =
R"(
void main()
void outputShader()
{
colorOut0 = vec4(texture(textureSrc, passUV).rgb,1.0);
}
@ -62,7 +63,7 @@ vec4 bcFilter(vec2 uv, vec4 texelSize)
mix(sample1, sample0, sx), sy);
}
void main(){
void outputShader(){
vec4 texelSize = vec4( 1.0 / textureSrcResolution.xy, textureSrcResolution.xy);
colorOut0 = vec4(bcFilter(passUV, texelSize).rgb,1.0);
}
@ -172,7 +173,7 @@ vec3 BicubicHermiteTexture(vec2 uv, vec4 texelSize)
return CubicHermite(CP0X, CP1X, CP2X, CP3X, frac.y);
}
void main(){
void outputShader(){
vec4 texelSize = vec4( 1.0 / textureSrcResolution.xy, textureSrcResolution.xy);
colorOut0 = vec4(BicubicHermiteTexture(passUV, texelSize), 1.0);
}
@ -267,14 +268,20 @@ RendererOutputShader::RendererOutputShader(const std::string& vertex_source, con
m_uniformLocations[0].m_loc_textureSrcResolution = m_vertex_shader->GetUniformLocation("textureSrcResolution");
m_uniformLocations[0].m_loc_nativeResolution = m_vertex_shader->GetUniformLocation("nativeResolution");
m_uniformLocations[0].m_loc_outputResolution = m_vertex_shader->GetUniformLocation("outputResolution");
m_uniformLocations[0].m_loc_applySRGBEncoding = m_vertex_shader->GetUniformLocation("applySRGBEncoding");
m_uniformLocations[0].m_loc_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma");
m_uniformLocations[0].m_loc_displayGamma = m_fragment_shader->GetUniformLocation("displayGamma");
m_uniformLocations[1].m_loc_textureSrcResolution = m_fragment_shader->GetUniformLocation("textureSrcResolution");
m_uniformLocations[1].m_loc_nativeResolution = m_fragment_shader->GetUniformLocation("nativeResolution");
m_uniformLocations[1].m_loc_outputResolution = m_fragment_shader->GetUniformLocation("outputResolution");
m_uniformLocations[1].m_loc_applySRGBEncoding = m_fragment_shader->GetUniformLocation("applySRGBEncoding");
m_uniformLocations[1].m_loc_targetGamma = m_fragment_shader->GetUniformLocation("targetGamma");
m_uniformLocations[1].m_loc_displayGamma = m_fragment_shader->GetUniformLocation("displayGamma");
}
}
void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res) const
void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res, const bool padView) const
{
sint32 effectiveWidth, effectiveHeight;
texture_view.baseTexture->GetEffectiveSize(effectiveWidth, effectiveHeight, 0);
@ -300,6 +307,22 @@ void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_
res[1] = (float)output_res.y;
shader->SetUniform2fv(locations.m_loc_outputResolution, res, 1);
}
if (locations.m_loc_applySRGBEncoding != -1)
{
shader->SetUniform1i(locations.m_loc_applySRGBEncoding, padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB);
}
if (locations.m_loc_targetGamma != -1)
{
shader->SetUniform1f(locations.m_loc_targetGamma, padView ? ActiveSettings::GetDRCGamma() : ActiveSettings::GetTVGamma());
}
if (locations.m_loc_displayGamma != -1)
{
shader->SetUniform1f(locations.m_loc_displayGamma, GetConfig().userDisplayGamma);
}
};
setUniforms(m_vertex_shader.get(), m_uniformLocations[0]);
setUniforms(m_fragment_shader.get(), m_uniformLocations[1]);
@ -460,16 +483,52 @@ layout(push_constant) uniform pc {
vec2 textureSrcResolution;
vec2 nativeResolution;
vec2 outputResolution;
bool applySRGBEncoding; // true = app requested sRGB encoding
float targetGamma;
float displayGamma;
};
#else
uniform vec2 textureSrcResolution;
uniform vec2 nativeResolution;
uniform vec2 outputResolution;
uniform bool applySRGBEncoding;
uniform float targetGamma;
uniform float displayGamma;
#endif
layout(location = 0) smooth in vec2 passUV;
layout(binding = 0) uniform sampler2D textureSrc;
layout(location = 0) out vec4 colorOut0;
float sRGBEncode(float linear)
{
if(linear <= 0.0031308)
return 12.92f * linear;
else
return 1.055f * pow(linear, 1.0f / 2.4f) - 0.055f;
}
vec3 sRGBEncode(vec3 linear)
{
return vec3(sRGBEncode(linear.r), sRGBEncode(linear.g), sRGBEncode(linear.b));
}
// fwd. declaration
void outputShader();
void main()
{
outputShader(); // sets colorOut0
if(applySRGBEncoding)
colorOut0 = vec4(sRGBEncode(colorOut0.rgb), 1.0f);
if (displayGamma > 0.0f)
colorOut0 = pow(colorOut0, vec4(targetGamma / displayGamma) );
else
colorOut0 = vec4( sRGBEncode( pow(colorOut0.rgb, vec3(targetGamma)) ), 1.0f);
}
)" + shaderSrc;
}
void RendererOutputShader::InitializeStatic()

View file

@ -17,7 +17,7 @@ public:
RendererOutputShader(const std::string& vertex_source, const std::string& fragment_source);
virtual ~RendererOutputShader() = default;
void SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res) const;
void SetUniformParameters(const LatteTextureView& texture_view, const Vector2i& output_res, const bool padView) const;
RendererShader* GetVertexShader() const
{
@ -56,6 +56,9 @@ protected:
sint32 m_loc_textureSrcResolution = -1;
sint32 m_loc_nativeResolution = -1;
sint32 m_loc_outputResolution = -1;
sint32 m_loc_applySRGBEncoding = -1;
sint32 m_loc_targetGamma = -1;
sint32 m_loc_displayGamma = -1;
} m_uniformLocations[2]{};
private:

View file

@ -20,6 +20,8 @@ public:
virtual sint32 GetUniformLocation(const char* name) = 0;
virtual void SetUniform1i(sint32 location, sint32 value) = 0;
virtual void SetUniform1f(sint32 location, float value) = 0;
virtual void SetUniform2fv(sint32 location, void* data, sint32 count) = 0;
virtual void SetUniform4iv(sint32 location, void* data, sint32 count) = 0;

View file

@ -87,7 +87,7 @@ LatteTextureVk::LatteTextureVk(class VulkanRenderer* vkRenderer, Latte::E_DIM di
if (vkCreateImage(m_vkr->GetLogicalDevice(), &imageInfo, nullptr, &vkObjTex->m_image) != VK_SUCCESS)
m_vkr->UnrecoverableError("Failed to create texture image");
if (m_vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT)
if (m_vkr->IsDebugMarkersEnabled())
{
VkDebugUtilsObjectNameInfoEXT objName{};
objName.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;

View file

@ -232,6 +232,16 @@ sint32 RendererShaderVk::GetUniformLocation(const char* name)
return 0;
}
void RendererShaderVk::SetUniform1i(sint32 location, sint32 value)
{
cemu_assert_suspicious();
}
void RendererShaderVk::SetUniform1f(sint32 location, float value)
{
cemu_assert_suspicious();
}
void RendererShaderVk::SetUniform2fv(sint32 location, void* data, sint32 count)
{
cemu_assert_suspicious();
@ -261,7 +271,7 @@ void RendererShaderVk::CreateVkShaderModule(std::span<uint32> spirvBuffer)
}
// set debug name
if (vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT)
if (vkr->IsDebugMarkersEnabled())
{
VkDebugUtilsObjectNameInfoEXT objName{};
objName.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
@ -282,8 +292,10 @@ void RendererShaderVk::FinishCompilation()
void RendererShaderVk::CompileInternal(bool isRenderThread)
{
const bool compileWithDebugInfo = ((VulkanRenderer*)g_renderer.get())->IsTracingToolEnabled();
// try to retrieve SPIR-V module from cache
if (s_isLoadingShadersVk && (m_isGameShader && !m_isGfxPackShader) && s_spirvCache)
if (s_isLoadingShadersVk && (m_isGameShader && !m_isGfxPackShader) && s_spirvCache && !compileWithDebugInfo)
{
cemu_assert_debug(m_baseHash != 0);
uint64 h1, h2;
@ -319,21 +331,12 @@ void RendererShaderVk::CompileInternal(bool isRenderThread)
Shader.setStrings(&cstr, 1);
Shader.setEnvInput(glslang::EShSourceGlsl, state, glslang::EShClientVulkan, 100);
Shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetClientVersion::EShTargetVulkan_1_1);
Shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetLanguageVersion::EShTargetSpv_1_3);
TBuiltInResource Resources = GetDefaultBuiltInResource();
std::string PreprocessedGLSL;
VulkanRenderer* vkr = (VulkanRenderer*)g_renderer.get();
EShMessages messagesPreprocess;
if (vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT)
messagesPreprocess = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules | EShMsgDebugInfo);
else
messagesPreprocess = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
glslang::TShader::ForbidIncluder Includer;
TBuiltInResource Resources = GetDefaultBuiltInResource();
EShMessages messagesPreprocess = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
if (!Shader.preprocess(&Resources, 450, ENoProfile, false, false, messagesPreprocess, &PreprocessedGLSL, Includer))
{
cemuLog_log(LogType::Force, fmt::format("GLSL Preprocessing Failed For {:016x}_{:016x}: \"{}\"", m_baseHash, m_auxHash, Shader.getInfoLog()));
@ -341,14 +344,9 @@ void RendererShaderVk::CompileInternal(bool isRenderThread)
return;
}
EShMessages messagesParseLink;
if (vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT)
messagesParseLink = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules | EShMsgDebugInfo);
else
messagesParseLink = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
const char* PreprocessedCStr = PreprocessedGLSL.c_str();
Shader.setStrings(&PreprocessedCStr, 1);
EShMessages messagesParseLink = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
if (!Shader.parse(&Resources, 100, false, messagesParseLink))
{
cemuLog_log(LogType::Force, fmt::format("GLSL parsing failed for {:016x}_{:016x}: \"{}\"", m_baseHash, m_auxHash, Shader.getInfoLog()));
@ -382,9 +380,17 @@ void RendererShaderVk::CompileInternal(bool isRenderThread)
glslang::SpvOptions spvOptions;
spvOptions.disableOptimizer = false;
spvOptions.generateDebugInfo = (vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT);
spvOptions.validate = false;
spvOptions.optimizeSize = true;
if (compileWithDebugInfo)
{
spvOptions.generateDebugInfo = true;
spvOptions.emitNonSemanticShaderDebugInfo = true;
spvOptions.emitNonSemanticShaderDebugSource = true;
Shader.addSourceText(m_glslCode.c_str(), (uint32)m_glslCode.size());
Shader.setSourceFile(fmt::format("shader_{:016x}_{:016x}.glsl", m_baseHash, m_auxHash).c_str());
}
//auto beginTime = benchmarkTimer_start();
@ -393,7 +399,8 @@ void RendererShaderVk::CompileInternal(bool isRenderThread)
//double timeDur = benchmarkTimer_stop(beginTime);
//forceLogRemoveMe_printf("Shader GLSL-to-SPIRV compilation took %lfms Size %08x", timeDur, spirvBuffer.size()*4);
if (s_spirvCache && m_isGameShader && m_isGfxPackShader == false)
// store in cache, unless it got compiled with debug info or is a modified shader from a gfx pack
if (s_spirvCache && m_isGameShader && m_isGfxPackShader == false && !compileWithDebugInfo)
{
uint64 h1, h2;
GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2);

View file

@ -32,6 +32,8 @@ public:
static void Shutdown();
sint32 GetUniformLocation(const char* name) override;
void SetUniform1i(sint32 location, sint32 value) override;
void SetUniform1f(sint32 location, float value) override;
void SetUniform2fv(sint32 location, void* data, sint32 count) override;
void SetUniform4iv(sint32 location, void* data, sint32 count) override;
VkShaderModule& GetShaderModule() { return m_shader_module; }

View file

@ -319,18 +319,8 @@ VkSurfaceFormatKHR SwapchainInfoVk::ChooseSurfaceFormat(const std::vector<VkSurf
for (const auto& format : formats)
{
bool useSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB;
if (useSRGB)
{
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
return format;
}
else
{
if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
return format;
}
if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
return format;
}
return formats[0];

View file

@ -59,7 +59,6 @@ struct SwapchainInfoVk
bool mainWindow{};
bool m_shouldRecreate = false;
bool m_usesSRGB = false;
VSync m_vsyncState = VSync::Immediate;
bool hasDefinedSwapchainImage{}; // indicates if the swapchain image is in a defined state

View file

@ -446,8 +446,6 @@ VulkanRenderer::VulkanRenderer()
}
CheckDeviceExtensionSupport(m_physicalDevice, m_featureControl); // todo - merge this with GetDeviceFeatures and separate from IsDeviceSuitable?
if (m_featureControl.debugMarkersSupported)
cemuLog_log(LogType::Force, "Debug: Frame debugger attached, will use vkDebugMarkerSetObjectNameEXT");
DetermineVendor();
GetDeviceFeatures();
@ -582,10 +580,14 @@ VulkanRenderer::VulkanRenderer()
debugCallback.pfnUserCallback = &DebugUtilsCallback;
vkCreateDebugUtilsMessengerEXT(m_instance, &debugCallback, nullptr, &m_debugCallback);
cemuLog_log(LogType::Force, "Debug: Vulkan validation layer enabled, vkCreateDebugUtilsMessengerEXT will be used to log validation errors");
}
if (m_featureControl.instanceExtensions.debug_utils)
cemuLog_log(LogType::Force, "Using available debug function: vkCreateDebugUtilsMessengerEXT()");
if (this->IsTracingToolEnabled())
cemuLog_log(LogType::Force, "Debug: Tracing tool detected, will recompile all shaders with debug info enabled. This disables the SPIR-V cache.");
if (this->IsDebugMarkersEnabled())
cemuLog_log(LogType::Force, "Debug: Detected tool capable of using debug markers, will use vkDebugMarkerSetObjectNameEXT to identify Vulkan objects");
// set initial viewport and scissor box size
m_state.currentViewport.width = 4;
@ -612,6 +614,8 @@ VulkanRenderer::VulkanRenderer()
m_uniformVarBufferMemoryIsCoherent = true; // unified memory
else if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory))
m_uniformVarBufferMemoryIsCoherent = true;
else if (memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory))
m_uniformVarBufferMemoryIsCoherent = true;
else
{
memoryManager->CreateBuffer(UNIFORMVAR_RINGBUFFER_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, m_uniformVarBuffer, m_uniformVarBufferMemory);
@ -624,7 +628,10 @@ VulkanRenderer::VulkanRenderer()
m_uniformVarBufferPtr = (uint8*)bufferPtr;
// texture readback buffer
memoryManager->CreateBuffer(TEXTURE_READBACK_SIZE, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_textureReadbackBuffer, m_textureReadbackBufferMemory);
if (!memoryManager->CreateBuffer(TEXTURE_READBACK_SIZE, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_textureReadbackBuffer, m_textureReadbackBufferMemory))
{
memoryManager->CreateBuffer(TEXTURE_READBACK_SIZE, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_textureReadbackBuffer, m_textureReadbackBufferMemory);
}
bufferPtr = nullptr;
vkMapMemory(m_logicalDevice, m_textureReadbackBufferMemory, 0, VK_WHOLE_SIZE, 0, &bufferPtr);
m_textureReadbackBufferPtr = (uint8*)bufferPtr;
@ -633,7 +640,10 @@ VulkanRenderer::VulkanRenderer()
memoryManager->CreateBuffer(LatteStreamout_GetRingBufferSize(), VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | (m_featureControl.mode.useTFEmulationViaSSBO ? VK_BUFFER_USAGE_STORAGE_BUFFER_BIT : 0), 0, m_xfbRingBuffer, m_xfbRingBufferMemory);
// occlusion query result buffer
memoryManager->CreateBuffer(OCCLUSION_QUERY_POOL_SIZE * sizeof(uint64), VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults);
if (!memoryManager->CreateBuffer(OCCLUSION_QUERY_POOL_SIZE * sizeof(uint64), VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults))
{
memoryManager->CreateBuffer(OCCLUSION_QUERY_POOL_SIZE * sizeof(uint64), VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, m_occlusionQueries.bufferQueryResults, m_occlusionQueries.memoryQueryResults);
}
bufferPtr = nullptr;
vkMapMemory(m_logicalDevice, m_occlusionQueries.memoryQueryResults, 0, VK_WHOLE_SIZE, 0, &bufferPtr);
m_occlusionQueries.ptrQueryResults = (uint64*)bufferPtr;
@ -1256,8 +1266,9 @@ bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device,
// dynamic rendering doesn't provide any benefits for us right now. Driver implementations are very unoptimized as of Feb 2022
info.deviceExtensions.present_wait = isExtensionAvailable(VK_KHR_PRESENT_WAIT_EXTENSION_NAME) && isExtensionAvailable(VK_KHR_PRESENT_ID_EXTENSION_NAME);
// check for framedebuggers
info.debugMarkersSupported = false;
// check for validation layers and frame debuggers
info.usingDebugMarkerTool = false;
info.usingTracingTool = false;
if (info.deviceExtensions.tooling_info && vkGetPhysicalDeviceToolPropertiesEXT)
{
uint32_t toolCount = 0;
@ -1268,8 +1279,10 @@ bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device,
{
for (auto& itr : toolProperties)
{
if ((itr.purposes & VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT) != 0)
info.debugMarkersSupported = true;
if ((itr.purposes & VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT) != 0 && info.instanceExtensions.debug_utils && vkSetDebugUtilsObjectNameEXT)
info.usingDebugMarkerTool = true;
if ((itr.purposes & VK_TOOL_PURPOSE_TRACING_BIT) != 0)
info.usingTracingTool = true;
}
}
}
@ -2590,7 +2603,6 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet
uint64 hash = 0;
hash += (uint64)vertexRendererShader;
hash += (uint64)fragmentRendererShader;
hash += (uint64)(chainInfo.m_usesSRGB);
hash += ((uint64)padView) << 1;
const auto it = m_backbufferBlitPipelineCache.find(hash);
@ -2660,6 +2672,8 @@ VkPipeline VulkanRenderer::backbufferBlit_createGraphicsPipeline(VkDescriptorSet
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = 0,
.size = 3 * sizeof(float) * 2 // 3 vec2's
+ 4 // + 1 VkBool32
+ 4 * 2 // + 2 float
};
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
@ -2771,10 +2785,6 @@ bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow)
if(chainInfo.m_vsyncState != configValue)
stateChanged = true;
const bool latteBufferUsesSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB;
if (chainInfo.m_usesSRGB != latteBufferUsesSRGB)
stateChanged = true;
int width, height;
if (mainWindow)
WindowSystem::GetWindowPhysSize(width, height);
@ -2799,7 +2809,6 @@ bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow)
chainInfo.m_shouldRecreate = false;
chainInfo.m_vsyncState = configValue;
chainInfo.m_usesSRGB = latteBufferUsesSRGB;
return true;
}
@ -3046,24 +3055,35 @@ void VulkanRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutpu
vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &descriptSet, 0, nullptr);
// update push constants
Vector2f pushData[3];
struct
{
Vector2f vecs[3];
VkBool32 applySRGBEncoding;
float targetGamma;
float displayGamma;
} pushData;
// textureSrcResolution
sint32 effectiveWidth, effectiveHeight;
texView->baseTexture->GetEffectiveSize(effectiveWidth, effectiveHeight, 0);
pushData[0] = {(float)effectiveWidth, (float)effectiveHeight};
pushData.vecs[0] = {(float)effectiveWidth, (float)effectiveHeight};
// nativeResolution
pushData[1] = {
pushData.vecs[1] = {
(float)texViewVk->baseTexture->width,
(float)texViewVk->baseTexture->height,
};
// outputResolution
pushData[2] = {(float)imageWidth,(float)imageHeight};
pushData.vecs[2] = {(float)imageWidth,(float)imageHeight};
vkCmdPushConstants(m_state.currentCommandBuffer, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float) * 2 * 3, &pushData);
pushData.applySRGBEncoding = padView ? LatteGPUState.drcBufferUsesSRGB : LatteGPUState.tvBufferUsesSRGB;
pushData.targetGamma = padView ? ActiveSettings::GetDRCGamma() : ActiveSettings::GetTVGamma();
pushData.displayGamma = GetConfig().userDisplayGamma;
vkCmdPushConstants(m_state.currentCommandBuffer, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pushData), &pushData);
vkCmdDraw(m_state.currentCommandBuffer, 6, 1, 0, 0);

View file

@ -481,7 +481,8 @@ private:
uint32 nonCoherentAtomSize = 256;
}limits;
bool debugMarkersSupported{ false }; // frame debugger is attached
bool usingDebugMarkerTool{ false }; // validation layer or other tool capable of handling debug markers is used
bool usingTracingTool{ false }; // frame debugger or other API replaying tool is used
bool disableMultithreadedCompilation{ false }; // for old nvidia drivers
}m_featureControl{};
@ -937,7 +938,8 @@ private:
public:
bool GetDisableMultithreadedCompilation() const { return m_featureControl.disableMultithreadedCompilation; }
bool HasSPRIVRoundingModeRTE32() const { return m_featureControl.shaderFloatControls.shaderRoundingModeRTEFloat32; }
bool IsDebugUtilsEnabled() const { return m_featureControl.debugMarkersSupported && m_featureControl.instanceExtensions.debug_utils; }
bool IsDebugMarkersEnabled() const { return m_featureControl.usingDebugMarkerTool; }
bool IsTracingToolEnabled() const { return m_featureControl.usingTracingTool; }
private:

View file

@ -245,7 +245,7 @@ void compilePipelineThread_queue(PipelineCompiler* v)
bool VulkanRenderer::IsAsyncPipelineAllowed(uint32 numIndices)
{
// frame debuggers dont handle async well (as of 2020)
if (IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT)
if (IsTracingToolEnabled())
return false;
CachedFBOVk* currentFBO = m_state.activeFBO;

View file

@ -659,7 +659,10 @@ namespace iosu
for (uint32 i = 0; i < numIn + numOut; i++)
{
if (vec[i].baseVirt == nullptr && vec[i].size != 0)
{
cemuLog_logDebug(LogType::Force, "IPC Ioctlv failed because baseVirt is null but size is not 0");
return IOS_ERROR_INVALID;
}
// todo - check for valid pointer range
vec[i].basePhys = vec[i].baseVirt;
}

View file

@ -782,7 +782,7 @@ namespace iosu
public:
AcpMainService() : iosu::nn::IPCService("/dev/acp_main") {}
nnResult ServiceCall(uint32 serviceId, void* request, void* response) override
nnResult ServiceCall(IPCServiceCall& serviceCall) override
{
cemuLog_log(LogType::Force, "Unsupported service call to /dev/acp_main");
cemu_assert_unimplemented();

View file

@ -603,7 +603,7 @@ namespace iosu
public:
ActService() : iosu::nn::IPCService("/dev/act") {}
nnResult ServiceCall(uint32 serviceId, void* request, void* response) override
nnResult ServiceCall(IPCServiceCall& serviceCall) override
{
cemuLog_log(LogType::Force, "Unsupported service call to /dev/act");
cemu_assert_unimplemented();

File diff suppressed because it is too large Load diff

View file

@ -1,54 +0,0 @@
#pragma once
#include "iosu_ioctl.h"
// custom dev/boss protocol (Cemu only)
#define IOSU_BOSS_REQUEST_CEMU (0xEE)
typedef struct
{
uint32 requestCode;
// input
uint32 accountId;
char* taskId;
bool bool_parameter;
uint64 titleId;
uint32 timeout;
uint32 waitState;
uint32 uk1;
void* settings;
// output
uint32 returnCode; // ACP return value
union
{
struct
{
uint32 exec_count;
uint32 result;
} u32;
struct
{
uint32 exec_count;
uint64 result;
} u64;
};
}iosuBossCemuRequest_t;
#define IOSU_NN_BOSS_TASK_RUN (0x01)
#define IOSU_NN_BOSS_TASK_GET_CONTENT_LENGTH (0x02)
#define IOSU_NN_BOSS_TASK_GET_PROCESSED_LENGTH (0x03)
#define IOSU_NN_BOSS_TASK_GET_HTTP_STATUS_CODE (0x04)
#define IOSU_NN_BOSS_TASK_GET_TURN_STATE (0x05)
#define IOSU_NN_BOSS_TASK_WAIT (0x06)
#define IOSU_NN_BOSS_TASK_REGISTER (0x07)
#define IOSU_NN_BOSS_TASK_IS_REGISTERED (0x08)
#define IOSU_NN_BOSS_TASK_REGISTER_FOR_IMMEDIATE_RUN (0x09)
#define IOSU_NN_BOSS_TASK_UNREGISTER (0x0A)
#define IOSU_NN_BOSS_TASK_START_SCHEDULING (0x0B)
#define IOSU_NN_BOSS_TASK_STOP_SCHEDULING (0x0C)
namespace iosu
{
void boss_init();
}

View file

View file

@ -0,0 +1,431 @@
#pragma once
#include "Common/CafeString.h"
namespace nn::boss
{
typedef uint32 BossResult;
struct VTableEntry
{
uint16be offsetA{0};
uint16be offsetB{0};
MEMPTR<void> ptr;
};
static_assert(sizeof(VTableEntry) == 8);
struct TitleId
{
uint64be u64{};
static bool IsValid(TitleId* _thisptr);
static TitleId* ctorDefault(TitleId* _thisptr);
static TitleId* ctorFromTitleId(TitleId* _thisptr, uint64 titleId); // __ct__Q3_2nn4boss7TitleIDFUL
static TitleId* ctorCopy(TitleId* _thisptr, TitleId* titleId); // __ct__Q3_2nn4boss7TitleIDFRCQ3_2nn4boss7TitleID
static bool operator_ne(TitleId* _thisptr, TitleId* titleId);
};
static_assert(sizeof(TitleId) == 8);
struct TaskId
{
CafeString<8> id;
TaskId() = default;
TaskId(const char* taskId) { id.assign(taskId); }
static TaskId* ctorDefault(TaskId* _thisptr);
static TaskId* ctorFromString(TaskId* _thisptr, const char* taskId);
//auto operator<=>(const TaskId& other) const = default;
std::strong_ordering operator<=>(const TaskId& other) const noexcept {
return id <=> other.id; // Delegate to CafeString's operator<=>
}
};
static_assert(sizeof(TaskId) == 8);
struct Title
{
uint32be accountId{}; // 0x00
TitleId titleId{}; // 0x8
MEMPTR<void> vTablePtr{}; // 0x10
struct VTable
{
VTableEntry rtti;
VTableEntry dtor;
};
static inline SysAllocator<VTable> s_titleVTable;
static Title* ctor(Title* _this);
static void dtor(Title* _this, uint32 options);
static void InitVTable();
};
static_assert(sizeof(Title) == 0x18);
struct DirectoryName
{
CafeString<8> name2;
static DirectoryName* ctor(DirectoryName* _thisptr);
static const char* operator_const_char(DirectoryName* _thisptr);
};
static_assert(sizeof(DirectoryName) == 8);
struct BossAccount // the actual class name is "Account" and while the boss namespace helps us separate this from Account(.h) we use an alternative name to avoid confusion
{
struct VTable
{
VTableEntry rtti;
VTableEntry dtor;
};
static inline SysAllocator<VTable> s_VTable;
uint32be accountId;
MEMPTR<void> vTablePtr;
static BossAccount* ctor(BossAccount* _this, uint32 accountId);
static void dtor(BossAccount* _this, uint32 options);
static void InitVTable();
};
static_assert(sizeof(BossAccount) == 8);
enum class TaskWaitState : uint32 // for Task::Wait()
{
Done = 1,
};
enum class TaskTurnState : uint32
{
Ukn = 0,
Stopped = 1,
Ready = 4,
Running = 6,
Done = 7, // how does this differ from DoneSuccess?
DoneSuccess = 16,
DoneError = 17
};
enum class TaskState : uint8
{
Initial = 0,
Stopped = 1,
Ready = 4, // waiting for turn to run
Running = 6,
Done = 7,
};
enum class TaskType : uint16
{
NbdlTaskSetting = 2,
RawDlTaskSetting_1 = 1,
RawDlTaskSetting_3 = 3,
RawDlTaskSetting_9 = 9,
RawUlTaskSetting = 4,
PlayLogUploadTaskSetting = 5,
PlayReportSetting = 6,
DataStoreDownloadSetting = 8,
NbdlDataListTaskSetting = 10
};
struct TaskSettingCore // the setting struct as used by IOSU
{
struct LifeTime
{
uint32be high;
uint32be low;
};
uint32be persistentId;
uint32be ukn04;
uint32be ukn08;
uint32be ukn0C;
uint32be ukn10;
uint32be ukn14;
uint32be ukn18;
uint32be ukn1C;
TaskId taskId; // +0x20
betype<TaskType> taskType; // +0x28
uint8be priority; // +0x2A
uint8be mode; // +0x2B
uint8be permission; // +0x2C
uint16be intervalA; // +0x2E
uint32be intervalB; // +0x30
uint32be unk34; // +0x34 - could be padding
LifeTime lifeTime; // +0x38 - this is a 64bit value, but the whole struct has a size of 0x1004 and doesnt preserve the alignment in an array. Its probably handled as two 32bit values?
uint8be httpProtocol; // +0x40
uint8be internalClientCert; // +0x41
uint16be httpOption; // +0x42
uint8 ukn44[0x48 - 0x44]; // padding?
CafeString<256> url; // +0x48
CafeString<64> lastModifiedTime; // +0x148
uint8be internalCaCert[3]; // +0x188
uint8 ukn18B; // +0x18B - padding?
uint32be httpTimeout; // +0x18C
CafeString<128> caCert[3]; // +0x190 - 3 entries, each 0x80 bytes
CafeString<128> clientCertName; // +0x310
CafeString<128> clientCertKey; // +0x390
struct HttpHeader
{
CafeString<32> name; // +0x00
CafeString<64> value; // +0x20
};
HttpHeader httpHeaders[3]; // +0x410
CafeString<96> httpQueryString; // +0x530
CafeString<512> serviceToken; // +0x590
uint8 ukn790[0x7C0 - 0x790];
// after 0x7C0 the task-specific fields seem to start?
union
{
struct
{
uint32be optionValue; // +0x7C0
CafeString<32> largeHttpHeaderKey; // +0x7C4
CafeString<512> largeHttpHeaderValue; // +0x7E4
}rawUl; // RawUlTaskSetting
struct
{
uint32be optionValue; // +0x7C0
CafeString<32> largeHttpHeaderKey; // +0x7C4
CafeString<512> largeHttpHeaderValue; // +0x7E4
// inherits the above values from RawUlTaskSetting
// the play report fields are too large to fit into the available space, so instead they are passed via their own system call that attaches it to the task. See PlayReportSetting::RegisterPreprocess
}playReportSetting;
struct
{
uint8be newArrival; // +0x7C0
uint8be led; // +0x7C1
uint8be ukn7C2[6]; // +0x7C2 - padding?
CafeString<8> bossDirectory; // +0x7C8
CafeString<32> fileName; // +0x7D0 Not 100% sure this is fileName
}rawDl; // RawDlTaskSetting
struct
{
CafeString<32> bossCode; // +0x7C0
CafeString<8> bossDirectory; // +0x7E0
uint32be ukn7E8;
uint32be ukn7EC;
uint32be directorySizeLimitHigh; // +0x7F0
uint32be directorySizeLimitLow; // +0x7F4
CafeString<32> fileName; // +0x7F8
// more fields here...
}nbdl;
struct
{
uint8 finalPadding[0xC00 - 0x7C0];
}paddedBlock;
};
};
static_assert(sizeof(TaskSettingCore) == 0xC00);
struct TaskSetting : public TaskSettingCore
{
uint8 paddingC00[0x1000 - 0xC00];
MEMPTR<void> vTablePtr; // +0x1000
struct VTableTaskSetting
{
VTableEntry rtti;
VTableEntry dtor;
VTableEntry RegisterPreprocess; // todo - double check the offset
VTableEntry unk1;
};
static inline SysAllocator<VTableTaskSetting> s_VTable;
static TaskSetting* ctor(TaskSetting* _thisptr);
static void dtor(TaskSetting* _this, uint32 options);
static bool IsPrivileged(TaskSetting* _thisptr);
static void InitializeSetting(TaskSetting* _thisptr);
static void InitVTable();
};
static_assert(offsetof(TaskSetting, priority) == 0x2A);
static_assert(offsetof(TaskSetting, mode) == 0x2B);
static_assert(offsetof(TaskSetting, permission) == 0x2C);
static_assert(offsetof(TaskSetting, intervalA) == 0x2E);
static_assert(offsetof(TaskSetting, intervalB) == 0x30);
static_assert(offsetof(TaskSetting, lifeTime) == 0x38);
static_assert(offsetof(TaskSetting, httpProtocol) == 0x40);
static_assert(offsetof(TaskSetting, internalClientCert) == 0x41);
static_assert(offsetof(TaskSetting, httpOption) == 0x42);
static_assert(offsetof(TaskSetting, url) == 0x48);
static_assert(offsetof(TaskSetting, lastModifiedTime) == 0x148);
static_assert(offsetof(TaskSetting, internalCaCert) == 0x188);
static_assert(offsetof(TaskSetting, httpTimeout) == 0x18C);
static_assert(offsetof(TaskSetting, caCert) == 0x190);
static_assert(offsetof(TaskSetting, clientCertName) == 0x310);
static_assert(offsetof(TaskSetting, clientCertKey) == 0x390);
static_assert(offsetof(TaskSetting, httpHeaders) == 0x410);
static_assert(offsetof(TaskSetting, httpQueryString) == 0x530);
static_assert(offsetof(TaskSetting, serviceToken) == 0x590);
// rawUl
static_assert(offsetof(TaskSetting, rawUl.optionValue) == 0x7C0);
static_assert(offsetof(TaskSetting, rawUl.largeHttpHeaderKey) == 0x7C4);
static_assert(offsetof(TaskSetting, rawUl.largeHttpHeaderValue) == 0x7E4);
// rawDl
static_assert(offsetof(TaskSetting, rawDl.newArrival) == 0x7C0);
static_assert(offsetof(TaskSetting, rawDl.bossDirectory) == 0x7C8);
static_assert(offsetof(TaskSetting, rawDl.fileName) == 0x7D0);
// nbdl
static_assert(offsetof(TaskSetting, nbdl.bossCode) == 0x7C0);
static_assert(offsetof(TaskSetting, nbdl.bossDirectory) == 0x7E0);
static_assert(offsetof(TaskSetting, vTablePtr) == 0x1000);
static_assert(sizeof(TaskSetting) == 0x1004);
/* NetTaskSetting */
struct NetTaskSetting : TaskSetting
{
struct VTableNetTaskSetting : public VTableTaskSetting
{ };
static inline SysAllocator<VTableNetTaskSetting> s_VTable;
static NetTaskSetting* ctor(NetTaskSetting* _thisptr);
static BossResult AddCaCert(NetTaskSetting* _thisptr, const char* name);
static BossResult SetServiceToken(NetTaskSetting* _thisptr, const uint8* serviceToken);
static BossResult AddInternalCaCert(NetTaskSetting* _thisptr, char certId);
static void SetInternalClientCert(NetTaskSetting* _thisptr, char certId);
static void InitVTable();
};
static_assert(sizeof(NetTaskSetting) == 0x1004);
/* NbdlTaskSetting */
struct NbdlTaskSetting : NetTaskSetting
{
struct VTableNbdlTaskSetting : public VTableNetTaskSetting
{
VTableEntry rttiNetTaskSetting; // unknown
};
static_assert(sizeof(VTableNbdlTaskSetting) == 8*5);
static inline SysAllocator<VTableNbdlTaskSetting> s_VTable;
static NbdlTaskSetting* ctor(NbdlTaskSetting* _thisptr);
static BossResult Initialize(NbdlTaskSetting* _thisptr, const char* bossCode, uint64 directorySizeLimit, const char* bossDirectory);
static BossResult SetFileName(NbdlTaskSetting* _thisptr, const char* fileName);
static void InitVTable();
};
static_assert(sizeof(NbdlTaskSetting) == 0x1004);
/* RawUlTaskSetting */
struct RawUlTaskSetting : NetTaskSetting
{
uint32be ukRaw1; // 0x1004
uint32be ukRaw2; // 0x1008
uint32be ukRaw3; // 0x100C
uint8 rawSpace[0x200]; // 0x1010
struct VTableRawUlTaskSetting : public VTableNetTaskSetting
{
VTableEntry rttiNetTaskSetting; // unknown
};
static_assert(sizeof(VTableRawUlTaskSetting) == 8*5);
static inline SysAllocator<VTableRawUlTaskSetting> s_VTable;
static RawUlTaskSetting* ctor(RawUlTaskSetting* _thisptr);
static void dtor(RawUlTaskSetting* _this, uint32 options);
static void InitVTable();
};
static_assert(sizeof(RawUlTaskSetting) == 0x1210);
/* RawDlTaskSetting */
struct RawDlTaskSetting : NetTaskSetting
{
struct VTableRawDlTaskSetting : public VTableNetTaskSetting
{
VTableEntry rttiNetTaskSetting; // unknown
};
static_assert(sizeof(VTableRawDlTaskSetting) == 8*5);
static inline SysAllocator<VTableRawDlTaskSetting> s_VTable;
static RawDlTaskSetting* ctor(RawDlTaskSetting* _thisptr);
static BossResult Initialize(RawDlTaskSetting* _thisptr, const char* url, bool newArrival, bool led, const char* fileName, const char* bossDirectory);
static void InitVTable();
};
static_assert(sizeof(RawDlTaskSetting) == 0x1004);
/* PlayReportSetting */
struct PlayReportSetting : RawUlTaskSetting
{
MEMPTR<uint8> ukn1210_ptr; // 0x1210
uint32be ukn1214_size; // 0x1214
uint32be ukPlay3; // 0x1218
uint32be ukPlay4; // 0x121C
struct VTablePlayReportSetting : public VTableRawUlTaskSetting
{};
static_assert(sizeof(VTablePlayReportSetting) == 8*5);
static inline SysAllocator<VTablePlayReportSetting> s_VTable;
static PlayReportSetting* ctor(PlayReportSetting* _this);
static void dtor(PlayReportSetting* _this, uint32 options);
static void Initialize(PlayReportSetting* _this, uint8* ptr, uint32 size);
static bool Set(PlayReportSetting* _this, const char* keyname, uint32 value);
static void InitVTable();
};
static_assert(sizeof(PlayReportSetting) == 0x1220);
/* Storage */
enum class StorageKind : uint32
{
StorageNbdl = 0,
StorageRawDl = 1,
};
struct DataName
{
CafeString<32> name;
static DataName* ctor(DataName* _this); // __ct__Q3_2nn4boss8DataNameFv
static const char* operator_const_char(DataName* _this); // __opPCc__Q3_2nn4boss8DataNameCFv
const char* c_str() const
{
return name.c_str();
}
};
static_assert(sizeof(DataName) == 32);
/* IPC commands for /dev/boss */
enum class BossCommandId : uint32
{
// task operations
TaskIsRegistered = 0x69,
TaskRegisterA = 0x6A,
TaskRegisterForImmediateRunA = 0x6B,
TaskUnregister = 0x6D,
TaskRun = 0x78,
TaskStartScheduling = 0x77,
TaskStopScheduling = 0x79,
TaskWaitA = 0x7A,
TaskGetHttpStatusCodeA = 0x7C,
TaskGetTurnState = 0x7E,
TaskGetContentLength = 0x82,
TaskGetProcessedLength = 0x83,
// storage operations
StorageExist = 0x87,
StorageGetDataList = 0xB0,
// NsData operations
NsDataExist = 0x90,
NsDataRead = 0x93,
NsDataGetSize = 0x96,
NsDataDeleteFile = 0xA7,
NsDataDeleteFileWithHistory = 0xA8,
NsFinalize = 0xB3,
// Title operations
TitleSetNewArrivalFlag = 0x9E,
// most (if not all?) opcodes seem to have a secondary form with an additional titleId parameter in the high range around 0x150 and serviceId 1
// unknown commands
UknA7 = 0xA5,
DeleteDataRelated = 0xA6,
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
#pragma once
#include "Cafe/IOSU/iosu_types_common.h"
namespace iosu::boss
{
IOSUModule* GetModule();
}

View file

@ -188,18 +188,43 @@ namespace iosu
else if (cmd->cmdId == IPCCommandId::IOS_IOCTLV)
{
uint32 requestId = cmd->args[0];
uint32 numIn = cmd->args[1];
uint32 numOut = cmd->args[2];
uint32 numOut = cmd->args[1];
uint32 numIn = cmd->args[2];
IPCIoctlVector* vec = MEMPTR<IPCIoctlVector>{ cmd->args[3] }.GetPtr();
IPCIoctlVector* vecIn = vec + numIn;
IPCIoctlVector* vecOut = vec + 0;
IPCIoctlVector* vecOut = vec + 0; // out buffers come first
IPCIoctlVector* vecIn = vec + numOut;
cemu_assert(numIn > 0 && numOut > 0);
cemu_assert(vecIn->size >= 80 && !vecIn->basePhys.IsNull());
IPCServiceRequest* serviceRequest = MEMPTR<IPCServiceRequest>(vecIn->basePhys).GetPtr();
IPCServiceResponse* serviceResponse = MEMPTR<IPCServiceResponse>(vecOut->basePhys).GetPtr();
IPCServiceRequestHeader* serviceRequest = MEMPTR<IPCServiceRequestHeader>(vecIn->basePhys).GetPtr();
IPCServiceResponseHeader* serviceResponse = MEMPTR<IPCServiceResponseHeader>(vecOut->basePhys).GetPtr();
IOSDevHandle clientHandle = 0; // todo
IPCServiceCall serviceCall(clientHandle, serviceRequest->serviceId, serviceRequest->commandId);
#if 0
// log all buffers
cemuLog_log(LogType::Force, "IPC ServiceCall. In: {}, Out: {}, ServiceId: {}, CommandId: {} (0x{:x})", numIn, numOut, serviceRequest->serviceId, serviceRequest->commandId, serviceRequest->commandId);
for (size_t i = 0; i <numOut+numIn; i++)
{
cemuLog_log(LogType::Force, "");
cemuLog_log(LogType::Force, "Buffer {} - BasePhys: {}, Size: {}", i, vec[i].basePhys, vec[i].size);
cemuLog_logHexDump(LogType::Force, MEMPTR<uint8>(vec[i].basePhys).GetPtr(), vec[i].size, 16);
}
#endif
// parameter and response data is appended directly after the headers, so we add the streams without the headers
serviceCall.AddInputStream(MEMPTR<uint8>(vecIn[0].basePhys).GetPtr() + sizeof(IPCServiceRequestHeader), vecIn[0].size - sizeof(IPCServiceRequestHeader));
serviceCall.AddOutputStream(MEMPTR<uint8>(vecOut[0].basePhys).GetPtr() + sizeof(IPCServiceResponseHeader), vecOut[0].size - sizeof(IPCServiceResponseHeader));
// add remaining input/output buffers
for (size_t i = 1; i < numIn; i++)
serviceCall.AddInputStream(MEMPTR<uint8>(vecIn[i].basePhys).GetPtr(), vecIn[i].size);
for (size_t i = 1; i < numOut; i++)
serviceCall.AddOutputStream(MEMPTR<uint8>(vecOut[i].basePhys).GetPtr(), vecOut[i].size);
serviceResponse->nnResultCode = (uint32)ServiceCall(serviceCall);
serviceResponse->nnResultCode = (uint32)ServiceCall(serviceRequest->serviceId, nullptr, nullptr);
IOS_ResourceReply(cmd, IOS_ERROR_OK);
continue;
}

View file

@ -73,25 +73,239 @@ namespace iosu
SysAllocator<iosu::kernel::IOSMessage, 128> _m_msgBuffer;
};
struct IPCServiceRequest
{
uint32be ukn00;
uint32be serviceId;
uint32be ukn08;
uint32be commandId;
};
static_assert(sizeof(IPCServiceRequest) == 0x10);
struct IPCServiceResponse
{
uint32be nnResultCode;
};
// a complex service interface which wraps Ioctlv and adds an additional service channel, used by /dev/act, /dev/acp_main, ?
// a complex service interface which wraps Ioctlv and adds an additional service channel, used by /dev/act, /dev/acp_main, /dev/boss and most nn services
class IPCService
{
struct IPCServiceRequestHeader
{
uint32be ukn00;
uint32be serviceId;
uint32be ukn08;
uint32be commandId;
};
static_assert(sizeof(IPCServiceRequestHeader) == 0x10);
struct IPCServiceResponseHeader
{
uint32be nnResultCode;
};
public:
class IPCParameterStream // input stream for parameters
{
public:
IPCParameterStream() = default;
IPCParameterStream(void* data, uint32 size) : m_data((uint8_t*)data), m_size(size) {}
template<typename T>
T ReadParameter(bool& hasError)
{
hasError = false;
if (m_readIndex + sizeof(T) > m_size)
{
hasError = true;
return T{};
}
T value;
memcpy(&value, &m_data[m_readIndex], sizeof(T));
m_readIndex += sizeof(T);
return value;
}
uint8* GetData() { return m_data; }
uint32 GetSize() const { return m_size; }
private:
uint8* m_data{nullptr};
uint32 m_size{0};
uint32 m_readIndex{0};
};
class IPCResponseStream // output stream for response data
{
public:
IPCResponseStream() = default;
IPCResponseStream(void* data, uint32 size) : m_data((uint8*)data), m_size(size) {}
template<typename T>
void Write(const T& value)
{
if (m_writtenSize + sizeof(T) > m_size)
{
m_hasError = true;
return;
}
memcpy(&m_data[m_writtenSize], &value, sizeof(T));
m_writtenSize += sizeof(T);
}
uint8* GetData() { return m_data; }
uint32 GetSize() const { return m_size; }
private:
uint8* m_data{nullptr};
uint32 m_writtenSize{0};
uint32 m_size{0};
bool m_hasError{false};
};
class IPCServiceCall
{
struct LargeBufferHeader
{
uint32be alignedSize; // size of the aligned (middle) part of the buffer
uint8be ukn4;
uint8be ukn5;
uint8be headSize;
uint8be tailSize;
};
static_assert(sizeof(LargeBufferHeader) == 8);
public:
struct UnalignedBuffer
{
UnalignedBuffer(bool isOutput, std::span<uint8> head, std::span<uint8> middle, std::span<uint8> tail) : m_isOutput(isOutput)
{
headPtr = head.data();
headSize = (uint32)head.size();
middlePtr = middle.data();
middleSize = (uint32)middle.size();
tailPtr = tail.data();
tailSize = (uint32)tail.size();
};
template<typename T>
T ReadType()
{
cemu_assert(!m_isOutput);
T v;
memcpy((uint8_t*)&v, headPtr, headSize);
memcpy((uint8_t*)&v + headSize, middlePtr, middleSize);
memcpy((uint8_t*)&v + headSize + middleSize, tailPtr, tailSize);
return v;
}
void WriteData(void* data, size_t size)
{
if (size == 0)
return;
cemu_assert(m_isOutput);
cemu_assert((headSize + middleSize + tailSize) >= size);
size_t bytesToCopy = std::min<size_t>(size, headSize);
memcpy(headPtr, data, bytesToCopy);
size -= bytesToCopy;
if (size > 0)
{
bytesToCopy = std::min<size_t>(size, middleSize);
memcpy(middlePtr, (uint8_t*)data + headSize, bytesToCopy);
size -= bytesToCopy;
}
if (size > 0)
{
bytesToCopy = std::min<size_t>(size, tailSize);
memcpy(tailPtr, (uint8_t*)data + headSize + middleSize, bytesToCopy);
}
}
size_t GetSize() const
{
return headSize + middleSize + tailSize;
}
private:
void* headPtr;
uint32 headSize;
void* middlePtr; // aligned
uint32 middleSize;
void* tailPtr;
uint32 tailSize;
bool m_isOutput;
};
IPCServiceCall(IOSDevHandle clientHandle, uint32 serviceId, uint32 commandId) : m_clientHandle(clientHandle), m_serviceId(serviceId), m_commandId(commandId)
{
}
void AddInputStream(void* data, uint32 size)
{
cemu_assert(m_paramStreamArraySize < 4);
m_paramStreamArray[m_paramStreamArraySize++] = IPCParameterStream(data, size);
}
void AddOutputStream(void* data, uint32 size)
{
cemu_assert(m_responseStreamArraySize < 4);
m_responseStreamArray[m_responseStreamArraySize++] = IPCResponseStream(data, size);
}
uint32 GetServiceId() const
{
return m_serviceId;
}
uint32 GetCommandId() const
{
return m_commandId;
}
template<typename T>
T ReadParameter()
{
// read only from stream 0 for now
return m_paramStreamArray[0].ReadParameter<T>(m_hasError);
}
UnalignedBuffer ReadUnalignedInputBufferInfo()
{
// how large/unaligned buffers work:
// Instead of appending the data into the parameter stream, there are two separate buffers created:
// 1. A ioctl vector that points directly to the aligned part of the original buffer. Both the pointer and the size are aligned
// 2. A ioctl vector with an allocated up-to-128byte buffer that holds any unaligned head or tail data (e.g. anything that isn't occupying the full 64 byte alignment)
// The buffer layout is then also serialized into the parameter stream
LargeBufferHeader header = ReadParameter<LargeBufferHeader>();
// get aligned buffer part
void* alignedBuffer = m_paramStreamArray[m_inputBufferIndex+0].GetData();
cemu_assert(m_paramStreamArray[m_inputBufferIndex+0].GetSize() == header.alignedSize);
// get head and tail buffer parts
uint8* unalignedDataBuffer = m_paramStreamArray[m_inputBufferIndex+1].GetData();
cemu_assert((header.headSize + header.tailSize) <= m_paramStreamArray[m_inputBufferIndex+1].GetSize());
UnalignedBuffer largeBuffer(false, {(uint8*)unalignedDataBuffer, header.headSize}, {(uint8*)alignedBuffer, header.alignedSize}, {(uint8*)unalignedDataBuffer + header.headSize, header.tailSize});
m_inputBufferIndex += 2; // if there is no unaligned data then are both buffers still present?
return largeBuffer;
}
UnalignedBuffer ReadUnalignedOutputBufferInfo()
{
LargeBufferHeader header = ReadParameter<LargeBufferHeader>();
// get aligned buffer part
void* alignedBuffer = m_responseStreamArray[m_outputBufferIndex+0].GetData();
cemu_assert(m_responseStreamArray[m_outputBufferIndex+0].GetSize() == header.alignedSize);
// get head and tail buffer parts
uint8* unalignedDataBuffer = m_responseStreamArray[m_outputBufferIndex+1].GetData();
cemu_assert((header.headSize + header.tailSize) <= m_responseStreamArray[m_outputBufferIndex+1].GetSize());
UnalignedBuffer largeBuffer(true, {(uint8*)unalignedDataBuffer, header.headSize}, {(uint8*)alignedBuffer, header.alignedSize}, {(uint8*)unalignedDataBuffer + header.headSize, header.tailSize});
m_outputBufferIndex += 2; // if there is no unaligned data then are both buffers still present?
return largeBuffer;
}
template<typename T>
void WriteResponse(const T& value)
{
m_responseStreamArray[0].Write<T>(value);
}
private:
IOSDevHandle m_clientHandle;
uint32 m_serviceId;
uint32 m_commandId;
IPCParameterStream m_paramStreamArray[4]{};
size_t m_paramStreamArraySize{0};
IPCResponseStream m_responseStreamArray[4]{};
size_t m_responseStreamArraySize{0};
bool m_hasError{false};
sint8 m_inputBufferIndex{1};
sint8 m_outputBufferIndex{1};
};
IPCService(std::string_view devicePath) : m_devicePath(devicePath) {};
virtual ~IPCService() {};
@ -105,7 +319,7 @@ namespace iosu
}
virtual nnResult ServiceCall(uint32 serviceId, void* request, void* response)
virtual nnResult ServiceCall(IPCServiceCall& serviceCall)
{
cemu_assert_unimplemented();
return 0;

View file

@ -206,8 +206,12 @@ namespace GX2
void GX2SetTVGamma(float gamma)
{
if (abs(gamma - 1.0f) > 0.01f)
cemuLog_logDebug(LogType::Force, "TV gamma set to {} which is not supported", gamma);
LatteGPUState.tvGamma = (1.0f - gamma);
}
void GX2SetDRCGamma(float gamma)
{
LatteGPUState.drcGamma = (1.0f - gamma);
}
bool GX2GetLastFrame(uint32 deviceId, GX2Texture* textureOut)
@ -307,6 +311,7 @@ namespace GX2
cafeExportRegister("gx2", GX2SetTVBuffer, LogType::GX2);
cafeExportRegister("gx2", GX2SetTVGamma, LogType::GX2);
cafeExportRegister("gx2", GX2SetDRCGamma, LogType::GX2);
cafeExportRegister("gx2", GX2GetLastFrame, LogType::GX2);
cafeExportRegister("gx2", GX2GetLastFrameGammaA, LogType::GX2);

View file

@ -161,7 +161,7 @@ void gx2Surface_GX2CopySurface(GX2Surface* srcSurface, uint32 srcMip, uint32 src
if( dstMipWidth != srcMipWidth || dstMipHeight != srcMipHeight )
{
cemu_assert_debug(false);
cemuLog_logDebugOnce(LogType::Force, "GX2CopySurface: Mismatching mip resolution");
return;
}
// handle format

View file

@ -1 +1,6 @@
void nnAc_load();
namespace nn_ac
{
nnResult IsApplicationConnected(uint8be* connected);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,261 @@
#pragma once
#include "Cafe/OS/libs/coreinit/coreinit_IPC.h"
#include <boost/container/static_vector.hpp>
class IPCServiceClient
{
public:
class IPCServiceCall
{
struct IOVectorBuffer
{
IOVectorBuffer() = default;
IOVectorBuffer(uint8* ptr, uint32 size, bool isExternalBuffer = false) : ptr(ptr), size(size), isExternalBuffer(isExternalBuffer) {}
uint8* ptr;
uint32 size;
bool isExternalBuffer{false}; // buffer provided by caller
};
public:
IPCServiceCall(IPCServiceClient& client, uint32 serviceId, uint32 commandId) : m_client(client)
{
// allocate a parameter and response buffer
IPCBuffer* cmdBuf = client.AllocateCommandBuffer();
m_paramBuffers.emplace_back(cmdBuf->data, sizeof(IPCBuffer));
cmdBuf = client.AllocateCommandBuffer();
m_responseBuffers.emplace_back(cmdBuf->data, sizeof(IPCBuffer));
// write the request header
WriteParam<uint32be>(0); // ukn00
WriteParam<uint32be>(serviceId); // serviceId
WriteParam<uint32be>(0); // ukn08
WriteParam<uint32be>(commandId); // commandId
}
~IPCServiceCall()
{
for (auto& buf : m_paramBuffers)
{
if (buf.isExternalBuffer)
continue;
m_client.ReleaseCommandBuffer((IPCBuffer*)buf.ptr);
}
for (auto& buf : m_responseBuffers)
{
if (buf.isExternalBuffer)
continue;
m_client.ReleaseCommandBuffer((IPCBuffer*)buf.ptr);
}
}
IPCServiceCall(const IPCServiceCall&) = delete;
IPCServiceCall& operator=(const IPCServiceCall&) = delete;
template<typename T>
void WriteParam(const T& value)
{
cemu_assert(m_paramWriteIndex + sizeof(T) <= m_paramBuffers[0].size);
memcpy(m_paramBuffers[0].ptr + m_paramWriteIndex, &value, sizeof(T));
m_paramWriteIndex += sizeof(T);
}
// ptr and size defines an input buffer (PPC->IOSU)
void WriteParamBuffer(MEMPTR<void> ptr, uint32 size)
{
WriteInOutBuffer(MEMPTR<uint8>(ptr), size, false);
}
// ptr and size defines an output buffer (IOSU->PPC)
void WriteResponseBuffer(MEMPTR<void> ptr, uint32 size)
{
WriteInOutBuffer(MEMPTR<uint8>(ptr), size, true);
}
nnResult Submit()
{
StackAllocator<IPCIoctlVector, 16> vectorArray;
uint32 ioVecCount = m_paramBuffers.size() + m_responseBuffers.size();
cemu_assert(ioVecCount <= 16);
// output buffers come first
for (size_t i = 0; i < m_responseBuffers.size(); ++i)
{
vectorArray[i].baseVirt = MEMPTR<uint8>(m_responseBuffers[i].ptr);
vectorArray[i].basePhys = nullptr;
vectorArray[i].size = m_responseBuffers[i].size;
}
// input buffers
for (size_t i = 0; i < m_paramBuffers.size(); ++i)
{
vectorArray[m_responseBuffers.size() + i].baseVirt = MEMPTR<uint8>(m_paramBuffers[i].ptr);
vectorArray[m_responseBuffers.size() + i].basePhys = nullptr;
vectorArray[m_responseBuffers.size() + i].size = m_paramBuffers[i].size;
}
IOS_ERROR r = coreinit::IOS_Ioctlv(m_client.GetDevHandle(), 0, m_responseBuffers.size(), m_paramBuffers.size(), vectorArray.GetPointer());
if ( (r&0x80000000) != 0)
{
cemu_assert_unimplemented(); // todo - handle submission errors
}
uint32be resultCode = ReadResponse<uint32be>();
if (!NN_RESULT_IS_FAILURE((uint32)resultCode))
{
for (auto& bufCopy : m_bufferCopiesOut)
{
memcpy(bufCopy.dst, bufCopy.src, bufCopy.size);
}
}
return static_cast<nnResult>(resultCode);
}
template<typename T>
T ReadResponse()
{
cemu_assert(m_responseReadIndex + sizeof(T) <= m_responseBuffers[0].size);
T value;
memcpy(&value, m_responseBuffers[0].ptr + m_responseReadIndex, sizeof(T));
m_responseReadIndex += sizeof(T);
return value;
}
private:
struct BufferCopyOut
{
BufferCopyOut(void* dst, void* src, uint32 size) : dst(dst), src(src), size(size) {}
void* dst;
void* src;
uint32 size;
};
void WriteInOutBuffer(MEMPTR<uint8> ptr, uint32 size, bool isOutput)
{
uint32 headSize = (0x40 - (ptr.GetMPTR()&0x3F))&0x3F;
headSize = std::min<uint32>(headSize, size);
uint32 alignedSize = size - headSize;
uint32 tailSize = alignedSize - (alignedSize&~0x3F);
alignedSize -= tailSize;
// verify
cemu_assert_debug(headSize + alignedSize + tailSize == size);
cemu_assert_debug(alignedSize == 0 || ((ptr.GetMPTR()+headSize)&0x3F) == 0);
cemu_assert_debug(tailSize == 0 || ((ptr.GetMPTR()+headSize+alignedSize)&0x3F) == 0);
if (isOutput)
cemu_assert(m_responseBuffers.size()+2 <= m_responseBuffers.capacity());
else
cemu_assert(m_paramBuffers.size()+2 <= m_paramBuffers.capacity());
IOVectorBuffer alignedBuffer;
alignedBuffer.ptr = ptr + headSize;
alignedBuffer.size = alignedSize;
alignedBuffer.isExternalBuffer = true;
if (isOutput)
m_responseBuffers.emplace_back(alignedBuffer);
else
m_paramBuffers.emplace_back(alignedBuffer);
IPCBuffer* headAndTailBuffer = m_client.AllocateCommandBuffer();
IOVectorBuffer headAndTail;
headAndTail.ptr = headAndTailBuffer->data + (0x40 - headSize);
headAndTail.size = 128 - (0x40 - headSize);
headAndTail.isExternalBuffer = false;
if (headSize > 0)
{
if (isOutput)
m_bufferCopiesOut.emplace_back(ptr, headAndTailBuffer->data + 0x40 - headSize, headSize);
else
memcpy(headAndTailBuffer->data + 0x40 - headSize, ptr, headSize);
}
if (tailSize > 0)
{
if (isOutput)
m_bufferCopiesOut.emplace_back(ptr + headSize + alignedSize, headAndTailBuffer->data + 0x40, tailSize);
else
memcpy(headAndTailBuffer->data + 0x40, ptr + headSize + alignedSize, tailSize);
}
if (isOutput)
m_responseBuffers.emplace_back(headAndTail);
else
m_paramBuffers.emplace_back(headAndTail);
// serialize into parameter stream
WriteParam<uint32be>(alignedSize);
WriteParam<uint8be>(0); // ukn4
WriteParam<uint8be>(0); // ukn5
WriteParam<uint8be>((uint8)headSize);
WriteParam<uint8be>((uint8)tailSize);
}
IPCServiceClient& m_client;
boost::container::static_vector<IOVectorBuffer, 8> m_paramBuffers;
boost::container::static_vector<IOVectorBuffer, 8> m_responseBuffers;
sint32 m_paramWriteIndex{0};
sint32 m_responseReadIndex{0};
boost::container::static_vector<BufferCopyOut, 16> m_bufferCopiesOut;
};
IPCServiceClient()
{
}
~IPCServiceClient()
{
Shutdown();
}
void Initialize(std::string_view devicePath, uint8_t* buffer, uint32_t bufferSize)
{
m_devicePath = devicePath;
m_buffer = buffer;
m_bufferSize = bufferSize;
static_assert(sizeof(IPCBuffer) == 256);
size_t numCommandBuffers = m_bufferSize / sizeof(IPCBuffer);
m_commandBuffersFree.resize(numCommandBuffers);
for (size_t i = 0; i < numCommandBuffers; ++i)
{
m_commandBuffersFree[i] = reinterpret_cast<IPCBuffer*>(m_buffer + i * sizeof(IPCBuffer));
}
m_clientHandle = coreinit::IOS_Open(m_devicePath.c_str(), 0);
}
void Shutdown()
{
if (m_clientHandle != 0)
{
coreinit::IOS_Close(m_clientHandle);
m_clientHandle = 0;
}
}
IPCServiceCall Begin(uint32_t serviceId, uint32_t commandId)
{
return IPCServiceCall(*this, serviceId, commandId);
}
IOSDevHandle GetDevHandle() const
{
cemu_assert(m_clientHandle != 0);
return m_clientHandle;
}
private:
struct IPCBuffer
{
uint8 data[256];
};
IPCBuffer* AllocateCommandBuffer()
{
cemu_assert(m_commandBuffersFree.size() > 0);
IPCBuffer* buf = m_commandBuffersFree.back();
m_commandBuffersFree.pop_back();
return buf;
}
void ReleaseCommandBuffer(IPCBuffer* buffer)
{
m_commandBuffersFree.emplace_back(buffer);
}
private:
std::string m_devicePath;
IOSDevHandle m_clientHandle{0};
uint8_t* m_buffer{nullptr};
uint32_t m_bufferSize{0};
std::vector<IPCBuffer*> m_commandBuffersFree;
};

View file

@ -14,7 +14,7 @@ namespace nsyshid::backend::libusb
{
m_ctx = nullptr;
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: failed to initialize libusb, return code: {}",
m_initReturnCode);
m_initReturnCode);
return;
}
@ -33,8 +33,8 @@ namespace nsyshid::backend::libusb
if (ret != LIBUSB_SUCCESS)
{
cemuLog_logDebug(LogType::Force,
"nsyshid::BackendLibusb: failed to register hotplug callback with return code {}",
ret);
"nsyshid::BackendLibusb: failed to register hotplug callback with return code {}",
ret);
}
else
{
@ -51,8 +51,8 @@ namespace nsyshid::backend::libusb
if (ret != 0)
{
cemuLog_logDebug(LogType::Force,
"nsyshid::BackendLibusb: hotplug thread: error handling events: {}",
ret);
"nsyshid::BackendLibusb: hotplug thread: error handling events: {}",
ret);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
@ -137,8 +137,8 @@ namespace nsyshid::backend::libusb
case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
{
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device arrived: {:04x}:{:04x}",
desc.idVendor,
desc.idProduct);
desc.idVendor,
desc.idProduct);
auto device = CheckAndCreateDevice(dev);
if (device != nullptr)
{
@ -165,8 +165,8 @@ namespace nsyshid::backend::libusb
case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
{
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device left: {:04x}:{:04x}",
desc.idVendor,
desc.idProduct);
desc.idVendor,
desc.idProduct);
auto device = FindLibusbDevice(dev);
if (device != nullptr)
{
@ -202,7 +202,7 @@ namespace nsyshid::backend::libusb
if (ret < 0)
{
cemuLog_logDebug(LogType::Force,
"nsyshid::BackendLibusb::FindLibusbDevice(): failed to get device descriptor");
"nsyshid::BackendLibusb::FindLibusbDevice(): failed to get device descriptor");
return nullptr;
}
uint8 busNumber = libusb_get_bus_number(dev);
@ -267,7 +267,7 @@ namespace nsyshid::backend::libusb
if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241)
{
cemuLog_logDebug(LogType::Force,
"nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected");
"nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected");
}
auto device = std::make_shared<DeviceLibusb>(m_ctx,
desc.idVendor,
@ -492,7 +492,7 @@ namespace nsyshid::backend::libusb
if (!handleLock->IsValid())
{
cemuLog_logDebug(LogType::Force,
"nsyshid::DeviceLibusb::read(): cannot read from a non-opened device\n");
"nsyshid::DeviceLibusb::read(): cannot read from a non-opened device\n");
return ReadResult::Error;
}
@ -525,8 +525,8 @@ namespace nsyshid::backend::libusb
return ReadResult::Success;
}
cemuLog_logDebug(LogType::Force,
"nsyshid::DeviceLibusb::read(): failed at endpoint 0x{:02x} with error message: {}", this->m_libusbEndpointIn,
libusb_error_name(ret));
"nsyshid::DeviceLibusb::read(): failed at endpoint 0x{:02x} with error message: {}", this->m_libusbEndpointIn,
libusb_error_name(ret));
return ReadResult::Error;
}
@ -536,7 +536,7 @@ namespace nsyshid::backend::libusb
if (!handleLock->IsValid())
{
cemuLog_logDebug(LogType::Force,
"nsyshid::DeviceLibusb::write(): cannot write to a non-opened device\n");
"nsyshid::DeviceLibusb::write(): cannot write to a non-opened device\n");
return WriteResult::Error;
}
@ -565,8 +565,8 @@ namespace nsyshid::backend::libusb
return WriteResult::Success;
}
cemuLog_logDebug(LogType::Force,
"nsyshid::DeviceLibusb::write(): failed with error code: {}",
ret);
"nsyshid::DeviceLibusb::write(): failed with error code: {}",
ret);
return WriteResult::Error;
}
@ -763,6 +763,9 @@ namespace nsyshid::backend::libusb
int DeviceLibusb::ClaimAllInterfaces(uint8 config_num)
{
const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](uint8 i) {
// On macos detaching would fail without root or entitlement.
// We assume user is using GCAdapterDriver and therefore don't want to detach anything
#if !defined(__APPLE__)
if (libusb_kernel_driver_active(this->m_libusbHandle, i))
{
const int ret2 = libusb_detach_kernel_driver(this->m_libusbHandle, i);
@ -773,6 +776,7 @@ namespace nsyshid::backend::libusb
return ret2;
}
}
#endif
return libusb_claim_interface(this->m_libusbHandle, i);
});
if (ret < LIBUSB_SUCCESS)

View file

@ -43,17 +43,8 @@ set_property(TARGET CemuComponents PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$
target_include_directories(CemuComponents PUBLIC "../")
target_link_libraries(CemuComponents PRIVATE
CemuCafe
CemuCommon
CemuConfig
CemuGui
CemuUtil
Boost::headers
CURL::libcurl
OpenSSL::Crypto
OpenSSL::SSL
pugixml::pugixml
ZLIB::ZLIB
CemuGui
)
# PUBLIC because fmt/format.h is included in ExpressionParser/ExpressionParser.h

View file

@ -224,6 +224,24 @@ bool cemuLog_log(LogType type, std::u8string_view text)
return cemuLog_log(type, s);
}
void cemuLog_logHexDump(LogType type, const void* data, size_t size, size_t lineSize)
{
const uint8* dataU8 = static_cast<const uint8*>(data);
std::vector<char> hexLine;
hexLine.resize(6 + lineSize * 3 + 1, ' ');
for (size_t i = 0; i < size; i += lineSize)
{
sprintf(hexLine.data(), "%04X: ", (int)i);
size_t remainingSize = std::min<size_t>(lineSize, size - i);
for (size_t j=0; j<remainingSize; j++)
sprintf(hexLine.data() + 6 + j * 3, "%02X ", dataU8[j]);
dataU8 += remainingSize;
if (remainingSize == lineSize)
hexLine.resize(6 + remainingSize * 3 + 1);
cemuLog_log(type, hexLine.data());
}
}
void cemuLog_waitForFlush()
{
cemuLog_createLogFile(false);

View file

@ -125,6 +125,9 @@ bool cemuLog_logDebug(LogType type, TFmt format, TArgs&&... args)
#define cemuLog_logDebugOnce(...) { static bool _not_first_call = false; if (!_not_first_call) { _not_first_call = true; cemuLog_logDebug(__VA_ARGS__); } }
// utility function for logging binary data as a hex dump
void cemuLog_logHexDump(LogType type, const void* data, size_t size, size_t lineSize = 16);
// cafe lib calls
bool cemuLog_advancedPPCLoggingEnabled();

View file

@ -269,6 +269,7 @@ const char* ppcAssembler_getInstructionName(uint32 ppcAsmOp)
case PPCASM_OP_PS_NMSUB: return "PS_NMSUB";
case PPCASM_OP_FMR: return "FMR";
case PPCASM_OP_FABS: return "FABS";
case PPCASM_OP_FNEG: return "FNEG";
case PPCASM_OP_FRSP: return "FRSP";
case PPCASM_OP_FRSQRTE: return "FRSQRTE";
@ -1325,6 +1326,7 @@ PPCInstructionDef ppcInstructionTable[] =
{PPCASM_OP_FCMPO, 0, 63, 32|OPC_EXTENDED_BIT, OPC_NONE, OP_FORM_X_FP_CMP, FLG_DEFAULT, 0, 0, nullptr },
{PPCASM_OP_FNEG, 0, 63, 40|OPC_EXTENDED_BIT, OPC_NONE, OP_FORM_DYNAMIC, FLG_DEFAULT, 0, 0, nullptr, { EncodedOperand_FPR(21), EncodedOperand_FPR(11) } },
{PPCASM_OP_FMR, 0, 63, 72|OPC_EXTENDED_BIT, OPC_NONE, OP_FORM_DYNAMIC, FLG_DEFAULT, 0, 0, nullptr, { EncodedOperand_FPR(21), EncodedOperand_FPR(11) } },
{PPCASM_OP_FABS, 0, 63, 264|OPC_EXTENDED_BIT, OPC_NONE, OP_FORM_DYNAMIC, FLG_DEFAULT, 0, 0, nullptr, { EncodedOperand_FPR(21), EncodedOperand_FPR(11) } },
};
@ -3194,6 +3196,13 @@ void ppcAsmTestDisassembler()
checkOpFPR(0, 7);
checkOpFPR(1, 12);
// FABS
_testAsm(0xFC200A10, "fabs f1, f1");
disassemble(0xFC200A10, PPCASM_OP_FABS);
checkOperandMask(true, true);
checkOpFPR(0, 1);
checkOpFPR(1, 1);
// FCTIWZ
_testAsm(0xFD80401E, "fctiwz f12, f8");
disassemble(0xFD80401E, PPCASM_OP_FCTIWZ);

View file

@ -233,6 +233,7 @@ enum PPCASM_OP
// FP
PPCASM_OP_FMR,
PPCASM_OP_FABS,
PPCASM_OP_FNEG,
PPCASM_OP_FRSP,
PPCASM_OP_FRSQRTE,

View file

@ -23,7 +23,7 @@ CURLcode _sslctx_function_NUS(CURL* curl, void* sslctx, void* param)
{
cemuLog_log(LogType::Force, "Invalid CA certificate (102)");
}
if (iosuCrypto_addCACertificate(sslctx, 0x69) == false)
if (iosuCrypto_addCACertificate(sslctx, 105) == false)
{
cemuLog_log(LogType::Force, "Invalid CA certificate (105)");
}
@ -79,7 +79,6 @@ CURLcode _sslctx_function_OLIVE(CURL* curl, void* sslctx, void* param)
cemuLog_log(LogType::Force, "Olive client certificate error");
}
// NSSLAddServerPKIGroups(sslCtx, 3, &x, &y);
{
std::vector<sint16> certGroups = {
100, 101, 102, 103, 104, 105,
@ -99,6 +98,32 @@ CURLcode _sslctx_function_OLIVE(CURL* curl, void* sslctx, void* param)
return CURLE_OK;
}
CURLcode _sslctx_function_CUSTOM(CURL* curl, void* sslctx, void* param)
{
CurlRequestHelper* requestHelper = (CurlRequestHelper*)param;
for (auto& caCertId : requestHelper->GetCaCertIds())
{
if (iosuCrypto_addCACertificate(sslctx, caCertId) == false)
{
cemuLog_log(LogType::Force, "Invalid CA certificate ({})", caCertId);
}
}
for (auto& clientCertId : requestHelper->GetClientCertIds())
{
if (iosuCrypto_addCACertificate(sslctx, clientCertId) == false)
{
cemuLog_log(LogType::Force, "Invalid client certificate ({})", clientCertId);
}
}
SSL_CTX_set_mode((SSL_CTX*)sslctx, SSL_MODE_AUTO_RETRY);
SSL_CTX_set_verify_depth((SSL_CTX*)sslctx, 2);
if (requestHelper->GetClientCertIds().empty())
SSL_CTX_set_verify((SSL_CTX*)sslctx, SSL_VERIFY_NONE, nullptr);
else
SSL_CTX_set_verify((SSL_CTX*)sslctx, SSL_VERIFY_PEER, nullptr);
return CURLE_OK;
}
CurlRequestHelper::CurlRequestHelper()
{
m_curl = curl_easy_init();
@ -162,6 +187,11 @@ void CurlRequestHelper::initate(NetworkService service, std::string url, SERVER_
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_OLIVE);
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, NULL);
}
else if (sslContext == SERVER_SSL_CONTEXT::CUSTOM)
{
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_FUNCTION, _sslctx_function_CUSTOM);
curl_easy_setopt(m_curl, CURLOPT_SSL_CTX_DATA, this);
}
else
{
cemu_assert(false);
@ -218,7 +248,8 @@ bool CurlRequestHelper::submitRequest(bool isPost)
// post
if (isPost)
{
if (!m_isUsingMultipartFormData) {
if (!m_isUsingMultipartFormData)
{
curl_easy_setopt(m_curl, CURLOPT_POST, 1);
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_postData.data());
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, m_postData.size());
@ -242,9 +273,14 @@ bool CurlRequestHelper::submitRequest(bool isPost)
// check response code
long httpCode = 0;
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &httpCode);
m_httpStatusCode = httpCode;
if (httpCode != 200)
{
cemuLog_log(LogType::Force, "HTTP request received response {} but expected 200", httpCode);
char* effectiveUrl = nullptr;
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &effectiveUrl);
if (effectiveUrl)
cemuLog_log(LogType::Force, "Request: {}", effectiveUrl);
// error status codes (4xx or 5xx range) are always considered a failed request, except for code 400 which is usually returned as a response to failed logins etc.
if (httpCode >= 400 && httpCode <= 599 && httpCode != 400)
return false;

View file

@ -28,6 +28,7 @@ public:
IDBE, // idbe-wup.
TAGAYA, // tagaya.wup.shop.nintendo.net
OLIVE, // olv.
CUSTOM, // use cert parameters
};
CurlRequestHelper();
@ -56,6 +57,41 @@ public:
m_isUsingMultipartFormData = isUsingMultipartFormData;
}
void ClearCaCertIds()
{
m_caCertIds.clear();
}
void ClearClientCertIds()
{
m_clientCertIds.clear();
}
void AddCaCertId(sint32 caCertId)
{
m_caCertIds.emplace_back(caCertId);
}
void AddClientCertId(sint32 clientCertId)
{
m_clientCertIds.emplace_back(clientCertId);
}
std::vector<sint32> GetCaCertIds() const
{
return m_caCertIds;
}
std::vector<sint32> GetClientCertIds() const
{
return m_clientCertIds;
}
sint32 GetHTTPStatusCode() const
{
return m_httpStatusCode;
}
private:
static size_t __curlWriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata);
@ -69,6 +105,11 @@ private:
void* m_writeCallbackUserData{};
bool m_isUsingMultipartFormData = false;
// cert data
std::vector<sint32> m_caCertIds;
std::vector<sint32> m_clientCertIds;
// result
sint32 m_httpStatusCode{ 0 };
};
class CurlSOAPHelper // todo - make this use CurlRequestHelper

View file

@ -59,9 +59,6 @@ target_precompile_headers(CemuCommon PUBLIC precompiled.h)
target_include_directories(CemuCommon PUBLIC "../")
target_link_libraries(CemuCommon PRIVATE
CemuCafe
CemuConfig
CemuComponents
Boost::nowide
Boost::filesystem
glm::glm

View file

@ -7,12 +7,23 @@ template <size_t N>
class CafeString // fixed buffer size, null-terminated, PPC char
{
public:
constexpr static size_t Size()
{
return N;
}
// checks whether the string and a null terminator can fit into the buffer
bool CanHoldString(std::string_view sv) const
{
return sv.size() < N;
}
bool assign(std::string_view sv)
{
if (sv.size()+1 >= N)
if (sv.size() >= N)
{
memcpy(data, sv.data(), sv.size()-1);
data[sv.size()-1] = '\0';
memcpy(data, sv.data(), N-1);
data[N-1] = '\0';
return false;
}
memcpy(data, sv.data(), sv.size());
@ -20,11 +31,50 @@ class CafeString // fixed buffer size, null-terminated, PPC char
return true;
}
const char* c_str()
void Copy(CafeString<N>& other)
{
memcpy(data, other.data, N);
}
bool empty() const
{
return data[0] == '\0';
}
const char* c_str() const
{
return (const char*)data;
}
void ClearAllBytes()
{
memset(data, 0, N);
}
auto operator<=>(const CafeString<N>& other) const
{
for (size_t i = 0; i < N; i++)
{
if (data[i] != other.data[i])
return data[i] <=> other.data[i];
if (data[i] == '\0')
return std::strong_ordering::equal;
}
return std::strong_ordering::equal;
}
bool operator==(const CafeString<N>& other) const
{
for (size_t i = 0; i < N; i++)
{
if (data[i] != other.data[i])
return false;
if (data[i] == '\0')
return true;
}
return true;
}
uint8be data[N];
};

View file

@ -116,6 +116,7 @@ GLFUNC(PFNGLPROGRAMUNIFORM1IPROC, glProgramUniform1i)
GLFUNC(PFNGLPROGRAMUNIFORM2IPROC, glProgramUniform2i)
GLFUNC(PFNGLPROGRAMUNIFORM1IVPROC, glProgramUniform1iv)
GLFUNC(PFNGLPROGRAMUNIFORM4IVPROC, glProgramUniform4iv)
GLFUNC(PFNGLPROGRAMUNIFORM1IPROC, glProgramUniform1f)
GLFUNC(PFNGLPROGRAMUNIFORM1FVPROC, glProgramUniform1fv)
GLFUNC(PFNGLPROGRAMUNIFORM2FVPROC, glProgramUniform2fv)

View file

@ -1,5 +1,9 @@
#pragma once
#include <type_traits>
#include <cstddef>
#include <vector>
uint32 coreinit_allocFromSysArea(uint32 size, uint32 alignment);
class SysAllocatorBase;

View file

@ -1,4 +1,3 @@
#ifdef __GNUC__
#define ATTRIBUTE_AVX2 __attribute__((target("avx2")))
#define ATTRIBUTE_SSE41 __attribute__((target("sse4.1")))
@ -9,6 +8,8 @@
#define ATTRIBUTE_AESNI
#endif
#include <string>
class CPUFeaturesImpl
{
public:

View file

@ -31,11 +31,8 @@ endif()
target_include_directories(CemuAudio PUBLIC "../")
target_link_libraries(CemuAudio PRIVATE
CemuCafe
CemuGui
CemuCommon
CemuConfig
CemuUtil
CemuGui
)
if(ENABLE_CUBEB)

View file

@ -6,6 +6,7 @@
#include "config/ActiveSettings.h"
#include "config/LaunchSettings.h"
#include "util/helpers/helpers.h"
#include "Cafe/HW/Latte/Core/Latte.h"
void ActiveSettings::SetPaths(bool isPortableMode,
const fs::path& executablePath,
@ -112,6 +113,18 @@ GraphicAPI ActiveSettings::GetGraphicsAPI()
return api;
}
float ActiveSettings::GetTVGamma()
{
const auto& config = GetConfig();
return config.overrideGammaValue.GetValue() + LatteGPUState.tvGamma * !config.overrideAppGammaPreference.GetValue();
}
float ActiveSettings::GetDRCGamma()
{
const auto& config = GetConfig();
return config.overrideGammaValue.GetValue() + LatteGPUState.drcGamma * !config.overrideAppGammaPreference.GetValue();
}
bool ActiveSettings::AudioOutputOnlyAux()
{
return s_audio_aux_only;

View file

@ -96,6 +96,10 @@ public:
[[nodiscard]] static bool WaitForGX2DrawDoneEnabled();
[[nodiscard]] static GraphicAPI GetGraphicsAPI();
// gamma
[[nodiscard]] static float GetTVGamma();
[[nodiscard]] static float GetDRCGamma();
// audio
[[nodiscard]] static bool AudioOutputOnlyAux();
static void EnableAudioOnlyAux(bool state);

View file

@ -16,11 +16,7 @@ set_property(TARGET CemuConfig PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<C
target_include_directories(CemuConfig PUBLIC "../")
target_link_libraries(CemuConfig PRIVATE
CemuCafe
CemuCommon
CemuGui
CemuUtil
Boost::headers
Boost::program_options
pugixml::pugixml
Boost::program_options
)

View file

@ -130,6 +130,13 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser)
graphic.get("vkDevice", vk_graphic_device_uuid);
mtl_graphic_device_uuid = graphic.get("mtlDevice", 0);
vsync = graphic.get("VSync", 0);
overrideAppGammaPreference = graphic.get("OverrideAppGammaPreference", false);
overrideGammaValue = graphic.get("OverrideGammaValue", 2.2f);
if(overrideGammaValue < 0)
overrideGammaValue = 2.2f;
userDisplayGamma = graphic.get("UserDisplayGamma", 2.2f);
if(userDisplayGamma < 0)
userDisplayGamma = 2.2f;
gx2drawdone_sync = graphic.get("GX2DrawdoneSync", true);
upscale_filter = graphic.get("UpscaleFilter", kBicubicHermiteFilter);
downscale_filter = graphic.get("DownscaleFilter", kLinearFilter);
@ -351,6 +358,9 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser)
graphic.set("vkDevice", vk_graphic_device_uuid);
graphic.set("mtlDevice", mtl_graphic_device_uuid);
graphic.set("VSync", vsync);
graphic.set("OverrideAppGammaPreference", overrideAppGammaPreference);
graphic.set("OverrideGammaValue", overrideGammaValue);
graphic.set("UserDisplayGamma", userDisplayGamma);
graphic.set("GX2DrawdoneSync", gx2drawdone_sync);
graphic.set("ForceMeshShaders", force_mesh_shaders);
//graphic.set("PrecompiledShaders", precompiled_shaders.GetValue());

View file

@ -440,6 +440,11 @@ struct CemuConfig
ConfigValue<bool> async_compile{ true };
ConfigValue<bool> force_mesh_shaders{ false };
// Gamma
ConfigValue<bool> overrideAppGammaPreference{ false };
ConfigValue<float> overrideGammaValue{ 2.2f };
ConfigValue<float> userDisplayGamma { 2.2f }; // 0 = sRGB, >0 gamma
ConfigValue<bool> vk_accurate_barriers{ true };
struct

View file

@ -4,6 +4,9 @@
#include <mutex>
#include <atomic>
#include <shared_mutex>
#include <cassert>
#include <string>
template<typename TType>
class ConfigValueAtomic

View file

@ -124,7 +124,7 @@ AudioDebuggerWindow::AudioDebuggerWindow(wxFrame& parent)
sizer->Add(voiceListbox, 1, wxEXPAND | wxBOTTOM, 0);
voiceListbox->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(AudioDebuggerWindow::OnVoiceListRightClick), NULL, this);
voiceListbox->Bind(wxEVT_RIGHT_DOWN, &AudioDebuggerWindow::OnVoiceListRightClick, this);
mainPane->SetSizer(sizer);

View file

@ -1,4 +1,4 @@
add_library(CemuWxGui
add_library(CemuWxGui STATIC
canvas/IRenderCanvas.h
canvas/OpenGLCanvas.cpp
canvas/OpenGLCanvas.h
@ -76,8 +76,6 @@ add_library(CemuWxGui
input/InputAPIAddWindow.h
input/InputSettings2.cpp
input/InputSettings2.h
input/PairingDialog.cpp
input/PairingDialog.h
input/panels/ClassicControllerInputPanel.cpp
input/panels/ClassicControllerInputPanel.h
input/panels/InputPanel.cpp
@ -118,11 +116,18 @@ add_library(CemuWxGui
wxHelper.h
)
if(ENABLE_METAL)
target_sources(CemuWxGui PRIVATE
canvas/MetalCanvas.cpp
canvas/MetalCanvas.h
)
if (ENABLE_METAL)
target_sources(CemuWxGui PRIVATE
canvas/MetalCanvas.cpp
canvas/MetalCanvas.h
)
endif()
if (ENABLE_BLUEZ)
target_sources(CemuWxGui PRIVATE
input/PairingDialog.cpp
input/PairingDialog.h
)
endif()
set_property(TARGET CemuWxGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
@ -133,20 +138,16 @@ target_include_directories(CemuWxGui PUBLIC "../")
target_include_directories(CemuWxGui PUBLIC ${RAPIDJSON_INCLUDE_DIRS})
target_link_libraries(CemuWxGui PRIVATE
CemuAudio
CemuCafe
CemuCommon
CemuComponents
CemuConfig
CemuInput
CemuResource
CemuUtil
Boost::headers
CURL::libcurl
libzip::zip
OpenSSL::Crypto
pugixml::pugixml
ZArchive::zarchive
CemuCommon
CemuResource
libzip::zip
ZArchive::zarchive
CemuComponents
SDL2::SDL2
pugixml::pugixml
CemuCafe
PUBLIC
CURL::libcurl
)
if(ENABLE_WXWIDGETS AND UNIX AND NOT APPLE)
@ -168,7 +169,7 @@ if(UNIX AND NOT APPLE)
endif()
if (ENABLE_WXWIDGETS)
# PUBLIC because wx/app.h is included in CemuApp.h
target_link_libraries(CemuWxGui PUBLIC wx::base wx::core wx::gl wx::propgrid wx::xrc)
target_link_libraries(CemuWxGui PUBLIC wxWidgets::wxWidgets)
endif()
if(WIN32)

View file

@ -63,7 +63,7 @@ void unused_translation_dummy()
}
#if BOOST_OS_WINDOWS
#include <shlobj_core.h>
#include <shlobj.h>
fs::path GetAppDataRoamingPath()
{
PWSTR path = nullptr;
@ -295,6 +295,10 @@ bool CemuApp::OnInit()
{
MSWEnableDarkMode(DarkMode_Always);
}
// extend tooltip duration to the maximum possible value
wxToolTip::SetDelay(-1);
wxToolTip::SetAutoPop(MAKELPARAM(std::numeric_limits<short>::max(),0));
#endif
for (auto&& path : failedWriteAccess)

View file

@ -590,16 +590,16 @@ void CemuUpdateWindow::OnClose(wxCloseEvent& event)
if (m_restartRequired && !m_restartFile.empty() && fs::exists(m_restartFile))
{
PROCESS_INFORMATION pi{};
STARTUPINFO si{};
STARTUPINFOW si{};
si.cb = sizeof(si);
std::wstring cmdline = GetCommandLineW();
const auto index = cmdline.find('"', 1);
cemu_assert_debug(index != std::wstring::npos);
cmdline = L"\"" + m_restartFile.wstring() + L"\"" + cmdline.substr(index + 1);
cmdline = L"\"" + boost::nowide::widen(_pathToUtf8(m_restartFile)) + L"\"" + cmdline.substr(index + 1);
HANDLE lock = CreateMutex(nullptr, TRUE, L"Global\\cemu_update_lock");
CreateProcess(nullptr, (wchar_t*)cmdline.c_str(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
HANDLE lock = CreateMutexW(nullptr, TRUE, L"Global\\cemu_update_lock");
CreateProcessW(nullptr, (wchar_t*)cmdline.c_str(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
exit(0);
}

View file

@ -28,7 +28,7 @@ size_t DownloadGraphicPacksWindow::curlDownloadFile_writeData(void *ptr, size_t
return writeSize;
}
int DownloadGraphicPacksWindow::progress_callback(curlDownloadFileState_t* downloadState, double dltotal, double dlnow, double ultotal, double ulnow)
int DownloadGraphicPacksWindow::progress_callback(curlDownloadFileState_t* downloadState, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
if (dltotal > 1.0)
downloadState->progress = dlnow / dltotal;
@ -47,7 +47,7 @@ bool DownloadGraphicPacksWindow::curlDownloadFile(const char *url, curlDownloadF
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlDownloadFile_writeData);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, downloadState);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, downloadState);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);

View file

@ -1,10 +1,12 @@
#pragma once
#include <atomic>
#include <curl/system.h>
#include <thread>
#include <string>
#include <memory>
#include <wx/dialog.h>
#include <wx/timer.h>
#include <wx/gauge.h>
@ -55,6 +57,6 @@ private:
std::unique_ptr<curlDownloadFileState_t> m_downloadState;
static size_t curlDownloadFile_writeData(void* ptr, size_t size, size_t nmemb, curlDownloadFileState_t* downloadState);
static int progress_callback(curlDownloadFileState_t* downloadState, double dltotal, double dlnow, double ultotal, double ulnow);
static int progress_callback(curlDownloadFileState_t* downloadState, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
static bool curlDownloadFile(const char* url, curlDownloadFileState_t* downloadState);
};

View file

@ -400,6 +400,55 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
}
{
auto box = new wxStaticBox(graphics_panel, wxID_ANY, _("Gamma settings"));
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
auto row = new wxFlexGridSizer(0, 2, 0, 0);
row->SetFlexibleDirection(wxBOTH);
row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
auto targetGammaLabel = new wxStaticText(box, wxID_ANY, _("Target Gamma"));
row->Add(targetGammaLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_overrideGammaValue = new wxSpinCtrlDouble(box, wxID_ANY, "2.2f", wxDefaultPosition, {230, -1}, wxSP_ARROW_KEYS, 0.1f, 4.0f, 2.2f, 0.1f);
row->Add(m_overrideGammaValue, 0, wxALL, 5);
auto targetGammaTooltip = _("The display gamma to reproduce\nIf you are unsure, set this to 2.2");
targetGammaLabel->SetToolTip(targetGammaTooltip);
m_overrideGammaValue->SetToolTip(targetGammaTooltip);
auto displayGammaLabel = new wxStaticText(box, wxID_ANY, _("Display Gamma"));
row->Add(displayGammaLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
wxBoxSizer* srgbCheckBoxSizer = new wxBoxSizer(wxHORIZONTAL);
row->Add(srgbCheckBoxSizer);
m_userDisplayGamma = new wxSpinCtrlDouble(box, wxID_ANY, "2.2f", wxDefaultPosition, {230, -1}, wxSP_ARROW_KEYS, 0.1f, 4.0f, 2.2f, 0.1f);
auto displayGammaTooltip = _("The gamma of your monitor\nIf you are unsure, set this to 2.2");
m_userDisplayGamma->SetToolTip(displayGammaTooltip);
displayGammaLabel->SetToolTip(displayGammaTooltip);
m_userDisplayisSRGB = new wxCheckBox(box, wxID_ANY, "sRGB", wxDefaultPosition, wxDefaultSize);
m_userDisplayisSRGB->SetToolTip(_("Select this if Cemu is being displayed using a piecewise sRGB gamma curve.\n"
"This is typically not the case so you can probably leave this unchecked.\n"
"Exceptions include HDR displays (with HDR enabled), calibrated SDR displays with Windows 11's Auto Color Management enabled, "
"or when using a display profile with a VCGT tag that targets piecewise sRGB.\n"
"When this box is selected Cemu will compensate for the piecewise curve to approximate the pure gamma curve of a TV.\n"
"Colors will be more accurate, especially in dark scenes, but this may result in banding or crushed shadows, "
"so it is best if you display Cemu with pure gamma and do not use this setting."));
m_userDisplayisSRGB->Bind(wxEVT_CHECKBOX, &GeneralSettings2::OnUserDisplaySRGBSelected, this);
srgbCheckBoxSizer->Add(m_userDisplayGamma, 0, wxALL, 5);
srgbCheckBoxSizer->Add(m_userDisplayisSRGB, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
row->Add(new wxStaticText(box, wxID_ANY, _("Override Gamma")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_overrideGamma = new wxCheckBox(box, wxID_ANY, "", wxDefaultPosition, {230, -1});
m_overrideGamma->SetToolTip(_("Ignore title's gamma preference"));
row->Add(m_overrideGamma, 0, wxALL, 5);
box_sizer->Add(row, 0, wxEXPAND, 5);
graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
}
{
wxString choices[] = { _("Bilinear"), _("Bicubic"), _("Hermite"), _("Nearest Neighbor") };
m_upscale_filter = new wxRadioBox(graphics_panel, wxID_ANY, _("Upscale filter"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 5, wxRA_SPECIFY_COLS);
@ -1171,6 +1220,9 @@ void GeneralSettings2::StoreConfig()
config.vsync = m_vsync->GetSelection();
config.overrideAppGammaPreference = m_overrideGamma->IsChecked();
config.overrideGammaValue = m_overrideGammaValue->GetValue();
config.userDisplayGamma = m_userDisplayGamma->GetValue() * !m_userDisplayisSRGB->GetValue();
config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked();
config.force_mesh_shaders = m_force_mesh_shaders->IsChecked();
config.async_compile = m_async_compile->IsChecked();
@ -1789,6 +1841,15 @@ void GeneralSettings2::ApplyConfig()
// graphics
m_graphic_api->SetSelection(config.graphic_api);
m_vsync->SetSelection(config.vsync);
m_overrideGamma->SetValue(config.overrideAppGammaPreference);
m_overrideGammaValue->SetValue(config.overrideGammaValue);
m_userDisplayisSRGB->SetValue(config.userDisplayGamma == 0.0f);
m_userDisplayGamma->SetValue(config.userDisplayGamma);
if(m_userDisplayisSRGB->GetValue())
{
m_userDisplayGamma->Disable();
m_userDisplayGamma->SetValue(2.2f);
}
m_async_compile->SetValue(config.async_compile);
m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync);
m_force_mesh_shaders->SetValue(config.force_mesh_shaders);
@ -2148,6 +2209,15 @@ void GeneralSettings2::OnGraphicAPISelected(wxCommandEvent& event)
HandleGraphicsApiSelection();
}
void GeneralSettings2::OnUserDisplaySRGBSelected(wxCommandEvent& event)
{
m_userDisplayGamma->SetValue(2.2f);
if(event.GetInt())
m_userDisplayGamma->Disable();
else
m_userDisplayGamma->Enable();
}
void GeneralSettings2::OnAddPathClicked(wxCommandEvent& event)
{
wxDirDialog path_dialog(this, _("Select a directory containing games."), wxEmptyString, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);

View file

@ -56,6 +56,11 @@ private:
// Graphics
wxChoice* m_graphic_api, * m_graphic_device;
wxChoice* m_vsync;
wxCheckBox* m_overrideGamma;
wxSpinCtrlDouble* m_overrideGammaValue;
wxSpinCtrlDouble* m_userDisplayGamma;
wxCheckBox* m_userDisplayisSRGB;
wxCheckBox *m_async_compile, *m_gx2drawdone_sync, *m_force_mesh_shaders;
wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling;
wxChoice* m_overlay_position, *m_notification_position, *m_overlay_scale, *m_notification_scale;
@ -97,6 +102,7 @@ private:
void OnAudioDeviceSelected(wxCommandEvent& event);
void OnAudioChannelsSelected(wxCommandEvent& event);
void OnGraphicAPISelected(wxCommandEvent& event);
void OnUserDisplaySRGBSelected(wxCommandEvent& event);
void OnAddPathClicked(wxCommandEvent& event);
void OnRemovePathClicked(wxCommandEvent& event);
void OnActiveAccountChanged(wxCommandEvent& event);

View file

@ -86,6 +86,7 @@ enum
MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER,
MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER,
MAINFRAME_MENU_ID_FILE_OPEN_SHADERCACHE_FOLDER,
MAINFRAME_MENU_ID_FILE_CLEAR_SPOTPASS_CACHE,
MAINFRAME_MENU_ID_FILE_EXIT,
MAINFRAME_MENU_ID_FILE_END_EMULATION,
MAINFRAME_MENU_ID_FILE_RECENT_0,
@ -184,6 +185,7 @@ EVT_MENU(MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, MainWindow::OnInstallUpdate)
EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, MainWindow::OnOpenFolder)
EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, MainWindow::OnOpenFolder)
EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_SHADERCACHE_FOLDER, MainWindow::OnOpenFolder)
EVT_MENU(MAINFRAME_MENU_ID_FILE_CLEAR_SPOTPASS_CACHE, MainWindow::OnClearSpotPassCache)
EVT_MENU(MAINFRAME_MENU_ID_FILE_EXIT, MainWindow::OnFileExit)
EVT_MENU(MAINFRAME_MENU_ID_FILE_END_EMULATION, MainWindow::OnFileMenu)
EVT_MENU_RANGE(MAINFRAME_MENU_ID_FILE_RECENT_0 + 0, MAINFRAME_MENU_ID_FILE_RECENT_LAST, MainWindow::OnFileMenu)
@ -703,8 +705,15 @@ void MainWindow::OnOpenFolder(wxCommandEvent& event)
wxLaunchDefaultApplication(wxHelper::FromPath(ActiveSettings::GetMlcPath()));
else if (id == MAINFRAME_MENU_ID_FILE_OPEN_SHADERCACHE_FOLDER)
wxLaunchDefaultApplication(wxHelper::FromPath(ActiveSettings::GetCachePath("shaderCache")));
}
void MainWindow::OnClearSpotPassCache(wxCommandEvent& event)
{
fs::path bossPath = ActiveSettings::GetMlcPath("usr/boss");
fs::remove_all(bossPath);
// recreate usr/boss/
fs::create_directory(bossPath);
wxMessageBox(_("SpotPass cache cleared"));
}
void MainWindow::OnInstallUpdate(wxCommandEvent& event)
@ -865,7 +874,7 @@ void MainWindow::OpenSettings()
const bool mlc_modified = frame.MLCModified();
if (paths_modified)
m_game_list->ReloadGameEntries(false);
m_game_list->ReloadGameEntries();
else
SaveSettings();
@ -1010,7 +1019,7 @@ void MainWindow::OnConsoleLanguage(wxCommandEvent& event)
if (m_game_list)
{
m_game_list->DeleteCachedStrings();
m_game_list->ReloadGameEntries(false);
m_game_list->ReloadGameEntries();
}
GetConfigHandle().Save();
}
@ -1456,7 +1465,7 @@ void MainWindow::OnAccountListRefresh(wxCommandEvent& event)
void MainWindow::OnRequestGameListRefresh(wxCommandEvent& event)
{
m_game_list->ReloadGameEntries(false);
m_game_list->ReloadGameEntries();
}
void MainWindow::OnSetWindowTitle(wxCommandEvent& event)
@ -2128,7 +2137,8 @@ void MainWindow::RecreateMenu()
m_loadMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_LOAD, _("&Load..."));
m_installUpdateMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, _("&Install game title, update or DLC..."));
sint32 recentFileIndex = 0;
wxMenu* recentMenu = new wxMenu();
sint32 recentFileIndex = 1;
m_fileMenuSeparator0 = nullptr;
m_fileMenuSeparator1 = nullptr;
for (size_t i = 0; i < guiConfig.recent_launch_files.size(); i++)
@ -2136,15 +2146,21 @@ void MainWindow::RecreateMenu()
const std::string& pathStr = guiConfig.recent_launch_files[i];
if (pathStr.empty())
continue;
if (recentFileIndex == 0)
m_fileMenuSeparator0 = m_fileMenu->AppendSeparator();
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_RECENT_0 + i, to_wxString(fmt::format("{}. {}", recentFileIndex, pathStr)));
recentMenu->Append(MAINFRAME_MENU_ID_FILE_RECENT_0 + i, to_wxString(fmt::format("{}. {}", recentFileIndex, pathStr)));
recentFileIndex++;
if (recentFileIndex >= 8)
if (recentFileIndex >= 10)
break;
}
m_fileMenuSeparator1 = m_fileMenu->AppendSeparator();
if (recentFileIndex == 0)
{
wxMenuItem* placeholder = recentMenu->Append(wxID_NONE, _("(No recent files)"));
placeholder->Enable(false);
}
m_fileMenu->AppendSeparator();
m_fileMenu->AppendSubMenu(recentMenu, _("Recent files"));
m_fileMenu->AppendSeparator();
}
else
{
@ -2154,11 +2170,14 @@ void MainWindow::RecreateMenu()
#endif
}
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, _("&Open Cemu folder"));
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, _("&Open MLC folder"));
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, _("Open Cemu folder"));
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, _("Open MLC folder"));
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_SHADERCACHE_FOLDER, _("Open &shader cache folder"));
m_fileMenu->AppendSeparator();
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_CLEAR_SPOTPASS_CACHE, _("Clear Spot&Pass cache"), wxEmptyString);
if (m_game_launched)
m_fileMenu->Enable(MAINFRAME_MENU_ID_FILE_CLEAR_SPOTPASS_CACHE, false);
m_fileMenu->AppendSeparator();
m_exitMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_EXIT, _("&Exit"));
m_menuBar->Append(m_fileMenu, _("&File"));
// options->account submenu

View file

@ -94,6 +94,7 @@ public:
void OnClose(wxCloseEvent& event);
void OnFileMenu(wxCommandEvent& event);
void OnOpenFolder(wxCommandEvent& event);
void OnClearSpotPassCache(wxCommandEvent& event);
void OnLaunchFromFile(wxLaunchGameEvent& event);
void OnInstallUpdate(wxCommandEvent& event);
void OnFileExit(wxCommandEvent& event);

View file

@ -382,7 +382,7 @@ void MemorySearcherTool::OnEntryListRightClick(wxDataViewEvent& event)
//mnu.SetClientData(data);
mnu.Append(LIST_ENTRY_ADD, _("&Add new entry"))->Enable(false);
mnu.Append(LIST_ENTRY_REMOVE, _("&Remove entry"));
mnu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemorySearcherTool::OnPopupClick), nullptr, this);
mnu.Bind(wxEVT_COMMAND_MENU_SELECTED, &MemorySearcherTool::OnPopupClick, this);
PopupMenu(&mnu);
}

View file

@ -3,6 +3,7 @@
#include <mutex>
#include <thread>
#include <atomic>
#include <future>
#include <wx/listctrl.h>

View file

@ -66,9 +66,9 @@ void _stripPathFilename(fs::path& path)
path = path.parent_path();
}
std::list<fs::path> _getCachesPaths(const TitleId& titleId)
std::vector<fs::path> _getCachesPaths(const TitleId& titleId)
{
std::list<fs::path> cachePaths{
std::vector<fs::path> cachePaths{
ActiveSettings::GetCachePath(L"shaderCache/driver/vk/{:016x}.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_spirv.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_gl.bin", titleId),
@ -78,12 +78,12 @@ std::list<fs::path> _getCachesPaths(const TitleId& titleId)
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_vkpipeline.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_mtlpipeline.bin", titleId)};
cachePaths.remove_if(
[](const fs::path& cachePath)
{
std::error_code ec;
return !fs::exists(cachePath, ec);
});
cachePaths.erase(std::remove_if(cachePaths.begin(), cachePaths.end(),
[](const fs::path& cachePath) {
std::error_code ec;
return !fs::exists(cachePath, ec);
}),
cachePaths.end());
return cachePaths;
}
@ -144,17 +144,15 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
const auto& config = GetWxGUIConfig();
char transparent_bitmap[kIconWidth * kIconWidth * 4] = {};
memset((void*)transparent_bitmap, wxSystemSettings::GetAppearance().IsDark() ? 0xFF : 0x00, sizeof(transparent_bitmap));
memset(transparent_bitmap, wxSystemSettings::GetAppearance().IsDark() ? 0xFF : 0x00, sizeof(transparent_bitmap));
wxBitmap blank(transparent_bitmap, kIconWidth, kIconWidth);
m_image_list_data = {};
m_image_list_data.emplace_back(wxBitmapBundle::FromBitmap(blank));
wxListCtrl::SetNormalImages(m_image_list_data);
m_image_list_data.Add(blank);
wxListCtrl::SetImageList(&m_image_list_data, wxIMAGE_LIST_NORMAL);
m_image_list_small_data = {};
wxBitmap::Rescale(blank, {kListIconWidth, kListIconWidth});
m_image_list_small_data.emplace_back(wxBitmapBundle::FromBitmap(blank));
wxListCtrl::SetSmallImages(m_image_list_small_data);
m_image_list_small_data.Add(blank);
wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_SMALL);
InsertColumn(ColumnHiddenName, "", wxLIST_FORMAT_LEFT, 0);
if(config.show_icon_column)
@ -305,12 +303,6 @@ int wxGameList::GetColumnDefaultWidth(int column)
switch (column)
{
case ColumnIcon:
#if __WXMSW__
// note: this is another workaround that could be used to fix the icon offset, but instead of this there's a vcpkg patch for wxWidgets that fixes the icon offset
// wxWidgets offsets the icon in the REPORT view so it's cut off if the column width is set to 64px, see https://github.com/wxWidgets/wxWidgets/blob/09f433faf39aab3f25b3c564b82448bb845fae56/src/msw/listctrl.cpp#L3091
// so add 6px to the column width to compensate, and add another 6px to the right side so that it has equal whitespace on both sides
// return kListIconWidth + 6 + 6;
#endif
return kListIconWidth;
case ColumnName:
return DefaultColumnSize::name;
@ -353,7 +345,7 @@ bool wxGameList::IsVisible(long item) const
return visible;
}
void wxGameList::ReloadGameEntries(bool cached)
void wxGameList::ReloadGameEntries()
{
wxWindowUpdateLocker windowlock(this);
DeleteAllItems();
@ -413,11 +405,13 @@ void wxGameList::SetStyle(Style style, bool save)
switch(style)
{
case Style::kIcons:
wxListCtrl::SetNormalImages(m_image_list_data);
wxListCtrl::SetImageList(&m_image_list_data, wxIMAGE_LIST_NORMAL);
break;
case Style::kSmallIcons:
wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_NORMAL);
break;
case Style::kList:
wxListCtrl::SetSmallImages(m_image_list_small_data);
wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_SMALL);
break;
}
@ -446,7 +440,7 @@ long wxGameList::GetStyleFlags(Style style) const
switch (style)
{
case Style::kList:
return (wxLC_SINGLE_SEL | wxLC_REPORT);
return (wxLC_SINGLE_SEL | wxLC_VRULES | wxLC_REPORT);
case Style::kIcons:
return (wxLC_SINGLE_SEL | wxLC_ICON);
case Style::kSmallIcons:
@ -461,25 +455,22 @@ void wxGameList::UpdateItemColors(sint32 startIndex)
{
wxWindowUpdateLocker lock(this);
wxColour bgColourPrimary = GetBackgroundColour();
wxColour bgColourSecondary = wxHelper::CalculateAccentColour(bgColourPrimary);
for (int i = startIndex; i < GetItemCount(); ++i)
{
const auto titleId = (uint64)GetItemData(i);
const uint64 titleId = GetItemData(i);
if (GetConfig().IsGameListFavorite(titleId))
{
SetItemBackgroundColour(i, kFavoriteColor);
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
else if ((i&1) != 0)
else if ((i % 2) != 0)
{
SetItemBackgroundColour(i, bgColourPrimary);
SetItemBackgroundColour(i, kPrimaryColor);
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
else
{
SetItemBackgroundColour(i, bgColourSecondary);
SetItemBackgroundColour(i, kAlternateColor);
SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
}
@ -487,7 +478,7 @@ void wxGameList::UpdateItemColors(sint32 startIndex)
static inline int order_to_int(const std::weak_ordering &wo)
{
// no easy conversion seems to exists in C++20
// no easy conversion seems to exist in C++20
if (wo == std::weak_ordering::less)
return -1;
else if (wo == std::weak_ordering::greater)
@ -499,27 +490,24 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2,
{
auto titleLastPlayed = [](uint64_t id)
{
iosu::pdm::GameListStat playTimeStat{};
iosu::pdm::GetStatForGamelist(id, playTimeStat);
return playTimeStat;
iosu::pdm::GameListStat playTimeStat{};
iosu::pdm::GetStatForGamelist(id, playTimeStat);
return playTimeStat;
};
auto titlePlayMinutes = [](uint64_t id)
{
iosu::pdm::GameListStat playTimeStat;
if (!iosu::pdm::GetStatForGamelist(id, playTimeStat))
return 0u;
return playTimeStat.numMinutesPlayed;
iosu::pdm::GameListStat playTimeStat;
if (!iosu::pdm::GetStatForGamelist(id, playTimeStat))
return 0u;
return playTimeStat.numMinutesPlayed;
};
auto titleRegion = [](uint64_t id)
{
return CafeTitleList::GetGameInfo(id).GetRegion();
return CafeTitleList::GetGameInfo(id).GetRegion();
};
if (!sortData->asc)
std::swap(titleId1, titleId2);
switch(sortData->column)
{
default:
@ -548,7 +536,7 @@ std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2,
int wxGameList::SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
{
const auto sort_data = (SortData*)sortData;
return order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data));
return sort_data->dir * order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data));
}
void wxGameList::SortEntries(int column)
@ -572,7 +560,7 @@ void wxGameList::SortEntries(int column)
case ColumnRegion:
case ColumnTitleID:
{
SortData data{this, ItemColumns{column}, ascending};
SortData data{this, ItemColumns{column}, ascending ? 1 : -1};
SortItems(SortFunction, (wxIntPtr)&data);
ShowSortIndicator(column, ascending);
break;
@ -843,8 +831,8 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event)
int icon_small;
if (!QueryIconForTitle(title_id, icon_large, icon_small))
break;
auto icon = m_image_list_data[icon_large];
auto newClipboardData = wxBitmapDataObject(icon.GetBitmap(icon.GetDefaultSize()));
auto icon = m_image_list_data.GetIcon(icon_large);
auto newClipboardData = wxBitmapDataObject(icon);
wxClipboard::Get()->SetData(&newClipboardData);
wxClipboard::Get()->Close();
}
@ -857,7 +845,7 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event)
switch (event.GetId())
{
case kContextMenuRefreshGames:
ReloadGameEntries(false);
ReloadGameEntries();
break;
case kContextMenuStyleList:
SetStyle(Style::kList);
@ -1005,7 +993,7 @@ void wxGameList::ApplyGameListColumnWidths()
const auto& config = GetWxGUIConfig();
wxWindowUpdateLocker lock(this);
if(config.show_icon_column)
SetColumnWidth(ColumnIcon, GetColumnDefaultWidth(ColumnIcon));
SetColumnWidth(ColumnIcon, kListIconWidth);
else
SetColumnWidth(ColumnIcon, 0);
SetColumnWidth(ColumnName, config.column_width.name);
@ -1120,19 +1108,6 @@ void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event)
TitleId baseTitleId = gameInfo.GetBaseTitleId();
bool isNewEntry = false;
if (m_style == Style::kIcons)
{
wxListCtrl::SetNormalImages(m_image_list_data);
}
else if (m_style == Style::kSmallIcons)
{
wxListCtrl::SetSmallImages(m_image_list_small_data);
}
else if (m_style == Style::kList)
{
wxListCtrl::SetSmallImages(m_image_list_small_data);
}
int icon = -1; /* 0 is the default empty icon */
int icon_small = -1; /* 0 is the default empty icon */
QueryIconForTitle(baseTitleId, icon, icon_small);
@ -1283,7 +1258,7 @@ void wxGameList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt)
}
}
void wxGameList::RemoveCache(const std::list<fs::path>& cachePaths, const std::string& titleName)
void wxGameList::RemoveCache(const std::vector<fs::path>& cachePaths, const std::string& titleName)
{
wxMessageDialog dialog(this, formatWxString(_("Remove the shader caches for {}?"), titleName), _("Remove shader caches"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION);
dialog.SetYesNoLabels(_("Yes"), _("No"));
@ -1291,7 +1266,7 @@ void wxGameList::RemoveCache(const std::list<fs::path>& cachePaths, const std::s
const auto dialogResult = dialog.ShowModal();
if (dialogResult != wxID_YES)
return;
std::list<std::string> errs;
std::vector<std::string> errs;
for (const fs::path& cachePath : cachePaths)
{
if (std::error_code ec; !fs::remove(cachePath, ec))
@ -1348,18 +1323,17 @@ void wxGameList::AsyncWorkerThread()
wxMemoryInputStream tmp_stream(tgaData->data(), tgaData->size());
const wxImage image(tmp_stream);
// todo - is wxImageList thread safe?
m_image_list_data.emplace_back(image.Scale(kIconWidth, kIconWidth, wxIMAGE_QUALITY_BICUBIC));
m_image_list_small_data.emplace_back(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC));
int icon = m_image_list_data.Add(image.Scale(kIconWidth, kIconWidth, wxIMAGE_QUALITY_BICUBIC));
int icon_small = m_image_list_small_data.Add(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC));
// store in cache
m_icon_cache_mtx.lock();
m_icon_cache.try_emplace(titleId, m_image_list_data.size() - 1, m_image_list_small_data.size() - 1);
m_icon_cache.try_emplace(titleId, icon, icon_small);
m_icon_cache_mtx.unlock();
iconSuccessfullyLoaded = true;
}
else
{
cemuLog_log(LogType::Force, "Failed to load icon for title {:016x}", titleId);
cemu_assert_debug(false);
}
titleInfo.Unmount(tempMountPath);
// notify UI about loaded icon
@ -1418,9 +1392,9 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
// Obtain and convert icon
[&]()
{
int iconIndex, smallIconIndex;
int iconIdx, smallIconIdx;
if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex))
if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx))
{
cemuLog_log(LogType::Force, "Icon hasn't loaded");
return;
@ -1436,8 +1410,8 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId());
wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value()));
const auto icon = m_image_list_data[iconIndex];
wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)};
const auto icon = m_image_list_data.GetIcon(iconIdx);
wxBitmap bitmap{icon};
wxImage image = bitmap.ConvertToImage();
wxPNGHandler pngHandler;
if (!pngHandler.SaveFile(&image, pngFileStream, false))
@ -1511,9 +1485,9 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
// Obtain and convert icon
[&]()
{
int iconIndex, smallIconIndex;
int iconIdx, smallIconIdx;
if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex))
if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx))
{
cemuLog_log(LogType::Force, "Icon hasn't loaded");
return;
@ -1529,8 +1503,8 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId());
wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value()));
const auto icon = m_image_list_data[iconIndex];
wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)};
const auto icon = m_image_list_data.GetIcon(iconIdx);
wxBitmap bitmap{icon};
wxImage image = bitmap.ConvertToImage();
wxPNGHandler pngHandler;
if (!pngHandler.SaveFile(&image, pngFileStream, false))
@ -1638,14 +1612,14 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
cemuLog_log(LogType::Force, "Icon hasn't loaded");
return;
}
const auto icon = m_image_list_data[iconIdx];
const auto icon = m_image_list_data.GetIcon(iconIdx);
const auto folder = ActiveSettings::GetUserDataPath("icons");
if (!fs::exists(folder) && !fs::create_directories(folder))
{
cemuLog_log(LogType::Force, "Failed to create icon directory");
return;
}
wxBitmap bitmap{icon.GetBitmap(wxDefaultSize)};
wxBitmap bitmap{icon};
icon_path = folder / fmt::format("{:016x}.ico", titleId);
auto stream = wxFileOutputStream(icon_path->wstring());

View file

@ -3,15 +3,15 @@
#include "config/CemuConfig.h"
#include "Cafe/TitleList/TitleId.h"
#include <future>
#include <mutex>
#include <optional>
#include <wx/listctrl.h>
#include <wx/timer.h>
#include <wx/panel.h>
#include <wx/settings.h>
#include <Cafe/TitleList/GameInfo.h>
#include "wxHelper.h"
#include "util/helpers/Semaphore.h"
class wxTitleIdEvent : public wxCommandEvent
@ -51,7 +51,7 @@ public:
void SaveConfig(bool flush = false);
bool IsVisible(long item) const; // only available in wxwidgets 3.1.3
void ReloadGameEntries(bool cached = false);
void ReloadGameEntries();
void DeleteCachedStrings();
void CreateShortcut(GameInfo2& gameInfo);
@ -66,7 +66,8 @@ private:
const wxColour kUpdateColor{ wxSystemSettings::SelectLightDark(wxColour(195, 57, 57), wxColour(84, 29, 29)) };
const wxColour kFavoriteColor{ wxSystemSettings::SelectLightDark(wxColour(253, 246, 211), wxColour(82, 84, 48)) };
const wxColour kSecondColor{ wxSystemSettings::SelectLightDark(wxColour(242, 249, 253), wxColour(34, 34, 34)) };
const wxColour kPrimaryColor = GetBackgroundColour();
const wxColour kAlternateColor = wxHelper::CalculateAccentColour(kPrimaryColor);
void UpdateItemColors(sint32 startIndex = 0);
enum ItemColumns : int
@ -89,7 +90,7 @@ private:
{
wxGameList* thisptr;
ItemColumns column;
bool asc;
int dir;
};
int FindInsertPosition(TitleId titleId);
@ -114,7 +115,7 @@ private:
void HandleTitleListCallback(struct CafeTitleListCallbackEvent* evt);
void RemoveCache(const std::list<fs::path>& cachePath, const std::string& titleName);
void RemoveCache(const std::vector<fs::path>& cachePath, const std::string& titleName);
void AsyncWorkerThread();
void RequestLoadIconAsync(TitleId titleId);
@ -122,7 +123,8 @@ private:
inline static constexpr int kListIconWidth = 64;
inline static constexpr int kIconWidth = 128;
wxWithImages::Images m_image_list_data, m_image_list_small_data;
wxImageList m_image_list_data = wxImageList(kIconWidth, kIconWidth, false, 1);
wxImageList m_image_list_small_data = wxImageList(kListIconWidth, kListIconWidth, false, 1);
std::mutex m_icon_cache_mtx;
std::set<TitleId> m_icon_loaded;

View file

@ -223,7 +223,7 @@ void BreakpointWindow::OnRightDown(wxMouseEvent& event)
menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)"));
menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)"));
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClick), nullptr, this);
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &BreakpointWindow::OnContextMenuClick, this);
PopupMenu(&menu);
}
else
@ -234,7 +234,7 @@ void BreakpointWindow::OnRightDown(wxMouseEvent& event)
wxMenu menu;
menu.Append(MENU_ID_DELETE_BP, _("Delete breakpoint"));
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(BreakpointWindow::OnContextMenuClickSelected), nullptr, this);
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &BreakpointWindow::OnContextMenuClickSelected, this);
PopupMenu(&menu);
}
}

View file

@ -239,8 +239,8 @@ void HotkeySettings::CreateHotkeyRow(const wxString& label, sHotkeyCfg& cfgHotke
controllerInput->Bind(wxEVT_BUTTON, &HotkeySettings::OnControllerHotkeyInputLeftClick, this);
/* for cancelling and clearing input */
keyInput->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(HotkeySettings::OnKeyboardHotkeyInputRightClick), NULL, this);
controllerInput->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(HotkeySettings::OnControllerHotkeyInputRightClick), NULL, this);
keyInput->Bind(wxEVT_RIGHT_UP, &HotkeySettings::OnKeyboardHotkeyInputRightClick, this);
controllerInput->Bind(wxEVT_RIGHT_UP, &HotkeySettings::OnControllerHotkeyInputRightClick, this);
keyInput->SetMinSize(m_minButtonSize);
controllerInput->SetMinSize(m_minButtonSize);

View file

@ -21,7 +21,9 @@
#include "wxgui/input/InputAPIAddWindow.h"
#include "input/ControllerFactory.h"
#ifdef HAS_BLUEZ
#include "wxgui/input/PairingDialog.h"
#endif
#include "wxgui/input/panels/VPADInputPanel.h"
#include "wxgui/input/panels/ProControllerInputPanel.h"
@ -255,12 +257,14 @@ wxWindow* InputSettings2::initialize_page(size_t index)
page_data.m_controller_api_remove = remove_api;
}
#ifdef HAS_BLUEZ
auto* pairingDialog = new wxButton(page, wxID_ANY, _("Pair Wii/Wii U Controller"));
pairingDialog->Bind(wxEVT_BUTTON, [this](wxEvent&) {
PairingDialog pairing_dialog(this);
pairing_dialog.ShowModal();
});
sizer->Add(pairingDialog, wxGBPosition(5, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5);
#endif
// controller
auto* controller_bttns = new wxBoxSizer(wxHORIZONTAL);

View file

@ -124,7 +124,7 @@ DebugPPCThreadsWindow::DebugPPCThreadsWindow(wxFrame& parent)
sizer->Add(row, 0, wxEXPAND | wxALL, 5);
m_thread_list->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(DebugPPCThreadsWindow::OnThreadListRightClick), nullptr, this);
m_thread_list->Bind(wxEVT_RIGHT_DOWN, &DebugPPCThreadsWindow::OnThreadListRightClick, this);
SetSizer(sizer);
@ -466,7 +466,7 @@ void DebugPPCThreadsWindow::OnThreadListRightClick(wxMouseEvent& event)
menu.AppendSeparator();
menu.Append(THREADLIST_MENU_DUMP_STACK_TRACE, _("Write stack trace to log"));
menu.Append(THREADLIST_MENU_PROFILE_THREAD, _("Profile thread"));
menu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(DebugPPCThreadsWindow::OnThreadListPopupClick), nullptr, this);
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &DebugPPCThreadsWindow::OnThreadListPopupClick, this);
PopupMenu(&menu);
}

View file

@ -124,7 +124,7 @@ TextureRelationViewerWindow::TextureRelationViewerWindow(wxFrame& parent)
sizerBottom->Add(checkboxShowViews, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM | wxTOP | wxLEFT, 10);
checkboxShowViews->SetValue(true);
textureRelationListA->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(TextureRelationViewerWindow::OnTextureListRightClick), NULL, this);
textureRelationListA->Bind(wxEVT_RIGHT_DOWN, &TextureRelationViewerWindow::OnTextureListRightClick, this);
sizer->Add(
sizerBottom,

View file

@ -25,10 +25,9 @@ namespace wxHelper
inline wxColour CalculateAccentColour(const wxColour& bgColour)
{
wxColour bgColourSecondary;
const uint32 bgLightness = (bgColour.GetRed() + bgColour.GetGreen() + bgColour.GetBlue()) / 3;
const bool isDarkTheme = bgLightness < 128;
bgColourSecondary = bgColour.ChangeLightness(isDarkTheme ? 110 : 90); // color for even rows
wxColour bgColourSecondary = bgColour.ChangeLightness(isDarkTheme ? 110 : 90); // color for even rows
// for very light themes we'll use a blue tint to match the older Windows Cemu look
if (bgLightness > 250)
bgColourSecondary = wxColour(bgColour.Red() - 13, bgColour.Green() - 6, bgColour.Blue() - 2);

View file

@ -121,49 +121,49 @@ void wxCheckTree::Init()
// Unchecked
renderer_dc.SelectObject(unchecked_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_NONE);
// Unchecked Mouse Over
renderer_dc.SelectObject(unchecked_mouse_over_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CURRENT);
// Unchecked and Disabled
renderer_dc.SelectObject(unchecked_disabled_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_DISABLED);
// Unchecked Left Down
renderer_dc.SelectObject(unchecked_left_down_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CURRENT | wxCONTROL_PRESSED);
// Checked
renderer_dc.SelectObject(checked_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED);
// Checked Mouse Over
renderer_dc.SelectObject(checked_mouse_over_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED | wxCONTROL_CURRENT);
// Checked Left Down
renderer_dc.SelectObject(checked_left_down_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED | wxCONTROL_CURRENT | wxCONTROL_PRESSED);
// Checked and Disabled
renderer_dc.SelectObject(checked_disabled_bmp);
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour(), wxSOLID));
renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour()));
renderer_dc.Clear();
wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED | wxCONTROL_DISABLED);

View file

@ -31,10 +31,6 @@ target_sources(imguiImpl PRIVATE
target_include_directories(imguiImpl PUBLIC "../../dependencies/imgui/")
target_link_libraries(imguiImpl PRIVATE
CemuCafe
CemuCommon
CemuGui
CemuInput
CemuResource
CemuUtil
)

View file

@ -88,16 +88,8 @@ endif ()
target_include_directories(CemuInput PUBLIC "../")
target_link_libraries(CemuInput PRIVATE
CemuCafe
CemuCommon
CemuConfig
CemuGui
CemuUtil
Boost::headers
Boost::program_options
glm::glm
pugixml::pugixml
SDL2::SDL2
)
if (ENABLE_HIDAPI)

View file

@ -22,9 +22,10 @@ DirectInputController::~DirectInputController()
// TODO: test if really needed
// workaround for gamecube controllers crash on release?
bool should_release_device = true;
if (m_product_guid == GUID{}) {
DIDEVICEINSTANCE info{};
info.dwSize = sizeof(DIDEVICEINSTANCE);
if (m_product_guid == GUID{})
{
DIDEVICEINSTANCEW info{};
info.dwSize = sizeof(DIDEVICEINSTANCEW);
if (SUCCEEDED(m_device->GetDeviceInfo(&info)))
{
m_product_guid = info.guidProduct;
@ -90,8 +91,8 @@ bool DirectInputController::connect()
if (FAILED(hr) || m_device == nullptr)
return false;
DIDEVICEINSTANCE idi{};
idi.dwSize = sizeof(DIDEVICEINSTANCE);
DIDEVICEINSTANCEW idi{};
idi.dwSize = sizeof(DIDEVICEINSTANCEW);
if (SUCCEEDED(m_device->GetDeviceInfo(&idi)))
{
// overwrite guid name with "real" display name
@ -147,8 +148,8 @@ bool DirectInputController::connect()
}
}
DIDEVICEINSTANCE info{};
info.dwSize = sizeof(DIDEVICEINSTANCE);
DIDEVICEINSTANCEW info{};
info.dwSize = sizeof(DIDEVICEINSTANCEW);
if (SUCCEEDED(m_device->GetDeviceInfo(&info)))
{
m_product_guid = info.guidProduct;
@ -157,7 +158,7 @@ bool DirectInputController::connect()
std::fill(m_min_axis.begin(), m_min_axis.end(), 0);
std::fill(m_max_axis.begin(), m_max_axis.end(), std::numeric_limits<uint16>::max());
m_device->EnumObjects(
[](LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) -> BOOL
[](LPCDIDEVICEOBJECTINSTANCEW lpddoi, LPVOID pvRef) -> BOOL
{
auto* thisptr = (DirectInputController*)pvRef;

View file

@ -42,7 +42,7 @@ private:
GUID m_product_guid{};
std::shared_mutex m_mutex;
Microsoft::WRL::ComPtr<IDirectInputDevice8> m_device;
Microsoft::WRL::ComPtr<IDirectInputDevice8W> m_device;
Microsoft::WRL::ComPtr<IDirectInputEffect> m_effect;
std::array<LONG, 6> m_min_axis{};

View file

@ -4,34 +4,16 @@
DirectInputControllerProvider::DirectInputControllerProvider()
{
/*m_module = LoadLibraryA("dinput8.dll");
if (!m_module)
throw std::runtime_error("can't load any xinput dll");
m_DirectInput8Create = (decltype(&DirectInput8Create))GetProcAddress(m_module, "DirectInput8Create");
m_GetdfDIJoystick = (decltype(&GetdfDIJoystick))GetProcAddress(m_module, "GetdfDIJoystick");
if (!m_DirectInput8Create)
{
FreeLibrary(m_module);
throw std::runtime_error("can't find the DirectInput8Create export");
}*/
const auto r = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_dinput8, nullptr);
const auto r = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8W, (void**)&m_dinput8, nullptr);
if (FAILED(r))
{
const auto error = GetLastError();
//FreeLibrary(m_module);
throw std::runtime_error(fmt::format("can't create direct input object (error: {:#x})", error));
}
}
DirectInputControllerProvider::~DirectInputControllerProvider()
{
/*if (m_module)
FreeLibrary(m_module);
*/
}
std::vector<std::shared_ptr<ControllerBase>> DirectInputControllerProvider::get_controllers()
@ -39,7 +21,7 @@ std::vector<std::shared_ptr<ControllerBase>> DirectInputControllerProvider::get_
std::vector<std::shared_ptr<ControllerBase>> result;
m_dinput8->EnumDevices(DI8DEVCLASS_GAMECTRL,
[](LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) -> BOOL
[](LPCDIDEVICEINSTANCEW lpddi, LPVOID pvRef) -> BOOL
{
auto* controllers = (decltype(&result))pvRef;
@ -55,8 +37,5 @@ std::vector<std::shared_ptr<ControllerBase>> DirectInputControllerProvider::get_
LPCDIDATAFORMAT DirectInputControllerProvider::get_data_format() const
{
/*if (m_GetdfDIJoystick)
return m_GetdfDIJoystick();*/
return GetdfDIJoystick();
}

View file

@ -23,7 +23,7 @@ public:
std::vector<std::shared_ptr<ControllerBase>> get_controllers() override;
IDirectInput8* get_dinput() const { return m_dinput8.Get(); }
IDirectInput8W* get_dinput() const { return m_dinput8.Get(); }
LPCDIDATAFORMAT get_data_format() const;
private:
@ -32,7 +32,7 @@ private:
decltype(&DirectInput8Create) m_DirectInput8Create;
decltype(&GetdfDIJoystick) m_GetdfDIJoystick = nullptr;
Microsoft::WRL::ComPtr<IDirectInput8> m_dinput8;
Microsoft::WRL::ComPtr<IDirectInput8W> m_dinput8;
};
#endif

View file

@ -96,12 +96,12 @@ void WindowsInitCwd()
{
#if BOOST_OS_WINDOWS
executablePath.resize(4096);
int i = GetModuleFileName(NULL, executablePath.data(), executablePath.size());
int i = GetModuleFileNameW(NULL, executablePath.data(), executablePath.size());
if(i >= 0)
executablePath.resize(i);
else
executablePath.clear();
SetCurrentDirectory(executablePath.c_str());
SetCurrentDirectoryW(executablePath.c_str());
// set high priority
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
#endif
@ -192,7 +192,7 @@ void HandlePostUpdate()
HANDLE lock;
do
{
lock = CreateMutex(nullptr, TRUE, L"Global\\cemu_update_lock");
lock = CreateMutexW(nullptr, TRUE, L"Global\\cemu_update_lock");
std::this_thread::sleep_for(std::chrono::milliseconds(1));
} while (lock == nullptr);
const DWORD wait_result = WaitForSingleObject(lock, 2000);
@ -220,7 +220,7 @@ void ToolShaderCacheMerger();
#if BOOST_OS_WINDOWS
// entrypoint for release builds
int wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd)
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
{
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
cemuLog_log(LogType::Force, "CoInitializeEx() failed");

View file

@ -22,4 +22,4 @@ target_sources(CemuResource PRIVATE CafeDefaultFont.cpp)
target_include_directories(CemuResource PUBLIC "../")
target_link_libraries(CemuResource PRIVATE CemuCommon CemuComponents)
target_link_libraries(CemuResource PRIVATE CemuCommon)

View file

@ -92,9 +92,5 @@ set_property(TARGET CemuUtil PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CON
target_include_directories(CemuUtil PUBLIC "../")
target_link_libraries(CemuUtil PRIVATE
CemuCommon
CemuConfig
Boost::headers
Boost::nowide
OpenSSL::Crypto
CemuCommon
)

View file

@ -3,6 +3,7 @@
#include <charconv>
#include <filesystem>
#include <string_view>
#include <set>
#include "util/math/vector2.h"
#include "util/math/vector3.h"

View file

@ -34,13 +34,7 @@
"default-features": false
},
"zstd",
{
"name": "wxwidgets",
"features": [
"debug-support"
],
"default-features": false
},
"wxwidgets",
"openssl",
{
"name": "curl",