Merge branch 'main' into camera

This commit is contained in:
capitalistspz 2025-06-26 15:59:57 +01:00 committed by GitHub
commit 6123f2cb35
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 1324 additions and 486 deletions

View file

@ -177,6 +177,9 @@ jobs:
build-macos:
runs-on: macos-14
strategy:
matrix:
arch: [x86_64, arm64]
steps:
- name: "Checkout repo"
uses: actions/checkout@v4
@ -236,7 +239,7 @@ jobs:
cd build
cmake .. ${{ env.BUILD_FLAGS }} \
-DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
-DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \
-DMACOS_BUNDLE=ON \
-G Ninja
@ -259,5 +262,5 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: cemu-bin-macos-x64
name: cemu-bin-macos-${{ matrix.arch }}
path: ./bin/Cemu.dmg

View file

@ -166,7 +166,7 @@ if (UNIX AND NOT APPLE)
if(ENABLE_BLUEZ)
find_package(bluez REQUIRED)
set(ENABLE_WIIMOTE ON)
set(SUPPORTS_WIIMOTE ON)
add_compile_definitions(HAS_BLUEZ)
endif()
@ -188,7 +188,7 @@ endif()
if (ENABLE_HIDAPI)
find_package(hidapi REQUIRED)
set(ENABLE_WIIMOTE ON)
set(SUPPORTS_WIIMOTE ON)
add_compile_definitions(HAS_HIDAPI)
endif ()
@ -222,11 +222,20 @@ endif()
add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL)
<<<<<<< camera
set(BUILD_SHARED_LIBS OFF)
add_subdirectory("dependencies/openpnp-capture" EXCLUDE_FROM_ALL SYSTEM)
set(BUILD_SHARED_LIBS "")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "(aarch64)|(AARCH64)")
=======
if (CMAKE_OSX_ARCHITECTURES)
set(CEMU_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
else()
set(CEMU_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
>>>>>>> main
add_subdirectory("dependencies/xbyak_aarch64" EXCLUDE_FROM_ALL)
endif()
@ -235,4 +244,4 @@ if (NOT ZArchive_FOUND)
add_subdirectory("dependencies/ZArchive" EXCLUDE_FROM_ALL)
endif()
add_subdirectory(src)
add_subdirectory(src)

View file

@ -183,6 +183,9 @@ target_sources(ih264d PRIVATE
"decoder/arm/ih264d_function_selector.c"
)
target_compile_options(ih264d PRIVATE -DARMV8)
if(APPLE)
target_sources(ih264d PRIVATE "common/armv8/macos_arm_symbol_aliases.s")
endif()
else()
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
endif()

View file

@ -429,8 +429,13 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
rev64 v7.4h, v2.4h
ld1 {v3.2s}, [x10]
sub x5, x3, #8
#ifdef __APPLE__
adrp x12, _ih264_gai1_intrapred_chroma_plane_coeffs1@GOTPAGE
ldr x12, [x12, _ih264_gai1_intrapred_chroma_plane_coeffs1@GOTPAGEOFF]
#else
adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs1
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs1]
#endif
usubl v10.8h, v5.8b, v1.8b
ld1 {v8.8b, v9.8b}, [x12] // Load multiplication factors 1 to 8 into D3
mov v8.d[1], v9.d[0]
@ -484,10 +489,13 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
zip1 v1.8h, v0.8h, v2.8h
zip2 v2.8h, v0.8h, v2.8h
mov v0.16b, v1.16b
#ifdef __APPLE__
adrp x12, _ih264_gai1_intrapred_chroma_plane_coeffs2@GOTPAGE
ldr x12, [x12, _ih264_gai1_intrapred_chroma_plane_coeffs2@GOTPAGEOFF]
#else
adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs2
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs2]
#endif
ld1 {v8.2s, v9.2s}, [x12]
mov v8.d[1], v9.d[0]
mov v10.16b, v8.16b

View file

@ -431,10 +431,13 @@ ih264_intra_pred_luma_16x16_mode_plane_av8:
mov x10, x1 //top_left
mov x4, #-1
ld1 {v2.2s}, [x1], x8
#ifdef __APPLE__
adrp x7, _ih264_gai1_intrapred_luma_plane_coeffs@GOTPAGE
ldr x7, [x7, _ih264_gai1_intrapred_luma_plane_coeffs@GOTPAGEOFF]
#else
adrp x7, :got:ih264_gai1_intrapred_luma_plane_coeffs
ldr x7, [x7, #:got_lo12:ih264_gai1_intrapred_luma_plane_coeffs]
#endif
ld1 {v0.2s}, [x1]
rev64 v2.8b, v2.8b
ld1 {v6.2s, v7.2s}, [x7]

View file

@ -1029,9 +1029,13 @@ ih264_intra_pred_luma_8x8_mode_horz_u_av8:
mov v3.d[0], v2.d[1]
ext v4.16b, v2.16b , v2.16b , #1
mov v5.d[0], v4.d[1]
#ifdef __APPLE__
adrp x12, _ih264_gai1_intrapred_luma_8x8_horz_u@GOTPAGE
ldr x12, [x12, _ih264_gai1_intrapred_luma_8x8_horz_u@GOTPAGEOFF]
#else
adrp x12, :got:ih264_gai1_intrapred_luma_8x8_horz_u
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_luma_8x8_horz_u]
#endif
uaddl v20.8h, v0.8b, v2.8b
uaddl v22.8h, v1.8b, v3.8b
uaddl v24.8h, v2.8b, v4.8b

View file

@ -142,14 +142,22 @@ ih264_weighted_bi_pred_luma_av8:
sxtw x4, w4
sxtw x5, w5
stp x19, x20, [sp, #-16]!
#ifndef __APPLE__
ldr w8, [sp, #80] //Load wt2 in w8
ldr w9, [sp, #88] //Load ofst1 in w9
add w6, w6, #1 //w6 = log_WD + 1
neg w10, w6 //w10 = -(log_WD + 1)
dup v0.8h, w10 //Q0 = -(log_WD + 1) (32-bit)
ldr w10, [sp, #96] //Load ofst2 in w10
ldr w11, [sp, #104] //Load ht in w11
ldr w12, [sp, #112] //Load wd in w12
#else
ldr w8, [sp, #80] //Load wt2 in w8
ldr w9, [sp, #84] //Load ofst1 in w9
ldr w10, [sp, #88] //Load ofst2 in w10
ldr w11, [sp, #92] //Load ht in w11
ldr w12, [sp, #96] //Load wd in w12
#endif
add w6, w6, #1 //w6 = log_WD + 1
neg w10, w6 //w10 = -(log_WD + 1)
dup v0.8h, w10 //Q0 = -(log_WD + 1) (32-bit)
add w9, w9, #1 //w9 = ofst1 + 1
add w9, w9, w10 //w9 = ofst1 + ofst2 + 1
mov v2.s[0], w7
@ -424,17 +432,24 @@ ih264_weighted_bi_pred_chroma_av8:
sxtw x5, w5
stp x19, x20, [sp, #-16]!
#ifndef __APPLE__
ldr w8, [sp, #80] //Load wt2 in w8
ldr w9, [sp, #88] //Load ofst1 in w9
ldr w10, [sp, #96] //Load ofst2 in w10
ldr w11, [sp, #104] //Load ht in w11
ldr w12, [sp, #112] //Load wd in w12
#else
ldr w8, [sp, #80] //Load wt2 in w8
ldr w9, [sp, #84] //Load ofst1 in w9
ldr w10, [sp, #88] //Load ofst2 in w10
ldr w11, [sp, #92] //Load ht in w11
ldr w12, [sp, #96] //Load wd in w12
#endif
dup v4.4s, w8 //Q2 = (wt2_u, wt2_v) (32-bit)
dup v2.4s, w7 //Q1 = (wt1_u, wt1_v) (32-bit)
add w6, w6, #1 //w6 = log_WD + 1
ldr w9, [sp, #88] //Load ofst1 in w9
ldr w10, [sp, #96] //Load ofst2 in w10
neg w20, w6 //w20 = -(log_WD + 1)
dup v0.8h, w20 //Q0 = -(log_WD + 1) (16-bit)
ldr w11, [sp, #104] //Load ht in x11
ldr w12, [sp, #112] //Load wd in x12
dup v20.8h, w9 //0ffset1
dup v21.8h, w10 //0ffset2
srhadd v6.8b, v20.8b, v21.8b

View file

@ -0,0 +1,185 @@
// macOS clang compilers append preceding underscores to function names, this is to prevent
// mismatches with the assembly function names and the C functions as defined in the header.
.global _ih264_deblk_chroma_horz_bs4_av8
_ih264_deblk_chroma_horz_bs4_av8 = ih264_deblk_chroma_horz_bs4_av8
.global _ih264_deblk_chroma_horz_bslt4_av8
_ih264_deblk_chroma_horz_bslt4_av8 = ih264_deblk_chroma_horz_bslt4_av8
.global _ih264_deblk_chroma_vert_bs4_av8
_ih264_deblk_chroma_vert_bs4_av8 = ih264_deblk_chroma_vert_bs4_av8
.global _ih264_deblk_chroma_vert_bslt4_av8
_ih264_deblk_chroma_vert_bslt4_av8 = ih264_deblk_chroma_vert_bslt4_av8
.global _ih264_deblk_luma_horz_bs4_av8
_ih264_deblk_luma_horz_bs4_av8 = ih264_deblk_luma_horz_bs4_av8
.global _ih264_deblk_luma_horz_bslt4_av8
_ih264_deblk_luma_horz_bslt4_av8 = ih264_deblk_luma_horz_bslt4_av8
.global _ih264_deblk_luma_vert_bs4_av8
_ih264_deblk_luma_vert_bs4_av8 = ih264_deblk_luma_vert_bs4_av8
.global _ih264_deblk_luma_vert_bslt4_av8
_ih264_deblk_luma_vert_bslt4_av8 = ih264_deblk_luma_vert_bslt4_av8
.global _ih264_default_weighted_pred_chroma_av8
_ih264_default_weighted_pred_chroma_av8 = ih264_default_weighted_pred_chroma_av8
.global _ih264_default_weighted_pred_luma_av8
_ih264_default_weighted_pred_luma_av8 = ih264_default_weighted_pred_luma_av8
.global _ih264_ihadamard_scaling_4x4_av8
_ih264_ihadamard_scaling_4x4_av8 = ih264_ihadamard_scaling_4x4_av8
.global _ih264_inter_pred_chroma_av8
_ih264_inter_pred_chroma_av8 = ih264_inter_pred_chroma_av8
.global _ih264_inter_pred_luma_copy_av8
_ih264_inter_pred_luma_copy_av8 = ih264_inter_pred_luma_copy_av8
.global _ih264_inter_pred_luma_horz_av8
_ih264_inter_pred_luma_horz_av8 = ih264_inter_pred_luma_horz_av8
.global _ih264_inter_pred_luma_horz_hpel_vert_hpel_av8
_ih264_inter_pred_luma_horz_hpel_vert_hpel_av8 = ih264_inter_pred_luma_horz_hpel_vert_hpel_av8
.global _ih264_inter_pred_luma_horz_hpel_vert_qpel_av8
_ih264_inter_pred_luma_horz_hpel_vert_qpel_av8 = ih264_inter_pred_luma_horz_hpel_vert_qpel_av8
.global _ih264_inter_pred_luma_horz_qpel_av8
_ih264_inter_pred_luma_horz_qpel_av8 = ih264_inter_pred_luma_horz_qpel_av8
.global _ih264_inter_pred_luma_horz_qpel_vert_hpel_av8
_ih264_inter_pred_luma_horz_qpel_vert_hpel_av8 = ih264_inter_pred_luma_horz_qpel_vert_hpel_av8
.global _ih264_inter_pred_luma_horz_qpel_vert_qpel_av8
_ih264_inter_pred_luma_horz_qpel_vert_qpel_av8 = ih264_inter_pred_luma_horz_qpel_vert_qpel_av8
.global _ih264_inter_pred_luma_vert_av8
_ih264_inter_pred_luma_vert_av8 = ih264_inter_pred_luma_vert_av8
.global _ih264_inter_pred_luma_vert_qpel_av8
_ih264_inter_pred_luma_vert_qpel_av8 = ih264_inter_pred_luma_vert_qpel_av8
.global _ih264_intra_pred_chroma_8x8_mode_horz_av8
_ih264_intra_pred_chroma_8x8_mode_horz_av8 = ih264_intra_pred_chroma_8x8_mode_horz_av8
.global _ih264_intra_pred_chroma_8x8_mode_plane_av8
_ih264_intra_pred_chroma_8x8_mode_plane_av8 = ih264_intra_pred_chroma_8x8_mode_plane_av8
.global _ih264_intra_pred_chroma_8x8_mode_vert_av8
_ih264_intra_pred_chroma_8x8_mode_vert_av8 = ih264_intra_pred_chroma_8x8_mode_vert_av8
.global _ih264_intra_pred_luma_16x16_mode_dc_av8
_ih264_intra_pred_luma_16x16_mode_dc_av8 = ih264_intra_pred_luma_16x16_mode_dc_av8
.global _ih264_intra_pred_luma_16x16_mode_horz_av8
_ih264_intra_pred_luma_16x16_mode_horz_av8 = ih264_intra_pred_luma_16x16_mode_horz_av8
.global _ih264_intra_pred_luma_16x16_mode_plane_av8
_ih264_intra_pred_luma_16x16_mode_plane_av8 = ih264_intra_pred_luma_16x16_mode_plane_av8
.global _ih264_intra_pred_luma_16x16_mode_vert_av8
_ih264_intra_pred_luma_16x16_mode_vert_av8 = ih264_intra_pred_luma_16x16_mode_vert_av8
.global _ih264_intra_pred_luma_4x4_mode_dc_av8
_ih264_intra_pred_luma_4x4_mode_dc_av8 = ih264_intra_pred_luma_4x4_mode_dc_av8
.global _ih264_intra_pred_luma_4x4_mode_diag_dl_av8
_ih264_intra_pred_luma_4x4_mode_diag_dl_av8 = ih264_intra_pred_luma_4x4_mode_diag_dl_av8
.global _ih264_intra_pred_luma_4x4_mode_diag_dr_av8
_ih264_intra_pred_luma_4x4_mode_diag_dr_av8 = ih264_intra_pred_luma_4x4_mode_diag_dr_av8
.global _ih264_intra_pred_luma_4x4_mode_horz_av8
_ih264_intra_pred_luma_4x4_mode_horz_av8 = ih264_intra_pred_luma_4x4_mode_horz_av8
.global _ih264_intra_pred_luma_4x4_mode_horz_d_av8
_ih264_intra_pred_luma_4x4_mode_horz_d_av8 = ih264_intra_pred_luma_4x4_mode_horz_d_av8
.global _ih264_intra_pred_luma_4x4_mode_horz_u_av8
_ih264_intra_pred_luma_4x4_mode_horz_u_av8 = ih264_intra_pred_luma_4x4_mode_horz_u_av8
.global _ih264_intra_pred_luma_4x4_mode_vert_av8
_ih264_intra_pred_luma_4x4_mode_vert_av8 = ih264_intra_pred_luma_4x4_mode_vert_av8
.global _ih264_intra_pred_luma_4x4_mode_vert_l_av8
_ih264_intra_pred_luma_4x4_mode_vert_l_av8 = ih264_intra_pred_luma_4x4_mode_vert_l_av8
.global _ih264_intra_pred_luma_4x4_mode_vert_r_av8
_ih264_intra_pred_luma_4x4_mode_vert_r_av8 = ih264_intra_pred_luma_4x4_mode_vert_r_av8
.global _ih264_intra_pred_luma_8x8_mode_dc_av8
_ih264_intra_pred_luma_8x8_mode_dc_av8 = ih264_intra_pred_luma_8x8_mode_dc_av8
.global _ih264_intra_pred_luma_8x8_mode_diag_dl_av8
_ih264_intra_pred_luma_8x8_mode_diag_dl_av8 = ih264_intra_pred_luma_8x8_mode_diag_dl_av8
.global _ih264_intra_pred_luma_8x8_mode_diag_dr_av8
_ih264_intra_pred_luma_8x8_mode_diag_dr_av8 = ih264_intra_pred_luma_8x8_mode_diag_dr_av8
.global _ih264_intra_pred_luma_8x8_mode_horz_av8
_ih264_intra_pred_luma_8x8_mode_horz_av8 = ih264_intra_pred_luma_8x8_mode_horz_av8
.global _ih264_intra_pred_luma_8x8_mode_horz_d_av8
_ih264_intra_pred_luma_8x8_mode_horz_d_av8 = ih264_intra_pred_luma_8x8_mode_horz_d_av8
.global _ih264_intra_pred_luma_8x8_mode_horz_u_av8
_ih264_intra_pred_luma_8x8_mode_horz_u_av8 = ih264_intra_pred_luma_8x8_mode_horz_u_av8
.global _ih264_intra_pred_luma_8x8_mode_vert_av8
_ih264_intra_pred_luma_8x8_mode_vert_av8 = ih264_intra_pred_luma_8x8_mode_vert_av8
.global _ih264_intra_pred_luma_8x8_mode_vert_l_av8
_ih264_intra_pred_luma_8x8_mode_vert_l_av8 = ih264_intra_pred_luma_8x8_mode_vert_l_av8
.global _ih264_intra_pred_luma_8x8_mode_vert_r_av8
_ih264_intra_pred_luma_8x8_mode_vert_r_av8 = ih264_intra_pred_luma_8x8_mode_vert_r_av8
.global _ih264_iquant_itrans_recon_4x4_av8
_ih264_iquant_itrans_recon_4x4_av8 = ih264_iquant_itrans_recon_4x4_av8
.global _ih264_iquant_itrans_recon_4x4_dc_av8
_ih264_iquant_itrans_recon_4x4_dc_av8 = ih264_iquant_itrans_recon_4x4_dc_av8
.global _ih264_iquant_itrans_recon_8x8_av8
_ih264_iquant_itrans_recon_8x8_av8 = ih264_iquant_itrans_recon_8x8_av8
.global _ih264_iquant_itrans_recon_8x8_dc_av8
_ih264_iquant_itrans_recon_8x8_dc_av8 = ih264_iquant_itrans_recon_8x8_dc_av8
.global _ih264_iquant_itrans_recon_chroma_4x4_av8
_ih264_iquant_itrans_recon_chroma_4x4_av8 = ih264_iquant_itrans_recon_chroma_4x4_av8
.global _ih264_iquant_itrans_recon_chroma_4x4_dc_av8
_ih264_iquant_itrans_recon_chroma_4x4_dc_av8 = ih264_iquant_itrans_recon_chroma_4x4_dc_av8
.global _ih264_pad_left_chroma_av8
_ih264_pad_left_chroma_av8 = ih264_pad_left_chroma_av8
.global _ih264_pad_left_luma_av8
_ih264_pad_left_luma_av8 = ih264_pad_left_luma_av8
.global _ih264_pad_right_chroma_av8
_ih264_pad_right_chroma_av8 = ih264_pad_right_chroma_av8
.global _ih264_pad_right_luma_av8
_ih264_pad_right_luma_av8 = ih264_pad_right_luma_av8
.global _ih264_pad_top_av8
_ih264_pad_top_av8 = ih264_pad_top_av8
.global _ih264_weighted_bi_pred_chroma_av8
_ih264_weighted_bi_pred_chroma_av8 = ih264_weighted_bi_pred_chroma_av8
.global _ih264_weighted_bi_pred_luma_av8
_ih264_weighted_bi_pred_luma_av8 = ih264_weighted_bi_pred_luma_av8
.global _ih264_weighted_pred_chroma_av8
_ih264_weighted_pred_chroma_av8 = ih264_weighted_pred_chroma_av8
.global _ih264_weighted_pred_luma_av8
_ih264_weighted_pred_luma_av8 = ih264_weighted_pred_luma_av8

View file

@ -102,13 +102,21 @@ if (MACOS_BUNDLE)
endforeach(folder)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/debug/lib/libusb-1.0.0.dylib")
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/lib/libusb-1.0.0.dylib")
else()
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/lib/libusb-1.0.0.dylib")
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/lib/libusb-1.0.0.dylib")
endif()
if (EXISTS "/usr/local/lib/libMoltenVK.dylib")
set(MOLTENVK_PATH "/usr/local/lib/libMoltenVK.dylib")
elseif (EXISTS "/opt/homebrew/lib/libMoltenVK.dylib")
set(MOLTENVK_PATH "/opt/homebrew/lib/libMoltenVK.dylib")
else()
message(FATAL_ERROR "failed to find libMoltenVK.dylib")
endif ()
add_custom_command (TARGET CemuBin POST_BUILD
COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib"
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${MOLTENVK_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib"
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${LIBUSB_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libusb-1.0.0.dylib"
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${CMAKE_SOURCE_DIR}/src/resource/update.sh" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/update.sh"
COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}"

View file

@ -537,7 +537,7 @@ if(APPLE)
target_sources(CemuCafe PRIVATE "HW/Latte/Renderer/Vulkan/CocoaSurface.mm")
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "(aarch64)|(AARCH64)")
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
target_sources(CemuCafe PRIVATE
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.h

View file

@ -13,6 +13,8 @@
#define SET_FST_ERROR(__code) if (errorCodeOut) *errorCodeOut = ErrorCode::__code
static_assert(sizeof(NCrypto::AesIv) == 16); // make sure IV is actually 16 bytes
class FSTDataSource
{
public:
@ -868,7 +870,7 @@ static_assert(sizeof(FSTHashedBlock) == BLOCK_SIZE);
struct FSTCachedRawBlock
{
FSTRawBlock blockData;
uint8 ivForNextBlock[16];
NCrypto::AesIv ivForNextBlock;
uint64 lastAccess;
};
@ -919,13 +921,13 @@ void FSTVolume::TrimCacheIfRequired(FSTCachedRawBlock** droppedRawBlock, FSTCach
}
}
void FSTVolume::DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, uint8 ivOut[16])
void FSTVolume::DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, NCrypto::AesIv& ivOut)
{
memset(ivOut, 0, sizeof(ivOut));
ivOut = {};
if(blockIndex == 0)
{
ivOut[0] = (uint8)(clusterIndex >> 8);
ivOut[1] = (uint8)(clusterIndex >> 0);
ivOut.iv[0] = (uint8)(clusterIndex >> 8);
ivOut.iv[1] = (uint8)(clusterIndex >> 0);
}
else
{
@ -936,20 +938,20 @@ void FSTVolume::DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex,
auto itr = m_cacheDecryptedRawBlocks.find(cacheBlockId);
if (itr != m_cacheDecryptedRawBlocks.end())
{
memcpy(ivOut, itr->second->ivForNextBlock, 16);
ivOut = itr->second->ivForNextBlock;
}
else
{
cemu_assert(m_sectorSize >= 16);
cemu_assert(m_sectorSize >= NCrypto::AesIv::SIZE);
uint64 clusterOffset = (uint64)m_cluster[clusterIndex].offset * m_sectorSize;
uint8 prevIV[16];
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * m_sectorSize - 16, prevIV, 16) != 16)
NCrypto::AesIv prevIV{};
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * m_sectorSize - NCrypto::AesIv::SIZE, prevIV.iv, NCrypto::AesIv::SIZE) != NCrypto::AesIv::SIZE)
{
cemuLog_log(LogType::Force, "Failed to read IV for raw FST block");
m_detectedCorruption = true;
return;
}
memcpy(ivOut, prevIV, 16);
ivOut = prevIV;
}
}
}
@ -984,10 +986,10 @@ FSTCachedRawBlock* FSTVolume::GetDecryptedRawBlock(uint32 clusterIndex, uint32 b
return nullptr;
}
// decrypt hash data
uint8 iv[16]{};
NCrypto::AesIv iv{};
DetermineUnhashedBlockIV(clusterIndex, blockIndex, iv);
memcpy(block->ivForNextBlock, block->blockData.rawData.data() + m_sectorSize - 16, 16);
AES128_CBC_decrypt(block->blockData.rawData.data(), block->blockData.rawData.data(), m_sectorSize, m_partitionTitlekey.b, iv);
std::copy(block->blockData.rawData.data() + m_sectorSize - NCrypto::AesIv::SIZE, block->blockData.rawData.data() + m_sectorSize, block->ivForNextBlock.iv);
AES128_CBC_decrypt(block->blockData.rawData.data(), block->blockData.rawData.data(), m_sectorSize, m_partitionTitlekey.b, iv.iv);
// if this is the next block, then hash it
if(cluster.hasContentHash)
{

View file

@ -83,7 +83,6 @@ public:
}
private:
/* FST data (in memory) */
enum class ClusterHashMode : uint8
{
@ -193,7 +192,7 @@ private:
std::unordered_map<uint64, struct FSTCachedHashedBlock*> m_cacheDecryptedHashedBlocks;
uint64 m_cacheAccessCounter{};
void DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, uint8 ivOut[16]);
void DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, NCrypto::AesIv& ivOut);
struct FSTCachedRawBlock* GetDecryptedRawBlock(uint32 clusterIndex, uint32 blockIndex);
struct FSTCachedHashedBlock* GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex);

View file

@ -821,7 +821,7 @@ void GraphicPack2::AddConstantsForCurrentPreset(ExpressionParser& ep)
}
}
void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC)
void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC, const char* virtualMountBase)
{
uint64 currentTitleId = CafeSystem::GetForegroundTitleId();
uint64 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull;
@ -836,7 +836,7 @@ void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC
}
else
{
virtualMountPath = fs::path("vol/content/") / virtualMountPath;
virtualMountPath = fs::path(virtualMountBase) / virtualMountPath;
}
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.file_size(), it.path().generic_string(), m_fs_priority);
}
@ -861,7 +861,7 @@ void GraphicPack2::LoadReplacedFiles()
{
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(contentPath, false);
_iterateReplacedFiles(contentPath, false, "vol/content/");
}
// /aoc/
fs::path aocPath(gfxPackPath);
@ -874,7 +874,18 @@ void GraphicPack2::LoadReplacedFiles()
aocTitleId |= 0x0005000c00000000ULL;
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(aocPath, true);
_iterateReplacedFiles(aocPath, true, nullptr);
}
// /code/
fs::path codePath(gfxPackPath);
codePath.append("code");
if (fs::exists(codePath, ec))
{
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(codePath, false, CafeSystem::GetInternalVirtualCodeFolder().c_str());
}
}

View file

@ -260,7 +260,7 @@ private:
CustomShader LoadShader(const fs::path& path, uint64 shader_base_hash, uint64 shader_aux_hash, GP_SHADER_TYPE shader_type) const;
void ApplyShaderPresets(std::string& shader_source) const;
void LoadReplacedFiles();
void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC);
void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC, const char* virtualMountBase);
// ram mappings
std::vector<std::pair<MPTR, MPTR>> m_ramMappings;

View file

@ -2,62 +2,70 @@
#include "PPCInterpreterInternal.h"
#include "PPCInterpreterHelper.h"
std::unordered_set<std::string> sUnsupportedHLECalls;
std::unordered_set<std::string> s_unsupportedHLECalls;
void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU)
{
const char* libFuncName = (char*)memory_getPointerFromVirtualOffset(hCPU->instructionPointer + 8);
std::string tempString = fmt::format("Unsupported lib call: {}", libFuncName);
if (sUnsupportedHLECalls.find(tempString) == sUnsupportedHLECalls.end())
if (s_unsupportedHLECalls.find(tempString) == s_unsupportedHLECalls.end())
{
cemuLog_log(LogType::UnsupportedAPI, "{}", tempString);
sUnsupportedHLECalls.emplace(tempString);
s_unsupportedHLECalls.emplace(tempString);
}
hCPU->gpr[3] = 0;
PPCInterpreter_nextInstruction(hCPU);
}
std::vector<void(*)(PPCInterpreter_t* hCPU)>* sPPCHLETable{};
static constexpr size_t HLE_TABLE_CAPACITY = 0x4000;
HLECALL s_ppcHleTable[HLE_TABLE_CAPACITY]{};
sint32 s_ppcHleTableWriteIndex = 0;
std::mutex s_ppcHleTableMutex;
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName)
{
if (!sPPCHLETable)
sPPCHLETable = new std::vector<void(*)(PPCInterpreter_t* hCPU)>();
for (sint32 i = 0; i < sPPCHLETable->size(); i++)
std::unique_lock _l(s_ppcHleTableMutex);
if (s_ppcHleTableWriteIndex >= HLE_TABLE_CAPACITY)
{
if ((*sPPCHLETable)[i] == hleCall)
return i;
cemuLog_log(LogType::Force, "HLE table is full");
cemu_assert(false);
}
HLEIDX newFuncIndex = (sint32)sPPCHLETable->size();
sPPCHLETable->resize(sPPCHLETable->size() + 1);
(*sPPCHLETable)[newFuncIndex] = hleCall;
return newFuncIndex;
for (sint32 i = 0; i < s_ppcHleTableWriteIndex; i++)
{
if (s_ppcHleTable[i] == hleCall)
{
return i;
}
}
cemu_assert(s_ppcHleTableWriteIndex < HLE_TABLE_CAPACITY);
s_ppcHleTable[s_ppcHleTableWriteIndex] = hleCall;
HLEIDX funcIndex = s_ppcHleTableWriteIndex;
s_ppcHleTableWriteIndex++;
return funcIndex;
}
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex)
{
if (funcIndex < 0 || funcIndex >= sPPCHLETable->size())
if (funcIndex < 0 || funcIndex >= HLE_TABLE_CAPACITY)
return nullptr;
return sPPCHLETable->data()[funcIndex];
return s_ppcHleTable[funcIndex];
}
std::mutex g_hleLogMutex;
std::mutex s_hleLogMutex;
void PPCInterpreter_virtualHLE(PPCInterpreter_t* hCPU, unsigned int opcode)
{
uint32 hleFuncId = opcode & 0xFFFF;
if (hleFuncId == 0xFFD0)
if (hleFuncId == 0xFFD0) [[unlikely]]
{
g_hleLogMutex.lock();
s_hleLogMutex.lock();
PPCInterpreter_handleUnsupportedHLECall(hCPU);
g_hleLogMutex.unlock();
return;
s_hleLogMutex.unlock();
}
else
{
// os lib function
cemu_assert(hleFuncId < sPPCHLETable->size());
auto hleCall = (*sPPCHLETable)[hleFuncId];
auto hleCall = PPCInterpreter_getHLECall(hleFuncId);
cemu_assert(hleCall);
hleCall(hCPU);
}

View file

@ -230,9 +230,9 @@ static inline float flushDenormalToZero(float f)
// HLE interface
typedef void(*HLECALL)(PPCInterpreter_t* hCPU);
using HLECALL = void(*)(PPCInterpreter_t*);
using HLEIDX = sint32;
typedef sint32 HLEIDX;
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName);
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex);

View file

@ -169,8 +169,10 @@ struct AArch64GenContext_t : CodeGenerator
bool processAllJumps()
{
for (auto&& [jumpStart, jumpInfo] : jumps)
for (auto jump : jumps)
{
auto jumpStart = jump.first;
auto jumpInfo = jump.second;
bool success = std::visit(
[&, this](const auto& jump) {
setSize(jumpStart);

View file

@ -6,6 +6,8 @@
#if defined(ARCH_X86_64) && defined(__GNUC__)
#include <immintrin.h>
#elif defined(__aarch64__)
#include <arm_neon.h>
#endif
struct
@ -502,6 +504,114 @@ void LatteIndices_fastConvertU32_AVX2(const void* indexDataInput, void* indexDat
indexMax = std::max(indexMax, _maxIndex);
indexMin = std::min(indexMin, _minIndex);
}
#elif defined(__aarch64__)
void LatteIndices_fastConvertU16_NEON(const void* indexDataInput, void* indexDataOutput, uint32 count, uint32& indexMin, uint32& indexMax)
{
const uint16* indicesU16BE = (const uint16*)indexDataInput;
uint16* indexOutput = (uint16*)indexDataOutput;
sint32 count8 = count >> 3;
sint32 countRemaining = count & 7;
if (count8)
{
uint16x8_t mMin = vdupq_n_u16(0xFFFF);
uint16x8_t mMax = vdupq_n_u16(0x0000);
uint16x8_t mTemp;
uint16x8_t* mRawIndices = (uint16x8_t*) indicesU16BE;
indicesU16BE += count8 * 8;
uint16x8_t* mOutputIndices = (uint16x8_t*) indexOutput;
indexOutput += count8 * 8;
while (count8--)
{
mTemp = vld1q_u16((uint16*)mRawIndices);
mRawIndices++;
mTemp = vrev16q_u8(mTemp);
mMin = vminq_u16(mMin, mTemp);
mMax = vmaxq_u16(mMax, mTemp);
vst1q_u16((uint16*)mOutputIndices, mTemp);
mOutputIndices++;
}
uint16* mMaxU16 = (uint16*)&mMax;
uint16* mMinU16 = (uint16*)&mMin;
for (int i = 0; i < 8; ++i) {
indexMax = std::max(indexMax, (uint32)mMaxU16[i]);
indexMin = std::min(indexMin, (uint32)mMinU16[i]);
}
}
// process remaining indices
uint32 _minIndex = 0xFFFFFFFF;
uint32 _maxIndex = 0;
for (sint32 i = countRemaining; (--i) >= 0;)
{
uint16 idx = _swapEndianU16(*indicesU16BE);
*indexOutput = idx;
indexOutput++;
indicesU16BE++;
_maxIndex = std::max(_maxIndex, (uint32)idx);
_minIndex = std::min(_minIndex, (uint32)idx);
}
// update min/max
indexMax = std::max(indexMax, _maxIndex);
indexMin = std::min(indexMin, _minIndex);
}
void LatteIndices_fastConvertU32_NEON(const void* indexDataInput, void* indexDataOutput, uint32 count, uint32& indexMin, uint32& indexMax)
{
const uint32* indicesU32BE = (const uint32*)indexDataInput;
uint32* indexOutput = (uint32*)indexDataOutput;
sint32 count8 = count >> 2;
sint32 countRemaining = count & 3;
if (count8)
{
uint32x4_t mMin = vdupq_n_u32(0xFFFFFFFF);
uint32x4_t mMax = vdupq_n_u32(0x00000000);
uint32x4_t mTemp;
uint32x4_t* mRawIndices = (uint32x4_t*) indicesU32BE;
indicesU32BE += count8 * 4;
uint32x4_t* mOutputIndices = (uint32x4_t*) indexOutput;
indexOutput += count8 * 4;
while (count8--)
{
mTemp = vld1q_u32((uint32*)mRawIndices);
mRawIndices++;
mTemp = vrev32q_u8(mTemp);
mMin = vminq_u32(mMin, mTemp);
mMax = vmaxq_u32(mMax, mTemp);
vst1q_u32((uint32*)mOutputIndices, mTemp);
mOutputIndices++;
}
uint32* mMaxU32 = (uint32*)&mMax;
uint32* mMinU32 = (uint32*)&mMin;
for (int i = 0; i < 4; ++i) {
indexMax = std::max(indexMax, mMaxU32[i]);
indexMin = std::min(indexMin, mMinU32[i]);
}
}
// process remaining indices
uint32 _minIndex = 0xFFFFFFFF;
uint32 _maxIndex = 0;
for (sint32 i = countRemaining; (--i) >= 0;)
{
uint32 idx = _swapEndianU32(*indicesU32BE);
*indexOutput = idx;
indexOutput++;
indicesU32BE++;
_maxIndex = std::max(_maxIndex, idx);
_minIndex = std::min(_minIndex, idx);
}
// update min/max
indexMax = std::max(indexMax, _maxIndex);
indexMin = std::min(indexMin, _minIndex);
}
#endif
template<typename T>
@ -688,27 +798,31 @@ void LatteIndices_decode(const void* indexData, LatteIndexType indexType, uint32
{
if (indexType == LatteIndexType::U16_BE)
{
#if defined(ARCH_X86_64)
#if defined(ARCH_X86_64)
if (g_CPUFeatures.x86.avx2)
LatteIndices_fastConvertU16_AVX2(indexData, indexOutputPtr, count, indexMin, indexMax);
else if (g_CPUFeatures.x86.sse4_1 && g_CPUFeatures.x86.ssse3)
LatteIndices_fastConvertU16_SSE41(indexData, indexOutputPtr, count, indexMin, indexMax);
else
LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax);
#else
#elif defined(__aarch64__)
LatteIndices_fastConvertU16_NEON(indexData, indexOutputPtr, count, indexMin, indexMax);
#else
LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax);
#endif
#endif
}
else if (indexType == LatteIndexType::U32_BE)
{
#if defined(ARCH_X86_64)
#if defined(ARCH_X86_64)
if (g_CPUFeatures.x86.avx2)
LatteIndices_fastConvertU32_AVX2(indexData, indexOutputPtr, count, indexMin, indexMax);
else
LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax);
#else
#elif defined(__aarch64__)
LatteIndices_fastConvertU32_NEON(indexData, indexOutputPtr, count, indexMin, indexMax);
#else
LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax);
#endif
#endif
}
else if (indexType == LatteIndexType::U16_LE)
{

View file

@ -209,7 +209,7 @@ class BootSoundPlayer
try
{
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(true, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::TV, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
if(!bootSndAudioDev)
return;
}

View file

@ -464,5 +464,34 @@ namespace iosu
return static_cast<IOSUModule*>(&sIOSUModuleNNPDM);
}
bool GameListStat::LastPlayDate::operator<(const LastPlayDate& b) const
{
const auto& a = *this;
if(a.year < b.year)
return true;
if(a.year > b.year)
return false;
// same year
if(a.month < b.month)
return true;
if(a.month > b.month)
return false;
// same year and month
return a.day < b.day;
}
bool GameListStat::LastPlayDate::operator==(const LastPlayDate& b) const
{
const auto& a = *this;
return a.year == b.year &&
a.month == b.month &&
a.day == b.day;
}
std::weak_ordering GameListStat::LastPlayDate::operator<=>(const LastPlayDate& b) const = default;
};
};

View file

@ -21,11 +21,15 @@ namespace iosu
/* Helper for UI game list */
struct GameListStat
{
struct
struct LastPlayDate
{
uint32 year; // if 0 -> never played
uint32 month;
uint32 day;
bool operator<(const LastPlayDate& b) const;
bool operator==(const LastPlayDate& b) const;
std::weak_ordering operator<=>(const LastPlayDate& b) const;
}last_played;
uint32 numMinutesPlayed;
};

View file

@ -502,6 +502,7 @@ namespace iosu
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, task_header_callback);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &(*it));
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0x3C);
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
if (IsNetworkServiceSSLDisabled(ActiveSettings::GetNetworkService()))
{
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);

View file

@ -132,7 +132,7 @@ namespace iosu
void convertMultiByteStringToBigEndianWidechar(const char* input, uint16be* output, sint32 maxOutputLength)
{
std::basic_string<uint16be> beStr = StringHelpers::FromUtf8(input);
std::vector<uint16be> beStr = StringHelpers::FromUtf8(input);
if (beStr.size() >= maxOutputLength - 1)
beStr.resize(maxOutputLength-1);
for (size_t i = 0; i < beStr.size(); i++)
@ -723,7 +723,7 @@ namespace iosu
{
if(numVecIn != 0 || numVecOut != 1)
return FPResult_InvalidIPCParam;
std::basic_string<uint16be> myComment;
std::vector<uint16be> myComment;
if(g_fpd.nexFriendSession)
{
if(vecOut->size != MY_COMMENT_LENGTH * sizeof(uint16be))
@ -735,8 +735,8 @@ namespace iosu
g_fpd.nexFriendSession->getMyComment(myNexComment);
myComment = StringHelpers::FromUtf8(myNexComment.commentString);
}
myComment.insert(0, 1, '\0');
memcpy(vecOut->basePhys.GetPtr(), myComment.c_str(), MY_COMMENT_LENGTH * sizeof(uint16be));
myComment.insert(myComment.begin(), '\0');
memcpy(vecOut->basePhys.GetPtr(), myComment.data(), MY_COMMENT_LENGTH * sizeof(uint16be));
return FPResult_Ok;
}

View file

@ -25,7 +25,11 @@ void nnNfp_update();
namespace coreinit
{
#ifdef __arm64__
void __OSFiberThreadEntry(uint32, uint32);
#else
void __OSFiberThreadEntry(void* thread);
#endif
void __OSAddReadyThreadToRunQueue(OSThread_t* thread);
void __OSRemoveThreadFromRunQueues(OSThread_t* thread);
};
@ -49,7 +53,7 @@ namespace coreinit
struct OSHostThread
{
OSHostThread(OSThread_t* thread) : m_thread(thread), m_fiber(__OSFiberThreadEntry, this, this)
OSHostThread(OSThread_t* thread) : m_thread(thread), m_fiber((void(*)(void*))__OSFiberThreadEntry, this, this)
{
}
@ -713,7 +717,10 @@ namespace coreinit
thread->id = 0x8000;
if (!thread->deallocatorFunc.IsNull())
{
__OSQueueThreadDeallocation(thread);
PPCCore_switchToSchedulerWithLock(); // make sure the deallocation function runs before we return
}
__OSUnlockScheduler();
@ -1304,8 +1311,14 @@ namespace coreinit
__OSThreadStartTimeslice(hostThread->m_thread, &hostThread->ppcInstance);
}
#ifdef __arm64__
void __OSFiberThreadEntry(uint32 _high, uint32 _low)
{
uint64 _thread = (uint64) _high << 32 | _low;
#else
void __OSFiberThreadEntry(void* _thread)
{
#endif
OSHostThread* hostThread = (OSHostThread*)_thread;
#if defined(ARCH_X86_64)
@ -1515,7 +1528,7 @@ namespace coreinit
}
// queue thread deallocation to run after current thread finishes
// the termination threads run at a higher priority on the same threads
// the termination threads run at a higher priority on the same core
void __OSQueueThreadDeallocation(OSThread_t* thread)
{
uint32 coreIndex = OSGetCoreId();

View file

@ -144,6 +144,11 @@ namespace GX2
void GX2Command_StartNewCommandBuffer(uint32 numU32s)
{
// On submission command buffers are padded to 32 byte alignment
// but nowhere is it guaranteed that internal command buffers have their size aligned to 32 byte (even on console, but testing is required)
// Thus the padding can write out of bounds but this seems to trigger only very rarely in partice. As a workaround we always pad the command buffer size to 32 bytes here
numU32s = (numU32s + 7) & ~0x7;
uint32 coreIndex = coreinit::OSGetCoreId();
auto& coreCBState = s_perCoreCBState[coreIndex];
numU32s = std::max<uint32>(numU32s, 0x100);

View file

@ -145,7 +145,8 @@ namespace nn
if (name.size() != 0)
{
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
auto name_utf16 = StringHelpers::FromUtf8(name);
name_utf16.resize(std::min<size_t>(name_utf16.size(), 128));
if (name_utf16.size() != 0)
{
for (int i = 0; i < name_utf16.size(); i++)
@ -160,7 +161,8 @@ namespace nn
if (description.size() != 0)
{
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
auto description_utf16 = StringHelpers::FromUtf8(description);
description_utf16.resize(std::min<size_t>(description_utf16.size(), 256));
if (description_utf16.size() != 0)
{
for (int i = 0; i < description_utf16.size(); i++)
@ -206,7 +208,8 @@ namespace nn
if (screen_name.size() != 0)
{
auto screen_name_utf16 = StringHelpers::FromUtf8(screen_name).substr(0, 32);
auto screen_name_utf16 = StringHelpers::FromUtf8(screen_name);
screen_name_utf16.resize(std::min<size_t>(screen_name_utf16.size(), 32));
if (screen_name_utf16.size() != 0)
{
for (int i = 0; i < screen_name_utf16.size(); i++)

View file

@ -250,7 +250,8 @@ namespace nn
if (name.size() != 0)
{
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
auto name_utf16 = StringHelpers::FromUtf8(name);
name_utf16.resize(std::min<size_t>(name_utf16.size(), 128));
if (name_utf16.size() != 0)
{
for (int i = 0; i < name_utf16.size(); i++)
@ -265,7 +266,8 @@ namespace nn
if (description.size() != 0)
{
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
auto description_utf16 = StringHelpers::FromUtf8(description);
description_utf16.resize(std::min<size_t>(description_utf16.size(), 256));
if (description_utf16.size() != 0)
{
for (int i = 0; i < description_utf16.size(); i++)

View file

@ -1,5 +1,6 @@
#include "nn_olv_UploadFavoriteTypes.h"
#include <algorithm>
#include <cstddef>
namespace nn
{
@ -115,7 +116,8 @@ namespace nn
if (name.size() != 0)
{
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
auto name_utf16 = StringHelpers::FromUtf8(name);
name_utf16.resize(std::min<size_t>(name_utf16.size(), 128));
if (name_utf16.size() != 0)
{
for (int i = 0; i < name_utf16.size(); i++)
@ -130,7 +132,8 @@ namespace nn
if (description.size() != 0)
{
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
auto description_utf16 = StringHelpers::FromUtf8(description);
description_utf16.resize(std::min<size_t>(description_utf16.size(), 256));
if (description_utf16.size() != 0)
{
for (int i = 0; i < description_utf16.size(); i++)

View file

@ -6,6 +6,8 @@
#include "Backend.h"
#include "Common/FileStream.h"
#include "audio/IAudioAPI.h"
#include "config/CemuConfig.h"
namespace nsyshid
{
@ -558,6 +560,26 @@ namespace nsyshid
Device::WriteResult SkylanderPortalDevice::Write(WriteMessage* message)
{
if (message->length != 64) {
cemu_assert_error();
}
if (!g_portalAudio)
{
// Portal audio is mono channel, 16 bit audio.
// Audio is unsigned 16 bit, supplied as 64 bytes which is 32 samples per block
g_portalAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::Portal, 8000, 32, 16);
}
std::array<sint16, 32> mono_samples;
for (unsigned int i = 0; i < mono_samples.size(); ++i)
{
sint16 sample = static_cast<uint16>(message->data[i * 2 + 1]) << 8 | static_cast<uint16>(message->data[i * 2]);
mono_samples[i] = sample;
}
if (g_portalAudio)
{
g_portalAudio->FeedBlock(mono_samples.data());
}
message->bytesWritten = message->length;
return Device::WriteResult::Success;
}
@ -604,20 +626,20 @@ namespace nsyshid
*(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength
currentWritePtr = currentWritePtr + 9;
// endpoint descriptor 1
*(uint8*)(currentWritePtr + 0) = 7; // bLength
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
*(uint8*)(currentWritePtr + 0) = 7; // bLength
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
currentWritePtr = currentWritePtr + 7;
// endpoint descriptor 2
*(uint8*)(currentWritePtr + 0) = 7; // bLength
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
*(uint8*)(currentWritePtr + 0) = 7; // bLength
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
currentWritePtr = currentWritePtr + 7;
cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29);
@ -628,8 +650,8 @@ namespace nsyshid
}
bool SkylanderPortalDevice::SetIdle(uint8 ifIndex,
uint8 reportId,
uint8 duration)
uint8 reportId,
uint8 duration)
{
return true;
}

View file

@ -404,7 +404,7 @@ namespace snd_core
{
try
{
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(true, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::TV, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
}
catch (std::runtime_error& ex)
{
@ -417,7 +417,7 @@ namespace snd_core
{
try
{
g_padAudio = IAudioAPI::CreateDeviceFromConfig(false, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
g_padAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::Gamepad, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
if(g_padAudio)
g_padVolume = g_padAudio->GetVolume();
}
@ -442,6 +442,11 @@ namespace snd_core
g_padAudio->Stop();
g_padAudio.reset();
}
if (g_portalAudio)
{
g_portalAudio->Stop();
g_portalAudio.reset();
}
}
void AXOut_updateDevicePlayState(bool isPlaying)
@ -462,6 +467,14 @@ namespace snd_core
else
g_padAudio->Stop();
}
if (g_portalAudio)
{
if (isPlaying)
g_portalAudio->Play();
else
g_portalAudio->Stop();
}
}
// called periodically to check for AX updates

View file

@ -3,6 +3,7 @@
#include "util/helpers/helpers.h"
#include "config/CemuConfig.h"
#include "config/ActiveSettings.h"
#include "config/LaunchSettings.h"
#include <mutex>
#include <condition_variable>
@ -144,6 +145,9 @@ bool cemuLog_log(LogType type, std::string_view text)
if (!cemuLog_isLoggingEnabled(type))
return false;
if (LaunchSettings::Verbose())
std::cout << text << std::endl;
cemuLog_writeLineToLog(text);
const auto it = std::find_if(g_logging_window_mapping.cbegin(), g_logging_window_mapping.cend(),

View file

@ -13,10 +13,17 @@ namespace NCrypto
std::string base64Encode(const void* inputMem, size_t inputLen);
std::vector<uint8> base64Decode(std::string_view inputStr);
/* key helper struct */
/* key and iv helper struct */
struct AesKey
{
uint8 b[16];
static constexpr size_t SIZE = 16;
uint8 b[SIZE];
};
struct AesIv
{
static constexpr size_t SIZE = 16;
uint8 iv[SIZE];
};
/* ECC Certificate */

View file

@ -51,15 +51,15 @@ class CafeWideString // fixed buffer size, null-terminated, PPC wchar_t (16bit b
bool assignFromUTF8(std::string_view sv)
{
std::basic_string<uint16be> beStr = StringHelpers::FromUtf8(sv);
if(beStr.length() > N-1)
std::vector<uint16be> beStr = StringHelpers::FromUtf8(sv);
if(beStr.size() > N-1)
{
memcpy(data, beStr.data(), (N-1)*sizeof(uint16be));
data[N-1] = 0;
return false;
}
memcpy(data, beStr.data(), beStr.length()*sizeof(uint16be));
data[beStr.length()] = '\0';
memcpy(data, beStr.data(), beStr.size()*sizeof(uint16be));
data[beStr.size()] = '\0';
return true;
}

View file

@ -310,7 +310,8 @@ inline uint64 __rdtsc()
inline void _mm_mfence()
{
asm volatile("" ::: "memory");
std::atomic_thread_fence(std::memory_order_seq_cst);
}
inline unsigned char _addcarry_u64(unsigned char carry, unsigned long long a, unsigned long long b, unsigned long long *result)
@ -385,8 +386,6 @@ template <typename T1, typename T2>
constexpr bool HAS_FLAG(T1 flags, T2 test_flag) { return (flags & (T1)test_flag) == (T1)test_flag; }
template <typename T1, typename T2>
constexpr bool HAS_BIT(T1 value, T2 index) { return (value & ((T1)1 << index)) != 0; }
template <typename T>
constexpr void SAFE_RELEASE(T& p) { if (p) { p->Release(); p = nullptr; } }
template <typename T>
constexpr uint32_t ppcsizeof() { return (uint32_t) sizeof(T); }

View file

@ -1,9 +1,8 @@
#include "DirectSoundAPI.h"
#include "gui/wxgui.h"
#include "util/helpers/helpers.h"
#include "gui/guiWrapper.h"
#include <wrl/client.h>
#pragma comment(lib, "Dsound.lib")
@ -15,12 +14,9 @@ std::wstring DirectSoundAPI::DirectSoundDeviceDescription::GetIdentifier() const
DirectSoundAPI::DirectSoundAPI(GUID* guid, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
: IAudioAPI(samplerate, channels, samples_per_block, bits_per_sample)
{
LPDIRECTSOUND8 direct_sound;
if (DirectSoundCreate8(guid, &direct_sound, nullptr) != DS_OK)
if (DirectSoundCreate8(guid, &m_direct_sound, nullptr) != DS_OK)
throw std::runtime_error("can't create directsound device");
m_direct_sound = decltype(m_direct_sound)(direct_sound);
if (FAILED(m_direct_sound->SetCooperativeLevel(gui_getWindowInfo().window_main.hwnd, DSSCL_PRIORITY)))
throw std::runtime_error("can't set directsound priority");
@ -30,7 +26,7 @@ DirectSoundAPI::DirectSoundAPI(GUID* guid, sint32 samplerate, sint32 channels, s
bd.dwBufferBytes = kBufferCount * m_bytesPerBlock; // kBlockCount * (samples_per_block * channels * (bits_per_sample / 8));
bd.lpwfxFormat = (LPWAVEFORMATEX)&m_wfx;
LPDIRECTSOUNDBUFFER sound_buffer;
Microsoft::WRL::ComPtr<IDirectSoundBuffer> sound_buffer;
if (FAILED(m_direct_sound->CreateSoundBuffer(&bd, &sound_buffer, nullptr)))
throw std::runtime_error("can't create directsound soundbuffer");
@ -41,27 +37,17 @@ DirectSoundAPI::DirectSoundAPI(GUID* guid, sint32 samplerate, sint32 channels, s
m_sound_buffer_size = caps.dwBufferBytes;
LPDIRECTSOUNDBUFFER8 sound_buffer8;
LPDIRECTSOUNDNOTIFY8 notify8;
sound_buffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&sound_buffer8);
Microsoft::WRL::ComPtr<IDirectSoundNotify8> notify8;
if (!sound_buffer8)
if (FAILED(sound_buffer->QueryInterface(IID_IDirectSoundBuffer8, &m_sound_buffer)))
{
sound_buffer->Release();
throw std::runtime_error("can't get directsound buffer interface");
}
m_sound_buffer = decltype(m_sound_buffer)(sound_buffer8);
sound_buffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&notify8);
if (!notify8)
if (FAILED(sound_buffer->QueryInterface(IID_IDirectSoundNotify8, &m_notify)))
{
sound_buffer->Release();
throw std::runtime_error("can't get directsound notify interface");
}
m_notify = decltype(m_notify)(notify8);
sound_buffer->Release();
{ // initialize sound buffer
void *ptr1, *ptr2;
@ -155,10 +141,6 @@ DirectSoundAPI::~DirectSoundAPI()
if(m_thread.joinable())
m_thread.join();
m_notify.reset();
m_sound_buffer.reset();
m_direct_sound.reset();
for(auto entry : m_notify_event)
{
if (entry)
@ -186,7 +168,7 @@ bool DirectSoundAPI::Stop()
bool DirectSoundAPI::FeedBlock(sint16* data)
{
std::unique_lock lock(m_mutex);
std::lock_guard lock(m_mutex);
if (m_buffer.size() > kBlockCount)
{
cemuLog_logDebug(LogType::Force, "dropped direct sound block since too many buffers are queued");

View file

@ -2,8 +2,8 @@
#define DIRECTSOUND_VERSION 0x0800
#include <mmsystem.h>
//#include <mmreg.h>
#include <dsound.h>
#include <wrl/client.h>
#include "IAudioAPI.h"
@ -41,15 +41,10 @@ public:
static std::vector<DeviceDescriptionPtr> GetInputDevices();
private:
struct DirectSoundDeleter
{
void operator()(IUnknown* ptr) const { if (ptr) ptr->Release(); }
};
std::unique_ptr<IDirectSound8, DirectSoundDeleter> m_direct_sound;
//std::unique_ptr<IDirectSoundCapture8, DirectSoundDeleter> m_direct_sound_capture;
std::unique_ptr<IDirectSoundBuffer8, DirectSoundDeleter> m_sound_buffer;
std::unique_ptr<IDirectSoundNotify8, DirectSoundDeleter> m_notify;
Microsoft::WRL::ComPtr<IDirectSound8> m_direct_sound;
//Microsoft::WRL::ComPtr<IDirectSoundCapture8> m_direct_sound_capture;
Microsoft::WRL::ComPtr<IDirectSoundBuffer8> m_sound_buffer;
Microsoft::WRL::ComPtr<IDirectSoundNotify8> m_notify;
DWORD m_sound_buffer_size = 0;
uint32_t m_offset = 0;

View file

@ -13,13 +13,14 @@
std::shared_mutex g_audioMutex;
AudioAPIPtr g_tvAudio;
AudioAPIPtr g_padAudio;
AudioAPIPtr g_portalAudio;
std::atomic_int32_t g_padVolume = 0;
uint32 IAudioAPI::s_audioDelay = 2;
std::array<bool, IAudioAPI::AudioAPIEnd> IAudioAPI::s_availableApis{};
IAudioAPI::IAudioAPI(uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
: m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample)
: m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample)
{
m_bytesPerBlock = samples_per_block * channels * (bits_per_sample / 8);
InitWFX(m_samplerate, m_channels, m_bitsPerSample);
@ -80,7 +81,7 @@ void IAudioAPI::InitializeStatic()
#if BOOST_OS_WINDOWS
s_availableApis[DirectSound] = true;
s_availableApis[XAudio2] = XAudio2API::InitializeStatic();
if(!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available
if (!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available
s_availableApis[XAudio27] = XAudio27API::InitializeStatic();
#endif
#if HAS_CUBEB
@ -97,30 +98,29 @@ bool IAudioAPI::IsAudioAPIAvailable(AudioAPI api)
return false;
}
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
{
auto& config = GetConfig();
sint32 channels = CemuConfig::AudioChannelsToNChannels(TV ? config.tv_channels : config.pad_channels);
return CreateDeviceFromConfig(TV, rate, channels, samples_per_block, bits_per_sample);
sint32 channels = CemuConfig::AudioChannelsToNChannels(AudioTypeToChannels(type));
return CreateDeviceFromConfig(type, rate, channels, samples_per_block, bits_per_sample);
}
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
{
AudioAPIPtr audioAPIDev;
auto& config = GetConfig();
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api;
auto& selectedDevice = TV ? config.tv_device : config.pad_device;
auto selectedDevice = GetDeviceFromType(type);
if(selectedDevice.empty())
if (selectedDevice.empty())
return {};
IAudioAPI::DeviceDescriptionPtr device_description;
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
{
auto devices = IAudioAPI::GetDevices(audio_api);
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) {return d->GetIdentifier() == selectedDevice; });
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) { return d->GetIdentifier() == selectedDevice; });
if (it != devices.end())
device_description = *it;
}
@ -128,7 +128,8 @@ AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 chann
throw std::runtime_error("failed to find selected device while trying to create audio device");
audioAPIDev = CreateDevice(audio_api, device_description, rate, channels, samples_per_block, bits_per_sample);
audioAPIDev->SetVolume(TV ? config.tv_volume : config.pad_volume);
audioAPIDev->SetVolume(GetVolumeFromType(type));
return audioAPIDev;
}
@ -137,7 +138,7 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
if (!IsAudioAPIAvailable(api))
return {};
switch(api)
switch (api)
{
#if BOOST_OS_WINDOWS
case DirectSound:
@ -157,11 +158,11 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
}
#endif
#if HAS_CUBEB
case Cubeb:
{
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
}
case Cubeb:
{
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
}
#endif
default:
throw std::runtime_error(fmt::format("invalid audio api: {}", api));
@ -172,8 +173,8 @@ std::vector<IAudioAPI::DeviceDescriptionPtr> IAudioAPI::GetDevices(AudioAPI api)
{
if (!IsAudioAPIAvailable(api))
return {};
switch(api)
switch (api)
{
#if BOOST_OS_WINDOWS
case DirectSound:
@ -209,3 +210,51 @@ uint32 IAudioAPI::GetAudioDelay() const
{
return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay;
}
AudioChannels IAudioAPI::AudioTypeToChannels(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_channels;
case Gamepad:
return config.pad_channels;
case Portal:
return kMono;
default:
return kMono;
}
}
std::wstring IAudioAPI::GetDeviceFromType(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_device;
case Gamepad:
return config.pad_device;
case Portal:
return config.portal_device;
default:
return L"";
}
}
sint32 IAudioAPI::GetVolumeFromType(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_volume;
case Gamepad:
return config.pad_volume;
case Portal:
return config.portal_volume;
default:
return 0;
}
}

View file

@ -4,6 +4,8 @@
#include <mmreg.h>
#endif
#include "config/CemuConfig.h"
class IAudioAPI
{
friend class GeneralSettings2;
@ -30,6 +32,13 @@ public:
using DeviceDescriptionPtr = std::shared_ptr<DeviceDescription>;
enum AudioType
{
TV = 0,
Gamepad,
Portal
};
enum AudioAPI
{
DirectSound = 0,
@ -62,8 +71,8 @@ public:
static void InitializeStatic();
static bool IsAudioAPIAvailable(AudioAPI api);
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
static std::unique_ptr<IAudioAPI> CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
static std::vector<DeviceDescriptionPtr> GetDevices(AudioAPI api);
@ -84,6 +93,9 @@ protected:
private:
static uint32 s_audioDelay;
void InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample);
static AudioChannels AudioTypeToChannels(AudioType type);
static std::wstring GetDeviceFromType(AudioType type);
static sint32 GetVolumeFromType(AudioType type);
};
@ -93,3 +105,5 @@ extern AudioAPIPtr g_tvAudio;
extern AudioAPIPtr g_padAudio;
extern std::atomic_int32_t g_padVolume;
extern AudioAPIPtr g_portalAudio;

View file

@ -2,6 +2,7 @@
//#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
#include <wrl/client.h>
#include <xaudio2.h>
#ifndef XAUDIO2_DLL
@ -33,17 +34,15 @@ XAudio2API::XAudio2API(std::wstring device_id, uint32 samplerate, uint32 channel
throw std::runtime_error("can't find XAudio2Create import");
HRESULT hres;
IXAudio2* xaudio;
if (FAILED((hres = _XAudio2Create(&xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR))))
if (FAILED((hres = _XAudio2Create(&m_xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR))))
throw std::runtime_error(fmt::format("can't create xaudio device (hres: {:#x})", hres));
m_xaudio = decltype(m_xaudio)(xaudio);
IXAudio2MasteringVoice* mastering_voice;
if (FAILED((hres = m_xaudio->CreateMasteringVoice(&mastering_voice, channels, samplerate, 0, m_device_id.empty() ? nullptr : m_device_id.c_str()))))
throw std::runtime_error(fmt::format("can't create xaudio mastering voice (hres: {:#x})", hres));
m_mastering_voice = decltype(m_mastering_voice)(mastering_voice);
m_mastering_voice.reset(mastering_voice);
m_wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
m_wfx.Format.nChannels = channels;
@ -88,12 +87,6 @@ XAudio2API::XAudio2API(std::wstring device_id, uint32 samplerate, uint32 channel
m_xaudio->StartEngine();
}
void XAudio2API::XAudioDeleter::operator()(IXAudio2* ptr) const
{
if (ptr)
ptr->Release();
}
void XAudio2API::VoiceDeleter::operator()(IXAudio2Voice* ptr) const
{
if (ptr)
@ -106,10 +99,6 @@ XAudio2API::~XAudio2API()
m_xaudio->StopEngine();
XAudio2API::Stop();
m_source_voice.reset();
m_mastering_voice.reset();
m_xaudio.reset();
}
void XAudio2API::SetVolume(sint32 volume)
@ -179,10 +168,10 @@ const std::vector<XAudio2API::DeviceDescriptionPtr>& XAudio2API::RefreshDevices(
try
{
struct IWbemLocator *wbem_locator = nullptr;
Microsoft::WRL::ComPtr<IWbemLocator> wbem_locator;
HRESULT hres = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&wbem_locator);
if (FAILED(hres) || !wbem_locator)
HRESULT hres = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&wbem_locator));
if (FAILED(hres))
throw std::system_error(hres, std::system_category());
std::shared_ptr<OLECHAR> path(SysAllocString(LR"(\\.\root\cimv2)"), SysFreeString);
@ -191,20 +180,19 @@ const std::vector<XAudio2API::DeviceDescriptionPtr>& XAudio2API::RefreshDevices(
std::shared_ptr<OLECHAR> name_row(SysAllocString(L"Name"), SysFreeString);
std::shared_ptr<OLECHAR> device_id_row(SysAllocString(L"DeviceID"), SysFreeString);
IWbemServices *wbem_services = nullptr;
Microsoft::WRL::ComPtr<IWbemServices> wbem_services;
hres = wbem_locator->ConnectServer(path.get(), nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbem_services);
wbem_locator->Release(); // Free memory resources.
if (FAILED(hres) || !wbem_services)
throw std::system_error(hres, std::system_category());
hres = CoSetProxyBlanket(wbem_services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
if (FAILED(hres))
throw std::system_error(hres, std::system_category());
IEnumWbemClassObject* wbem_enum = nullptr;
hres = CoSetProxyBlanket(wbem_services.Get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
if (FAILED(hres))
throw std::system_error(hres, std::system_category());
Microsoft::WRL::ComPtr<IEnumWbemClassObject> wbem_enum;
hres = wbem_services->ExecQuery(language.get(), query.get(), WBEM_FLAG_RETURN_WBEM_COMPLETE | WBEM_FLAG_FORWARD_ONLY, nullptr, &wbem_enum);
if (FAILED(hres) || !wbem_enum)
if (FAILED(hres))
throw std::system_error(hres, std::system_category());
ULONG returned;
@ -250,11 +238,6 @@ const std::vector<XAudio2API::DeviceDescriptionPtr>& XAudio2API::RefreshDevices(
auto default_device = std::make_shared<XAudio2DeviceDescription>(L"Primary Sound Driver", L"");
s_devices.insert(s_devices.begin(), default_device);
}
wbem_enum->Release();
// Clean up
wbem_services->Release();
}
catch (const std::system_error& ex)
{

View file

@ -4,6 +4,7 @@
#include <mmsystem.h>
#include <mmreg.h>
#include <dsound.h>
#include <wrl/client.h>
#include "IAudioAPI.h"
@ -50,11 +51,6 @@ private:
static const std::vector<DeviceDescriptionPtr>& RefreshDevices();
struct XAudioDeleter
{
void operator()(IXAudio2* ptr) const;
};
struct VoiceDeleter
{
void operator()(IXAudio2Voice* ptr) const;
@ -63,7 +59,7 @@ private:
static HMODULE s_xaudio_dll;
static std::vector<DeviceDescriptionPtr> s_devices;
std::unique_ptr<IXAudio2, XAudioDeleter> m_xaudio;
Microsoft::WRL::ComPtr<IXAudio2> m_xaudio;
std::wstring m_device_id;
std::unique_ptr<IXAudio2MasteringVoice, VoiceDeleter> m_mastering_voice;
std::unique_ptr<IXAudio2SourceVoice, VoiceDeleter> m_source_voice;

View file

@ -278,6 +278,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
tv_volume = audio.get("TVVolume", 20);
pad_volume = audio.get("PadVolume", 0);
input_volume = audio.get("InputVolume", 20);
portal_volume = audio.get("PortalVolume", 20);
const auto tv = audio.get("TVDevice", "");
try
@ -309,6 +310,16 @@ void CemuConfig::Load(XMLConfigParser& parser)
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", input_device_name);
}
const auto portal_device_name = audio.get("PortalDevice", "");
try
{
portal_device = boost::nowide::widen(portal_device_name);
}
catch (const std::exception&)
{
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", portal_device_name);
}
// account
auto acc = parser.get("Account");
account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id);
@ -514,9 +525,11 @@ void CemuConfig::Save(XMLConfigParser& parser)
audio.set("TVVolume", tv_volume);
audio.set("PadVolume", pad_volume);
audio.set("InputVolume", input_volume);
audio.set("PortalVolume", portal_volume);
audio.set("TVDevice", boost::nowide::narrow(tv_device).c_str());
audio.set("PadDevice", boost::nowide::narrow(pad_device).c_str());
audio.set("InputDevice", boost::nowide::narrow(input_device).c_str());
audio.set("PortalDevice", boost::nowide::narrow(portal_device).c_str());
// account
auto acc = config.set("Account");

View file

@ -480,8 +480,8 @@ struct CemuConfig
sint32 audio_api = 0;
sint32 audio_delay = 2;
AudioChannels tv_channels = kStereo, pad_channels = kStereo, input_channels = kMono;
sint32 tv_volume = 50, pad_volume = 0, input_volume = 50;
std::wstring tv_device{ L"default" }, pad_device, input_device;
sint32 tv_volume = 50, pad_volume = 0, input_volume = 50, portal_volume = 50;
std::wstring tv_device{ L"default" }, pad_device, input_device, portal_device;
// account
struct

View file

@ -59,6 +59,9 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
desc.add_options()
("help,h", "This help screen")
("version,v", "Displays the version of Cemu")
#if !BOOST_OS_WINDOWS
("verbose", "Log to stdout")
#endif
("game,g", po::wvalue<std::wstring>(), "Path of game to launch")
("title-id,t", po::value<std::string>(), "Title ID of the title to be launched (overridden by --game)")
@ -125,6 +128,9 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
return false; // exit in main
}
if (vm.count("verbose"))
s_verbose = true;
if (vm.count("game"))
{
std::wstring tmp = vm["game"].as<std::wstring>();

View file

@ -22,6 +22,8 @@ public:
static std::optional<bool> RenderUpsideDownEnabled() { return s_render_upside_down; }
static std::optional<bool> FullscreenEnabled() { return s_fullscreen; }
static bool Verbose() { return s_verbose; }
static bool GDBStubEnabled() { return s_enable_gdbstub; }
static bool NSightModeEnabled() { return s_nsight_mode; }
@ -40,6 +42,8 @@ private:
inline static std::optional<bool> s_render_upside_down{};
inline static std::optional<bool> s_fullscreen{};
inline static bool s_verbose = false;
inline static bool s_enable_gdbstub = false;
inline static bool s_nsight_mode = false;

View file

@ -90,9 +90,9 @@ wxPanel* EmulatedUSBDeviceFrame::AddSkylanderPage(wxNotebook* notebook)
wxPanel* EmulatedUSBDeviceFrame::AddInfinityPage(wxNotebook* notebook)
{
auto* panel = new wxPanel(notebook);
auto* panelSizer = new wxBoxSizer(wxBOTH);
auto* panelSizer = new wxBoxSizer(wxVERTICAL);
auto* box = new wxStaticBox(panel, wxID_ANY, _("Infinity Manager"));
auto* boxSizer = new wxStaticBoxSizer(box, wxBOTH);
auto* boxSizer = new wxStaticBoxSizer(box, wxVERTICAL);
auto* row = new wxBoxSizer(wxHORIZONTAL);
@ -283,7 +283,7 @@ void EmulatedUSBDeviceFrame::LoadSkylanderPath(uint8 slot, wxString path)
std::unique_ptr<FileStream> skyFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true));
if (!skyFile)
{
wxMessageDialog open_error(this, "Error Opening File: " + path.c_str());
wxMessageDialog open_error(this, "Error Opening File: " + path);
open_error.ShowModal();
return;
}
@ -831,4 +831,4 @@ uint8 MoveDimensionFigureDialog::GetNewIndex() const
std::array<std::optional<uint32>, 7> EmulatedUSBDeviceFrame::GetCurrentMinifigs()
{
return m_dimSlots;
}
}

View file

@ -542,6 +542,36 @@ wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook)
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
}
{
auto box = new wxStaticBox(audio_panel, wxID_ANY, _("Trap Team Portal"));
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
auto portal_audio_row = new wxFlexGridSizer(0, 3, 0, 0);
portal_audio_row->SetFlexibleDirection(wxBOTH);
portal_audio_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
portal_audio_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_portal_device = new wxChoice(box, wxID_ANY, wxDefaultPosition);
m_portal_device->SetMinSize(wxSize(300, -1));
m_portal_device->SetToolTip(_("Select the active audio output device for Wii U GamePad"));
portal_audio_row->Add(m_portal_device, 0, wxEXPAND | wxALL, 5);
portal_audio_row->AddSpacer(0);
m_portal_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this);
portal_audio_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_portal_volume = new wxSlider(box, wxID_ANY, 100, 0, 100);
portal_audio_row->Add(m_portal_volume, 0, wxEXPAND | wxALL, 5);
auto audio_pad_volume_text = new wxStaticText(box, wxID_ANY, "100%");
portal_audio_row->Add(audio_pad_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5);
m_portal_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_pad_volume_text));
m_portal_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this);
box_sizer->Add(portal_audio_row, 1, wxEXPAND, 5);
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
}
audio_panel->SetSizerAndFit(audio_panel_sizer);
return audio_panel;
}
@ -993,6 +1023,7 @@ void GeneralSettings2::StoreConfig()
config.tv_volume = m_tv_volume->GetValue();
config.pad_volume = m_pad_volume->GetValue();
config.input_volume = m_input_volume->GetValue();
config.portal_volume = m_portal_volume->GetValue();
config.tv_device.clear();
const auto tv_device = m_tv_device->GetSelection();
@ -1021,6 +1052,15 @@ void GeneralSettings2::StoreConfig()
config.input_device = device_description->GetDescription()->GetIdentifier();
}
config.portal_device.clear();
const auto portal_device = m_portal_device->GetSelection();
if (portal_device != wxNOT_FOUND && portal_device != 0 && m_portal_device->HasClientObjectData())
{
const auto* device_description = (wxDeviceDescription*)m_portal_device->GetClientObject(portal_device);
if (device_description)
config.portal_device = device_description->GetDescription()->GetIdentifier();
}
// graphics
config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection();
@ -1131,11 +1171,16 @@ void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
g_padVolume = event.GetInt();
}
}
else
else if (event.GetEventObject() == m_tv_volume)
{
if (g_tvAudio)
g_tvAudio->SetVolume(event.GetInt());
}
else
{
if(g_portalAudio)
g_portalAudio->SetVolume(event.GetInt());
}
}
@ -1195,10 +1240,12 @@ void GeneralSettings2::UpdateAudioDeviceList()
m_tv_device->Clear();
m_pad_device->Clear();
m_input_device->Clear();
m_portal_device->Clear();
m_tv_device->Append(_("Disabled"));
m_pad_device->Append(_("Disabled"));
m_input_device->Append(_("Disabled"));
m_portal_device->Append(_("Disabled"));
const auto audio_api = (IAudioAPI::AudioAPI)GetConfig().audio_api;
const auto devices = IAudioAPI::GetDevices(audio_api);
@ -1206,6 +1253,7 @@ void GeneralSettings2::UpdateAudioDeviceList()
{
m_tv_device->Append(device->GetName(), new wxDeviceDescription(device));
m_pad_device->Append(device->GetName(), new wxDeviceDescription(device));
m_portal_device->Append(device->GetName(), new wxDeviceDescription(device));
}
const auto input_audio_api = IAudioInputAPI::Cubeb; //(IAudioAPI::AudioAPI)GetConfig().input_audio_api;
@ -1225,6 +1273,8 @@ void GeneralSettings2::UpdateAudioDeviceList()
m_input_device->SetSelection(0);
m_portal_device->SetSelection(0);
// todo reset global instance of audio device
}
@ -1708,6 +1758,22 @@ void GeneralSettings2::ApplyConfig()
else
m_input_device->SetSelection(0);
SendSliderEvent(m_portal_volume, config.portal_volume);
if (!config.portal_device.empty() && m_portal_device->HasClientObjectData())
{
for (uint32 i = 0; i < m_portal_device->GetCount(); ++i)
{
const auto device_description = (wxDeviceDescription*)m_portal_device->GetClientObject(i);
if (device_description && config.portal_device == device_description->GetDescription()->GetIdentifier())
{
m_portal_device->SetSelection(i);
break;
}
}
}
else
m_portal_device->SetSelection(0);
// account
UpdateOnlineAccounts();
m_active_account->SetSelection(0);
@ -1866,6 +1932,42 @@ void GeneralSettings2::UpdateAudioDevice()
}
}
}
// skylander portal audio device
{
const auto selection = m_portal_device->GetSelection();
if (selection == wxNOT_FOUND)
{
cemu_assert_debug(false);
return;
}
g_portalAudio.reset();
if (m_portal_device->HasClientObjectData())
{
const auto description = (wxDeviceDescription*)m_portal_device->GetClientObject(selection);
if (description)
{
sint32 channels;
if (m_game_launched && g_portalAudio)
channels = g_portalAudio->GetChannels();
else
channels = 1;
try
{
g_portalAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, description->GetDescription(), 8000, 1, 32, 16);
g_portalAudio->SetVolume(m_portal_volume->GetValue());
}
catch (std::runtime_error& ex)
{
cemuLog_log(LogType::Force, "can't initialize portal audio: {}", ex.what());
}
}
}
}
}
void GeneralSettings2::OnAudioDeviceSelected(wxCommandEvent& event)

View file

@ -63,9 +63,9 @@ private:
// Audio
wxChoice* m_audio_api;
wxSlider *m_audio_latency;
wxSlider *m_tv_volume, *m_pad_volume, *m_input_volume;
wxSlider *m_tv_volume, *m_pad_volume, *m_input_volume, *m_portal_volume;
wxChoice *m_tv_channels, *m_pad_channels, *m_input_channels;
wxChoice *m_tv_device, *m_pad_device, *m_input_device;
wxChoice *m_tv_device, *m_pad_device, *m_input_device, *m_portal_device;
// Account
wxButton* m_create_account, * m_delete_account;

View file

@ -92,7 +92,11 @@ enum
MAINFRAME_MENU_ID_OPTIONS_GENERAL2,
MAINFRAME_MENU_ID_OPTIONS_AUDIO,
MAINFRAME_MENU_ID_OPTIONS_INPUT,
<<<<<<< camera
MAINFRAME_MENU_ID_OPTIONS_CAMERA,
=======
MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS,
>>>>>>> main
// options -> account
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
@ -141,6 +145,7 @@ enum
MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS,
// debug->logging
MAINFRAME_MENU_ID_DEBUG_LOGGING_MESSAGE = 21499,
MAINFRAME_MENU_ID_DEBUG_LOGGING0 = 21500,
MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO = 21599,
// debug->dump
@ -189,7 +194,11 @@ EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_AUDIO, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput)
<<<<<<< camera
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_CAMERA, MainWindow::OnOptionsInput)
=======
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS, MainWindow::OnOptionsInput)
>>>>>>> main
// tools menu
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
@ -289,8 +298,13 @@ private:
};
MainWindow::MainWindow()
: wxFrame(nullptr, -1, GetInitialWindowTitle(), wxDefaultPosition, wxSize(1280, 720), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER)
: wxFrame(nullptr, wxID_ANY, GetInitialWindowTitle(), wxDefaultPosition, wxSize(1280, 720), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER)
{
#ifdef __WXMAC__
// Not necessary to set wxApp::s_macExitMenuItemId as automatically handled
wxApp::s_macAboutMenuItemId = MAINFRAME_MENU_ID_HELP_ABOUT;
wxApp::s_macPreferencesMenuItemId = MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS;
#endif
gui_initHandleContextFromWxWidgetsWindow(g_window_info.window_main, this);
g_mainFrame = this;
CafeSystem::SetImplementation(this);
@ -914,6 +928,7 @@ void MainWindow::OnOptionsInput(wxCommandEvent& event)
break;
}
case MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS:
case MAINFRAME_MENU_ID_OPTIONS_GENERAL2:
{
OpenSettings();
@ -1868,7 +1883,7 @@ public:
auto versionString = formatWxString(_("Cemu\nVersion {0}\nCompiled on {1}\nOriginal authors: {2}"), BUILD_VERSION_STRING, BUILD_DATE, "Exzap, Petergov");
sizer->Add(new wxStaticText(parent, wxID_ANY, versionString), wxSizerFlags().Border(wxALL, 3).Border(wxTOP, 10));
sizer->Add(new wxHyperlinkCtrl(parent, -1, "https://cemu.info", "https://cemu.info"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 3));
sizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://cemu.info", "https://cemu.info"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 3));
sizer->AddSpacer(3);
sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4));
@ -1888,95 +1903,105 @@ public:
// zLib
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "zLib ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.zlib.net", "https://www.zlib.net"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "zLib ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.zlib.net", "https://www.zlib.net"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
// wxWidgets
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "wxWidgets ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.wxwidgets.org/", "https://www.wxwidgets.org/"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "wxWidgets ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.wxwidgets.org/", "https://www.wxwidgets.org/"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
// OpenSSL
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "OpenSSL ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.openssl.org/", "https://www.openssl.org/"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "OpenSSL ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.openssl.org/", "https://www.openssl.org/"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
// libcurl
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "libcurl ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://curl.haxx.se/libcurl/", "https://curl.haxx.se/libcurl/"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "libcurl ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://curl.haxx.se/libcurl/", "https://curl.haxx.se/libcurl/"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
// imgui
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "imgui ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/ocornut/imgui", "https://github.com/ocornut/imgui"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "imgui ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/ocornut/imgui", "https://github.com/ocornut/imgui"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
// fontawesome
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "fontawesome ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/FortAwesome/Font-Awesome", "https://github.com/FortAwesome/Font-Awesome"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "fontawesome ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/FortAwesome/Font-Awesome", "https://github.com/FortAwesome/Font-Awesome"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
// boost
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "boost ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.boost.org", "https://www.boost.org"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "boost ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.boost.org", "https://www.boost.org"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
// libusb
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "libusb ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://libusb.info", "https://libusb.info"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "libusb ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://libusb.info", "https://libusb.info"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
#if BOOST_OS_MACOS
// MoltenVK
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "MoltenVK ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/KhronosGroup/MoltenVK", "https://github.com/KhronosGroup/MoltenVK"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
#endif
// icons
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "icons from "), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://icons8.com", "https://icons8.com"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "icons from "), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://icons8.com", "https://icons8.com"), 0);
sizer->Add(lineSizer);
}
// Lato font (are we still using it?)
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "\"Lato\" font by tyPoland Lukasz Dziedzic (OFL, V1.1)"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "\"Lato\" font by tyPoland Lukasz Dziedzic (OFL, V1.1)"), 0);
sizer->Add(lineSizer);
}
// SDL
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "SDL ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/libsdl-org/SDL", "https://github.com/libsdl-org/SDL"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "SDL ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/libsdl-org/SDL", "https://github.com/libsdl-org/SDL"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
// IH264
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "Modified ih264 from Android project ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "Source", "https://cemu.info/oss/ih264d.zip"), 0);
lineSizer->Add(new wxStaticText(parent, -1, " "), 0);
wxHyperlinkCtrl* noticeLink = new wxHyperlinkCtrl(parent, -1, "NOTICE", "");
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "Modified ih264 from Android project ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "Source", "https://cemu.info/oss/ih264d.zip"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, " "), 0);
wxHyperlinkCtrl* noticeLink = new wxHyperlinkCtrl(parent, wxID_ANY, "NOTICE", "");
noticeLink->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& event)
{
fs::path tempPath = fs::temp_directory_path();
@ -2010,7 +2035,7 @@ public:
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(tempPath))));
});
lineSizer->Add(noticeLink, 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer);
}
}
@ -2044,7 +2069,7 @@ public:
wxString& nameList = ((i % 2) == 0) ? nameListLeft : nameListRight;
if (i >= 2)
nameList.append("\n");
nameList.append(name);
nameList.append(wxString::FromUTF8(name));
}
gridSizer->Add(new wxStaticText(parent, wxID_ANY, nameListLeft), wxSizerFlags());
@ -2174,6 +2199,9 @@ void MainWindow::RecreateMenu()
m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString);
m_padViewMenuItem->Check(GetConfig().pad_open);
optionsMenu->AppendSeparator();
#if BOOST_OS_MACOS
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS, _("&Settings..." "\tCtrl-,"));
#endif
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, _("&General settings"));
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings"));
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_CAMERA, _("&Camera settings"));
@ -2223,7 +2251,7 @@ void MainWindow::RecreateMenu()
debugLoggingMenu->AppendSeparator();
wxMenu* logCosModulesMenu = new wxMenu();
logCosModulesMenu->AppendCheckItem(0, _("&Options below are for experts. Leave off if unsure"), wxEmptyString)->Enable(false);
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING_MESSAGE, _("&Options below are for experts. Leave off if unsure"), wxEmptyString)->Enable(false);
logCosModulesMenu->AppendSeparator();
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitFile), _("coreinit File-Access API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitFile));
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThreadSync), _("coreinit Thread-Synchronization API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThreadSync));

View file

@ -5,6 +5,7 @@
#include <vector>
#include <sstream>
#include <thread>
#include <wx/listctrl.h>
#include "config/ActiveSettings.h"
#include "gui/helpers/wxHelpers.h"
@ -79,7 +80,7 @@ MemorySearcherTool::MemorySearcherTool(wxFrame* parent)
m_gauge->Enable(false);
m_textEntryTable = new wxStaticText(this, wxID_ANY, _("Results"));
m_listResults = new wxListCtrl(this, LIST_RESULTS, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SORT_ASCENDING);
m_listResults = new wxListView(this, LIST_RESULTS, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SORT_ASCENDING);
m_listResults->Bind(wxEVT_LEFT_DCLICK, &MemorySearcherTool::OnResultListClick, this);
{
wxListItem col0;
@ -388,14 +389,8 @@ void MemorySearcherTool::OnEntryListRightClick(wxDataViewEvent& event)
void MemorySearcherTool::OnResultListClick(wxMouseEvent& event)
{
long selectedIndex = -1;
while (true)
for (long selectedIndex = m_listResults->GetFirstSelected(); selectedIndex != wxNOT_FOUND; selectedIndex = m_listResults->GetNextSelected(selectedIndex))
{
selectedIndex = m_listResults->GetNextItem(selectedIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selectedIndex == -1)
break;
long address = m_listResults->GetItemData(selectedIndex);
auto currValue = m_listResults->GetItemText(selectedIndex, 1);
@ -684,7 +679,7 @@ void MemorySearcherTool::OnPopupClick(wxCommandEvent& event)
if (event.GetId() == LIST_ENTRY_REMOVE)
{
const int row = m_listEntryTable->GetSelectedRow();
if (row == -1)
if (row == wxNOT_FOUND)
return;
m_listEntryTable->DeleteItem(row);
@ -700,7 +695,7 @@ void MemorySearcherTool::OnItemEdited(wxDataViewEvent& event)
else if (column == 3)
{
auto row = m_listEntryTable->GetSelectedRow();
if (row == -1)
if (row == wxNOT_FOUND)
return;
auto addressText = std::string(m_listEntryTable->GetTextValue(row, 1).mbc_str());

View file

@ -173,7 +173,7 @@ wxDECLARE_EVENT_TABLE();
wxComboBox* m_cbDataType;
wxTextCtrl* m_textValue;
wxButton *m_buttonStart, *m_buttonFilter;
wxListCtrl* m_listResults;
wxListView* m_listResults;
wxDataViewListCtrl* m_listEntryTable;
wxStaticText* m_textEntryTable;
wxGauge* m_gauge;

View file

@ -356,7 +356,7 @@ void TitleManager::OnTitleSearchComplete(wxCommandEvent& event)
void TitleManager::OnSetStatusBarText(wxSetStatusBarTextEvent& event)
{
m_status_bar->SetStatusText(_(event.GetText()), event.GetNumber());
m_status_bar->SetStatusText(event.GetText(), event.GetNumber());
}
void TitleManager::OnFilterChanged(wxCommandEvent& event)

View file

@ -25,9 +25,8 @@
wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent);
wxDownloadManagerList::wxDownloadManagerList(wxWindow* parent, wxWindowID id)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
{
AddColumns();
@ -48,11 +47,13 @@ wxDownloadManagerList::wxDownloadManagerList(wxWindow* parent, wxWindowID id)
Bind(wxEVT_REMOVE_ITEM, &wxDownloadManagerList::OnRemoveItem, this);
Bind(wxEVT_REMOVE_ENTRY, &wxDownloadManagerList::OnRemoveEntry, this);
Bind(wxEVT_CLOSE_WINDOW, &wxDownloadManagerList::OnClose, this);
ShowSortIndicator(ColumnName);
}
boost::optional<const wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetSelectedTitleEntry() const
{
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection != wxNOT_FOUND)
{
const auto tmp = GetTitleEntry(selection);
@ -65,7 +66,7 @@ boost::optional<const wxDownloadManagerList::TitleEntry&> wxDownloadManagerList:
boost::optional<wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetSelectedTitleEntry()
{
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection != wxNOT_FOUND)
{
const auto tmp = GetTitleEntry(selection);
@ -218,16 +219,7 @@ void wxDownloadManagerList::OnColumnClick(wxListEvent& event)
{
const int column = event.GetColumn();
if (column == m_sort_by_column)
{
m_sort_less = !m_sort_less;
}
else
{
m_sort_by_column = column;
m_sort_less = true;
}
SortEntries();
SortEntries(column);
event.Skip();
}
@ -324,7 +316,7 @@ void wxDownloadManagerList::OnContextMenu(wxContextMenuEvent& event)
wxMenu menu;
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxDownloadManagerList::OnContextMenuSelected, this);
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection == wxNOT_FOUND)
return;
@ -379,8 +371,8 @@ void wxDownloadManagerList::OnContextMenuSelected(wxCommandEvent& event)
// still doing work
if (m_context_worker.valid() && !future_is_ready(m_context_worker))
return;
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection == wxNOT_FOUND)
return;
@ -621,24 +613,31 @@ bool wxDownloadManagerList::SortFunc(std::span<int> sortColumnOrder, const Type_
#include <boost/container/small_vector.hpp>
void wxDownloadManagerList::SortEntries()
void wxDownloadManagerList::SortEntries(int column)
{
boost::container::small_vector<int, 12> s_SortColumnOrder{ ColumnName, ColumnType, ColumnVersion, ColumnTitleId, ColumnProgress };
if (m_sort_by_column != -1)
bool ascending;
if (column == -1)
{
// prioritize column by moving it to first position in the column sort order list
s_SortColumnOrder.erase(std::remove(s_SortColumnOrder.begin(), s_SortColumnOrder.end(), m_sort_by_column), s_SortColumnOrder.end());
s_SortColumnOrder.insert(s_SortColumnOrder.begin(), m_sort_by_column);
column = GetSortIndicator();
if (column == -1)
column = ColumnName;
ascending = IsAscendingSortIndicator();
}
else
ascending = GetUpdatedAscendingSortIndicator(column);
// prioritize column by moving it to first position in the column sort order list
s_SortColumnOrder.erase(std::remove(s_SortColumnOrder.begin(), s_SortColumnOrder.end(), column), s_SortColumnOrder.end());
s_SortColumnOrder.insert(s_SortColumnOrder.begin(), column);
std::sort(m_sorted_data.begin(), m_sorted_data.end(),
[this, &s_SortColumnOrder](const Type_t& v1, const Type_t& v2) -> bool
{
const bool result = SortFunc({ s_SortColumnOrder.data(), s_SortColumnOrder.size() }, v1, v2);
return m_sort_less ? result : !result;
});
[this, &s_SortColumnOrder, ascending](const Type_t& v1, const Type_t& v2) -> bool {
return ascending ? SortFunc(s_SortColumnOrder, v1, v2) : SortFunc(s_SortColumnOrder, v2, v1);
});
ShowSortIndicator(column, ascending);
RefreshPage();
}

View file

@ -9,7 +9,7 @@
#include <utility>
#include <vector>
class wxDownloadManagerList : public wxListCtrl
class wxDownloadManagerList : public wxListView
{
friend class TitleManager;
public:
@ -49,7 +49,7 @@ public:
// error state?
};
void SortEntries();
void SortEntries(int column = -1);
void RefreshPage();
void Filter(const wxString& filter);
void Filter2(bool showTitles, bool showUpdates, bool showInstalled);
@ -138,9 +138,6 @@ private:
std::vector<ItemDataPtr> m_data;
std::vector<std::reference_wrapper<ItemData>> m_sorted_data;
int m_sort_by_column = ItemColumn::ColumnName;
bool m_sort_less = true;
bool m_filterShowTitles = true;
bool m_filterShowUpdates = true;
bool m_filterShowInstalled = true;

View file

@ -6,6 +6,7 @@
#include <numeric>
#include <wx/listctrl.h>
#include <wx/wupdlock.h>
#include <wx/menu.h>
#include <wx/mstream.h>
@ -44,6 +45,7 @@
#include <objidl.h>
#include <shlguid.h>
#include <shlobj.h>
#include <wrl/client.h>
#endif
// public events
@ -82,8 +84,58 @@ std::list<fs::path> _getCachesPaths(const TitleId& titleId)
return cachePaths;
}
// Convert PNG to Apple icon image format
bool writeICNS(const fs::path& pngPath, const fs::path& icnsPath) {
// Read PNG file
std::ifstream pngFile(pngPath, std::ios::binary);
if (!pngFile)
return false;
// Get PNG size
pngFile.seekg(0, std::ios::end);
uint32 pngSize = static_cast<uint32>(pngFile.tellg());
pngFile.seekg(0, std::ios::beg);
// Calculate total file size (header + size + type + data)
uint32 totalSize = 8 + 8 + pngSize;
// Create output file
std::ofstream icnsFile(icnsPath, std::ios::binary);
if (!icnsFile)
return false;
// Write ICNS header
icnsFile.put(0x69); // 'i'
icnsFile.put(0x63); // 'c'
icnsFile.put(0x6e); // 'n'
icnsFile.put(0x73); // 's'
// Write total file size (big endian)
icnsFile.put((totalSize >> 24) & 0xFF);
icnsFile.put((totalSize >> 16) & 0xFF);
icnsFile.put((totalSize >> 8) & 0xFF);
icnsFile.put(totalSize & 0xFF);
// Write icon type (ic07 = 128x128 PNG)
icnsFile.put(0x69); // 'i'
icnsFile.put(0x63); // 'c'
icnsFile.put(0x30); // '0'
icnsFile.put(0x37); // '7'
// Write PNG size (big endian)
icnsFile.put((pngSize >> 24) & 0xFF);
icnsFile.put((pngSize >> 16) & 0xFF);
icnsFile.put((pngSize >> 8) & 0xFF);
icnsFile.put(pngSize & 0xFF);
// Copy PNG data
icnsFile << pngFile.rdbuf();
return true;
}
wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, GetStyleFlags(Style::kList)), m_style(Style::kList)
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, GetStyleFlags(Style::kList)), m_style(Style::kList)
{
const auto& config = GetConfig();
@ -142,6 +194,8 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
// start async worker (for icon loading)
m_async_worker_active = true;
m_async_worker_thread = std::thread(&wxGameList::AsyncWorkerThread, this);
ShowSortIndicator(ColumnName);
}
wxGameList::~wxGameList()
@ -343,7 +397,7 @@ void wxGameList::SetStyle(Style style, bool save)
SetWindowStyleFlag(GetStyleFlags(m_style));
uint64 selected_title_id = 0;
auto selection = GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
auto selection = GetFirstSelected();
if (selection != wxNOT_FOUND)
{
selected_title_id = (uint64)GetItemData(selection);
@ -366,8 +420,8 @@ void wxGameList::SetStyle(Style style, bool save)
if(selection != wxNOT_FOUND)
{
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
EnsureVisible(selection);
Select(selection);
Focus(selection);
}
if(save)
@ -434,44 +488,71 @@ static inline int order_to_int(const std::weak_ordering &wo)
return 0;
}
int wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData)
std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData)
{
const auto isFavoriteA = GetConfig().IsGameListFavorite(titleId1);
const auto isFavoriteB = GetConfig().IsGameListFavorite(titleId2);
const auto& name1 = GetNameByTitleId(titleId1);
const auto& name2 = GetNameByTitleId(titleId2);
auto titleLastPlayed = [](uint64_t id)
{
iosu::pdm::GameListStat playTimeStat{};
iosu::pdm::GetStatForGamelist(id, playTimeStat);
return playTimeStat;
};
if(sortData->dir > 0)
return order_to_int(std::tie(isFavoriteB, name1) <=> std::tie(isFavoriteA, name2));
else
return order_to_int(std::tie(isFavoriteB, name2) <=> std::tie(isFavoriteA, name1));
auto titlePlayMinutes = [](uint64_t id)
{
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();
};
switch(sortData->column)
{
default:
case ColumnName:
{
const auto isFavoriteA = GetConfig().IsGameListFavorite(titleId1);
const auto isFavoriteB = GetConfig().IsGameListFavorite(titleId2);
const auto nameA = GetNameByTitleId(titleId1);
const auto nameB = GetNameByTitleId(titleId2);
return std::tie(isFavoriteB, nameA) <=> std::tie(isFavoriteA, nameB);
}
case ColumnGameStarted:
return titleLastPlayed(titleId1).last_played <=> titleLastPlayed(titleId2).last_played;
case ColumnGameTime:
return titlePlayMinutes(titleId1) <=> titlePlayMinutes(titleId2);
case ColumnRegion:
return titleRegion(titleId1) <=> titleRegion(titleId2);
case ColumnTitleID:
return titleId1 <=> titleId2;
}
// unreachable
cemu_assert_debug(false);
return std::weak_ordering::less;
}
int wxGameList::SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
{
const auto sort_data = (SortData*)sortData;
const int dir = sort_data->dir;
return 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)
{
bool ascending;
if (column == -1)
column = s_last_column;
else
{
if (s_last_column == column)
{
s_last_column = 0;
s_direction = -1;
}
else
{
s_last_column = column;
s_direction = 1;
}
column = GetSortIndicator();
if (column == -1)
column = ColumnName;
ascending = IsAscendingSortIndicator();
}
else
ascending = GetUpdatedAscendingSortIndicator(column);
switch (column)
{
@ -479,9 +560,11 @@ void wxGameList::SortEntries(int column)
case ColumnGameTime:
case ColumnGameStarted:
case ColumnRegion:
case ColumnTitleID:
{
SortData data{ this, column, s_direction };
SortData data{this, ItemColumns{column}, ascending ? 1 : -1};
SortItems(SortFunction, (wxIntPtr)&data);
ShowSortIndicator(column, ascending);
break;
}
}
@ -493,21 +576,20 @@ void wxGameList::OnKeyDown(wxListEvent& event)
if (m_style != Style::kList)
return;
const auto keycode = std::tolower(event.m_code);
const auto keycode = event.GetKeyCode();
if (keycode == WXK_LEFT)
{
const auto item_count = GetItemCount();
if (item_count > 0)
{
auto selection = (int)GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
auto selection = (int)GetFirstSelected();
if (selection == wxNOT_FOUND)
selection = 0;
else
selection = std::max(0, selection - GetCountPerPage());
SetItemState(wxNOT_FOUND, 0, wxLIST_STATE_SELECTED);
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
EnsureVisible(selection);
Select(selection);
Focus(selection);
}
}
else if (keycode == WXK_RIGHT)
@ -515,15 +597,14 @@ void wxGameList::OnKeyDown(wxListEvent& event)
const auto item_count = GetItemCount();
if (item_count > 0)
{
auto selection = (int)GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
auto selection = (int)GetFirstSelected();
if (selection == wxNOT_FOUND)
selection = 0;
selection = std::min(item_count - 1, selection + GetCountPerPage());
SetItemState(wxNOT_FOUND, 0, wxLIST_STATE_SELECTED);
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
EnsureVisible(selection);
Select(selection);
Focus(selection);
}
}
}
@ -563,7 +644,7 @@ void wxGameList::OnContextMenu(wxContextMenuEvent& event)
wxMenu menu;
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxGameList::OnContextMenuSelected, this);
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection != wxNOT_FOUND)
{
const auto title_id = (uint64)GetItemData(selection);
@ -596,9 +677,7 @@ void wxGameList::OnContextMenu(wxContextMenuEvent& event)
menu.Append(kContextMenuEditGameProfile, _("&Edit game profile"));
menu.AppendSeparator();
#if BOOST_OS_LINUX || BOOST_OS_WINDOWS
menu.Append(kContextMenuCreateShortcut, _("&Create shortcut"));
#endif
menu.AppendSeparator();
menu.Append(kContextMenuCopyTitleName, _("&Copy Title Name"));
menu.Append(kContextMenuCopyTitleId, _("&Copy Title ID"));
@ -724,9 +803,7 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event)
}
case kContextMenuCreateShortcut:
{
#if BOOST_OS_LINUX || BOOST_OS_WINDOWS
CreateShortcut(gameInfo);
#endif
break;
}
case kContextMenuCopyTitleName:
@ -1004,7 +1081,7 @@ void wxGameList::OnClose(wxCloseEvent& event)
int wxGameList::FindInsertPosition(TitleId titleId)
{
SortData data{ this, s_last_column, s_direction };
SortData data{this, ItemColumns(GetSortIndicator()), IsAscendingSortIndicator()};
const auto itemCount = GetItemCount();
if (itemCount == 0)
return 0;
@ -1372,6 +1449,135 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
}
outputStream << desktopEntryString;
}
#elif BOOST_OS_MACOS
void wxGameList::CreateShortcut(GameInfo2& gameInfo)
{
const auto titleId = gameInfo.GetBaseTitleId();
const auto titleName = wxString::FromUTF8(gameInfo.GetTitleName());
auto exePath = ActiveSettings::GetExecutablePath();
const wxString appName = wxString::Format("%s.app", titleName);
wxFileDialog entryDialog(this, _("Choose shortcut location"), "~/Applications", appName,
"Application (*.app)|*.app", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT);
const auto result = entryDialog.ShowModal();
if (result == wxID_CANCEL)
return;
const auto output_path = entryDialog.GetPath();
// Create .app folder
const fs::path appPath = output_path.utf8_string();
if (!fs::create_directories(appPath))
{
cemuLog_log(LogType::Force, "Failed to create app directory");
return;
}
const fs::path infoPath = appPath / "Contents/Info.plist";
const fs::path scriptPath = appPath / "Contents/MacOS/run.sh";
const fs::path icnsPath = appPath / "Contents/Resources/shortcut.icns";
if (!(fs::create_directories(scriptPath.parent_path()) && fs::create_directories(icnsPath.parent_path())))
{
cemuLog_log(LogType::Force, "Failed to create app shortcut directories");
return;
}
std::optional<fs::path> iconPath;
// Obtain and convert icon
[&]()
{
int iconIndex, smallIconIndex;
if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex))
{
cemuLog_log(LogType::Force, "Icon hasn't loaded");
return;
}
const fs::path outIconDir = fs::temp_directory_path();
if (!fs::exists(outIconDir) && !fs::create_directories(outIconDir))
{
cemuLog_log(LogType::Force, "Failed to create icon directory");
return;
}
iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId());
wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value()));
auto image = m_image_list->GetIcon(iconIndex).ConvertToImage();
wxPNGHandler pngHandler;
if (!pngHandler.SaveFile(&image, pngFileStream, false))
{
iconPath = std::nullopt;
cemuLog_log(LogType::Force, "Icon failed to save");
}
}();
std::string runCommand = fmt::format("#!/bin/zsh\n\n{0:?} --title-id {1:016x}", _pathToUtf8(exePath), titleId);
const std::string infoPlist = fmt::format(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">\n"
"<dict>\n"
" <key>CFBundleDisplayName</key>\n"
" <string>{0}</string>\n"
" <key>CFBundleExecutable</key>\n"
" <string>run.sh</string>\n"
" <key>CFBundleIconFile</key>\n"
" <string>shortcut.icns</string>\n"
" <key>CFBundleName</key>\n"
" <string>{0}</string>\n"
" <key>CFBundlePackageType</key>\n"
" <string>APPL</string>\n"
" <key>CFBundleSignature</key>\n"
" <string>\?\?\?\?</string>\n"
" <key>LSApplicationCategoryType</key>\n"
" <string>public.app-category.games</string>\n"
" <key>CFBundleShortVersionString</key>\n"
" <string>{1}</string>\n"
" <key>CFBundleVersion</key>\n"
" <string>{1}</string>\n"
"</dict>\n"
"</plist>\n",
gameInfo.GetTitleName(),
std::to_string(gameInfo.GetVersion())
);
// write Info.plist to infoPath
std::ofstream infoStream(infoPath);
std::ofstream scriptStream(scriptPath);
if (!infoStream.good() || !scriptStream.good())
{
auto errorMsg = formatWxString(_("Failed to save app shortcut to {}"), output_path.utf8_string());
wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return;
}
infoStream << infoPlist;
scriptStream << runCommand;
scriptStream.close();
// Set execute permissions for script
fs::permissions(
scriptPath,
fs::perms::owner_exec | fs::perms::group_exec | fs::perms::others_exec,
fs::perm_options::add
);
// Return if iconPath is empty
if (!iconPath)
{
cemuLog_log(LogType::Force, "Icon not found");
return;
}
// Convert icon to icns, only works for 128x128 PNG
// Alternatively, can run the command "sips -s format icns {iconPath} --out '{icnsPath}'"
// using std::system() to handle images of any size
if (!writeICNS(*iconPath, icnsPath))
{
cemuLog_log(LogType::Force, "Failed to convert icon to icns");
return;
}
// Remove temp file
fs::remove(*iconPath);
}
#elif BOOST_OS_WINDOWS
void wxGameList::CreateShortcut(GameInfo2& gameInfo)
{
@ -1425,8 +1631,8 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
}
}
IShellLinkW* shellLink;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast<LPVOID*>(&shellLink));
Microsoft::WRL::ComPtr<IShellLinkW> shellLink;
HRESULT hres = CoCreateInstance(__uuidof(ShellLink), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
if (SUCCEEDED(hres))
{
const auto description = wxString::Format("Play %s on Cemu", titleName);
@ -1442,19 +1648,17 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
else
shellLink->SetIconLocation(exePath.wstring().c_str(), 0);
IPersistFile* shellLinkFile;
Microsoft::WRL::ComPtr<IPersistFile> shellLinkFile;
// save the shortcut
hres = shellLink->QueryInterface(IID_IPersistFile, reinterpret_cast<LPVOID*>(&shellLinkFile));
hres = shellLink.As(&shellLinkFile);
if (SUCCEEDED(hres))
{
hres = shellLinkFile->Save(outputPath.wc_str(), TRUE);
shellLinkFile->Release();
}
shellLink->Release();
}
if (!SUCCEEDED(hres)) {
auto errorMsg = formatWxString(_("Failed to save shortcut to {}"), outputPath);
wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
}
}
#endif
#endif

View file

@ -30,7 +30,7 @@ wxDECLARE_EVENT(wxEVT_OPEN_GRAPHIC_PACK, wxTitleIdEvent);
wxDECLARE_EVENT(wxEVT_GAMELIST_BEGIN_UPDATE, wxCommandEvent);
wxDECLARE_EVENT(wxEVT_GAMELIST_END_UPDATE, wxCommandEvent);
class wxGameList : public wxListCtrl
class wxGameList : public wxListView
{
friend class MainWindow;
public:
@ -53,9 +53,7 @@ public:
void ReloadGameEntries(bool cached = false);
void DeleteCachedStrings();
#if BOOST_OS_LINUX || BOOST_OS_WINDOWS
void CreateShortcut(GameInfo2& gameInfo);
#endif
long FindListItemByTitleId(uint64 title_id) const;
void OnClose(wxCloseEvent& event);
@ -70,7 +68,7 @@ private:
inline static const wxColour kSecondColor{ 0xFDF9F2 };
void UpdateItemColors(sint32 startIndex = 0);
enum ItemColumns
enum ItemColumns : int
{
ColumnHiddenName = 0,
ColumnIcon,
@ -85,18 +83,16 @@ private:
ColumnCounts,
};
int s_last_column = ColumnName;
int s_direction = 1;
void SortEntries(int column = -1);
struct SortData
{
wxGameList* thisptr;
int column;
ItemColumns column;
int dir;
};
int FindInsertPosition(TitleId titleId);
int SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData);
std::weak_ordering SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData);
static int SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData);
wxTimer* m_tooltip_timer;

View file

@ -38,7 +38,7 @@ wxDEFINE_EVENT(wxEVT_TITLE_REMOVED, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent);
wxTitleManagerList::wxTitleManagerList(wxWindow* parent, wxWindowID id)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
{
AddColumns();
@ -64,6 +64,8 @@ wxTitleManagerList::wxTitleManagerList(wxWindow* parent, wxWindowID id)
m_callbackIdTitleList = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((wxTitleManagerList*)ctx)->HandleTitleListCallback(evt); }, this);
m_callbackIdSaveList = CafeSaveList::RegisterCallback([](CafeSaveListCallbackEvent* evt, void* ctx) { ((wxTitleManagerList*)ctx)->HandleSaveListCallback(evt); }, this);
ShowSortIndicator(ColumnTitleId);
}
wxTitleManagerList::~wxTitleManagerList()
@ -74,7 +76,7 @@ wxTitleManagerList::~wxTitleManagerList()
boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry() const
{
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection != wxNOT_FOUND)
{
const auto tmp = GetTitleEntry(selection);
@ -87,7 +89,7 @@ boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSe
boost::optional<wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry()
{
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection != wxNOT_FOUND)
{
const auto tmp = GetTitleEntry(selection);
@ -574,7 +576,7 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId, uint64 righ
}
else
{
progressDialog.Update(0, _("Collecting list of files..." + fmt::format(" ({})", writerContext.totalFileCount.load())));
progressDialog.Update(0, _("Collecting list of files...") + fmt::format(" ({})", writerContext.totalFileCount.load()));
}
if (progressDialog.WasCancelled())
writerContext.cancelled.store(true);
@ -757,7 +759,7 @@ void wxTitleManagerList::OnContextMenu(wxContextMenuEvent& event)
wxMenu menu;
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxTitleManagerList::OnContextMenuSelected, this);
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection == wxNOT_FOUND)
return;
@ -855,8 +857,8 @@ void wxTitleManagerList::OnContextMenuSelected(wxCommandEvent& event)
// still doing work
if (m_context_worker.valid() && !future_is_ready(m_context_worker))
return;
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const auto selection = GetFirstSelected();
if (selection == wxNOT_FOUND)
return;
@ -1173,54 +1175,48 @@ bool wxTitleManagerList::SortFunc(int column, const Type_t& v1, const Type_t& v2
{
if(entry1.version == entry2.version)
return SortFunc(ColumnTitleId, v1, v2);
return std::underlying_type_t<EntryType>(entry1.version) < std::underlying_type_t<EntryType>(entry2.version);
return entry1.version < entry2.version;
}
else if (column == ColumnRegion)
{
if(entry1.region == entry2.region)
return SortFunc(ColumnTitleId, v1, v2);
return std::underlying_type_t<EntryType>(entry1.region) < std::underlying_type_t<EntryType>(entry2.region);
return std::underlying_type_t<CafeConsoleRegion>(entry1.region) < std::underlying_type_t<CafeConsoleRegion>(entry2.region);
}
else if (column == ColumnFormat)
{
if(entry1.format == entry2.format)
return SortFunc(ColumnType, v1, v2);
return std::underlying_type_t<EntryType>(entry1.format) < std::underlying_type_t<EntryType>(entry2.format);
return std::underlying_type_t<EntryFormat>(entry1.format) < std::underlying_type_t<EntryFormat>(entry2.format);
}
return false;
}
void wxTitleManagerList::SortEntries(int column)
{
if(column == -1)
bool ascending;
if (column == -1)
{
column = m_last_column_sorted;
m_last_column_sorted = -1;
column = GetSortIndicator();
if (column == -1)
column = ColumnTitleId;
ascending = IsAscendingSortIndicator();
}
else
ascending = GetUpdatedAscendingSortIndicator(column);
if (column != ColumnTitleId && column != ColumnName && column != ColumnType && column != ColumnVersion && column != ColumnRegion && column != ColumnFormat)
return;
if (m_last_column_sorted != column)
{
m_last_column_sorted = column;
m_sort_less = true;
}
else
m_sort_less = !m_sort_less;
std::sort(m_sorted_data.begin(), m_sorted_data.end(),
[this, column](const Type_t& v1, const Type_t& v2) -> bool
{
const bool result = SortFunc(column, v1, v2);
return m_sort_less ? result : !result;
});
[this, column, ascending](const Type_t& v1, const Type_t& v2) -> bool {
return ascending ? SortFunc(column, v1, v2) : SortFunc(column, v2, v1);
});
ShowSortIndicator(column, ascending);
RefreshPage();
}

View file

@ -9,7 +9,7 @@
#include <utility>
#include <vector>
class wxTitleManagerList : public wxListCtrl
class wxTitleManagerList : public wxListView
{
friend class TitleManager;
public:
@ -127,8 +127,6 @@ private:
std::vector<ItemDataPtr> m_data;
std::vector<std::reference_wrapper<ItemData>> m_sorted_data;
int m_last_column_sorted = -1;
bool m_sort_less = true;
using Type_t = std::reference_wrapper<const ItemData>;
bool SortFunc(int column, const Type_t& v1, const Type_t& v2);

View file

@ -230,8 +230,8 @@ void BreakpointWindow::OnRightDown(wxMouseEvent& event)
}
else
{
m_breakpoints->SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
m_breakpoints->SetItemState(index, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
m_breakpoints->Focus(index);
m_breakpoints->Select(index);
wxMenu menu;
menu.Append(MENU_ID_DELETE_BP, _("Delete breakpoint"));
@ -245,8 +245,8 @@ void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt)
{
if (evt.GetId() == MENU_ID_DELETE_BP)
{
long sel = m_breakpoints->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (sel != -1)
long sel = m_breakpoints->GetFirstSelected();
if (sel != wxNOT_FOUND)
{
if (sel >= debuggerState.breakpoints.size())
return;

View file

@ -615,7 +615,7 @@ void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
{
// double-clicked on disassembly (operation and operand data)
wxString currentInstruction = wxEmptyString;
wxTextEntryDialog set_value_dialog(this, _("Enter a new instruction."), _(wxString::Format("Overwrite instruction at address %08x", virtualAddress)), currentInstruction);
wxTextEntryDialog set_value_dialog(this, _("Enter a new instruction."), wxString::Format(_("Overwrite instruction at address %08x"), virtualAddress), currentInstruction);
if (set_value_dialog.ShowModal() == wxID_OK)
{
PPCAssemblerInOut ctx = { 0 };
@ -637,7 +637,7 @@ void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
if (comment && comment->type == RplDebugSymbolComment)
old_comment = comment->comment;
wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), _(wxString::Format("Create comment at address %08x", virtualAddress)), old_comment);
wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), wxString::Format(_("Create comment at address %08x"), virtualAddress), old_comment);
if (set_value_dialog.ShowModal() == wxID_OK)
{
rplDebugSymbol_createComment(virtualAddress, set_value_dialog.GetValue().wc_str());
@ -850,4 +850,4 @@ void DisasmCtrl::GoToAddressDialog()
}
}
}
}
}

View file

@ -196,7 +196,7 @@ void DumpCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
const uint32 offset = LineToOffset(line) + byte_index;
const uint8 value = memory_readU8(offset);
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set byte at address %08x", offset)), wxString::Format("%02x", value));
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set byte at address %08x"), offset), wxString::Format("%02x", value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const uint8 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
@ -303,4 +303,4 @@ void DumpCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position)
wxSize DumpCtrl::DoGetBestSize() const
{
return TextList::DoGetBestSize();
}
}

View file

@ -126,7 +126,7 @@ void ModuleWindow::OnGameLoaded()
void ModuleWindow::OnLeftDClick(wxMouseEvent& event)
{
long selected = m_modules->GetFirstSelected();
if (selected == -1)
if (selected == wxNOT_FOUND)
return;
const auto text = m_modules->GetItemText(selected, ColumnAddress);
const auto address = std::stoul(text.ToStdString(), nullptr, 16);

View file

@ -201,7 +201,7 @@ void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
if (position.x <= OFFSET_REGISTER + OFFSET_REGISTER_LABEL)
{
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index];
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set R%d value", register_index)), wxString::Format("%08x", register_value));
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set R%d value"), register_index), wxString::Format("%08x", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
@ -220,7 +220,7 @@ void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
if (position.x <= OFFSET_REGISTER + OFFSET_FPR)
{
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0;
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP0_%d value", register_index)), wxString::Format("%lf", register_value));
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP0_%d value"), register_index), wxString::Format("%lf", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
@ -234,7 +234,7 @@ void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
else
{
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1;
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP1_%d value", register_index)), wxString::Format("%lf", register_value));
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP1_%d value"), register_index), wxString::Format("%lf", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());

View file

@ -339,7 +339,7 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event)
{
const uint32 register_index = id - kRegisterValueR0;
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index];
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set R%d value", register_index)), wxString::Format("%08x", register_value));
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set R%d value"), register_index), wxString::Format("%08x", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
@ -355,7 +355,7 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event)
{
const uint32 register_index = id - kRegisterValueFPR0_0;
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0;
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP0_%d value", register_index)), wxString::Format("%lf", register_value));
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP0_%d value"), register_index), wxString::Format("%lf", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
@ -371,7 +371,7 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event)
{
const uint32 register_index = id - kRegisterValueFPR1_0;
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1;
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP1_%d value", register_index)), wxString::Format("%lf", register_value));
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP1_%d value"), register_index), wxString::Format("%lf", register_value));
if (set_value_dialog.ShowModal() == wxID_OK)
{
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());

View file

@ -2,6 +2,7 @@
#include "gui/guiWrapper.h"
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
#include <wx/listctrl.h>
enum ItemColumns
{
@ -10,8 +11,7 @@ enum ItemColumns
ColumnModule,
};
SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size) :
wxListCtrl(parent, id, pos, size, wxLC_REPORT | wxLC_VIRTUAL)
SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size) : wxListView(parent, id, pos, size, wxLC_REPORT | wxLC_VIRTUAL)
{
wxListItem col0;
col0.SetId(ColumnName);
@ -106,8 +106,8 @@ wxString SymbolListCtrl::OnGetItemText(long item, long column) const
void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
{
long selected = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selected == -1)
long selected = GetFirstSelected();
if (selected == wxNOT_FOUND)
return;
const auto text = GetItemText(selected, ColumnAddress);
const auto address = std::stoul(text.ToStdString(), nullptr, 16);
@ -119,8 +119,8 @@ void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
void SymbolListCtrl::OnRightClick(wxListEvent& event)
{
long selected = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selected == -1)
long selected = GetFirstSelected();
if (selected == wxNOT_FOUND)
return;
auto text = GetItemText(selected, ColumnAddress);
text = "0x" + text;
@ -162,4 +162,4 @@ void SymbolListCtrl::ChangeListFilter(std::string filter)
SetItemCount(visible_entries);
if (visible_entries > 0)
RefreshItems(GetTopItem(), std::min<long>(visible_entries - 1, GetTopItem() + GetCountPerPage() + 1));
}
}

View file

@ -2,7 +2,7 @@
#include <wx/listctrl.h>
class SymbolListCtrl : public wxListCtrl
class SymbolListCtrl : public wxListView
{
public:
SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size);

View file

@ -12,7 +12,7 @@ public:
: wxCommandEvent(EVT_LOG), m_filter(filter), m_message(message) { }
wxLogEvent(const wxLogEvent& event)
: wxCommandEvent(event), m_filter(event.m_filter), m_message(event.m_message) { }
: wxCommandEvent(event), m_filter(event.GetFilter()), m_message(event.GetMessage()) { }
wxEvent* Clone() const { return new wxLogEvent(*this); }

View file

@ -23,7 +23,7 @@ using wxControllerData = wxCustomData<ControllerPtr>;
InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position,
const std::vector<ControllerPtr>& controllers)
: wxDialog(parent, wxID_ANY, "Add input API", position, wxDefaultSize, 0), m_controllers(controllers)
: wxDialog(parent, wxID_ANY, "Add input API", position, wxDefaultSize, wxCAPTION), m_controllers(controllers)
{
this->SetSizeHints(wxDefaultSize, wxDefaultSize);

View file

@ -225,7 +225,7 @@ void PairingDialog::WorkerThread()
BluetoothFindDeviceClose(deviceFind);
}
}
#elif BOOST_OS_LINUX
#elif defined(HAS_BLUEZ)
void PairingDialog::WorkerThread()
{
constexpr static uint8_t LIAC_LAP[] = {0x00, 0x8b, 0x9e};

View file

@ -9,6 +9,7 @@
#include <cinttypes>
#include <helpers/wxHelpers.h>
#include <wx/listctrl.h>
enum
{
@ -42,7 +43,7 @@ DebugPPCThreadsWindow::DebugPPCThreadsWindow(wxFrame& parent)
wxFrame::SetBackgroundColour(*wxWHITE);
auto* sizer = new wxBoxSizer(wxVERTICAL);
m_thread_list = new wxListCtrl(this, GPLIST_ID, wxPoint(0, 0), wxSize(930, 240), wxLC_REPORT);
m_thread_list = new wxListView(this, GPLIST_ID, wxPoint(0, 0), wxSize(930, 240), wxLC_REPORT);
m_thread_list->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Courier New")); //wxSystemSettings::GetFont(wxSYS_OEM_FIXED_FONT));
@ -169,7 +170,7 @@ void DebugPPCThreadsWindow::RefreshThreadList()
wxWindowUpdateLocker lock(m_thread_list);
long selected_thread = 0;
const int selection = m_thread_list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
const int selection = m_thread_list->GetFirstSelected();
if (selection != wxNOT_FOUND)
selected_thread = m_thread_list->GetItemData(selection);
@ -267,12 +268,15 @@ void DebugPPCThreadsWindow::RefreshThreadList()
m_thread_list->SetItem(i, 12, tempStr);
if(selected_thread != 0 && selected_thread == (long)threadItrMPTR)
m_thread_list->SetItemState(i, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
}
srwlock_activeThreadList.UnlockWrite();
__OSUnlockScheduler();
}
if (selected_thread != 0 && selected_thread == (long)threadItrMPTR)
{
m_thread_list->Select(i);
m_thread_list->Focus(i);
}
}
srwlock_activeThreadList.UnlockWrite();
__OSUnlockScheduler();
}
m_thread_list->SetScrollPos(0, scrollPos, true);
}
@ -436,12 +440,11 @@ void DebugPPCThreadsWindow::OnThreadListRightClick(wxMouseEvent& event)
if (itemIndex == wxNOT_FOUND)
return;
// select item
m_thread_list->SetItemState(itemIndex, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
long sel = m_thread_list->GetNextItem(-1, wxLIST_NEXT_ALL,
wxLIST_STATE_SELECTED);
if (sel != -1)
m_thread_list->SetItemState(sel, 0, wxLIST_STATE_SELECTED);
m_thread_list->SetItemState(itemIndex, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
m_thread_list->Focus(itemIndex);
long sel = m_thread_list->GetFirstSelected();
if (sel != wxNOT_FOUND)
m_thread_list->Select(sel, false);
m_thread_list->Select(itemIndex);
// check if thread is still on the list of active threads
MPTR threadMPTR = (MPTR)m_thread_list->GetItemData(itemIndex);
__OSLockScheduler();

View file

@ -23,7 +23,7 @@ private:
void PresentProfileResults(OSThread_t* thread, const std::unordered_map<VAddr, uint32>& samples);
void DumpStackTrace(struct OSThread_t* thread);
wxListCtrl* m_thread_list;
wxListView* m_thread_list;
wxCheckBox* m_auto_refresh;
wxTimer* m_timer;
@ -32,4 +32,4 @@ private:
wxDECLARE_EVENT_TABLE();
};
};

View file

@ -72,7 +72,7 @@ DECLARE_EXPORTED_EVENT_TYPE(WXEXPORT, wxEVT_COMMAND_LIST_ITEM_UNCHECKED, -1);
//! This is the class which performs all transactions with the server.
//! It uses the wxSocket facilities.
class wxCheckedListCtrl : public wxListCtrl
class wxCheckedListCtrl : public wxListView
{
protected:
@ -85,18 +85,18 @@ protected:
public:
wxCheckedListCtrl()
: wxListCtrl(), m_imageList(16, 16, TRUE) {}
: wxListView(), m_imageList(16, 16, TRUE) {}
wxCheckedListCtrl(wxWindow *parent, wxWindowID id = -1,
wxCheckedListCtrl(wxWindow *parent, wxWindowID id = wxID_ANY,
const wxPoint& pt = wxDefaultPosition,
const wxSize& sz = wxDefaultSize,
long style = wxCLC_CHECK_WHEN_SELECTING,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxListCtrlNameStr)
: wxListCtrl(), m_imageList(16, 16, TRUE)
: wxListView(), m_imageList(16, 16, TRUE)
{ Create(parent, id, pt, sz, style, validator, name); }
bool Create(wxWindow *parent, wxWindowID id = -1,
bool Create(wxWindow *parent, wxWindowID id = wxID_ANY,
const wxPoint& pt = wxDefaultPosition,
const wxSize& sz = wxDefaultSize,
long style = wxCLC_CHECK_WHEN_SELECTING,

View file

@ -61,7 +61,7 @@ if(WIN32)
)
endif()
if (ENABLE_WIIMOTE)
if (SUPPORTS_WIIMOTE)
target_compile_definitions(CemuInput PUBLIC SUPPORTS_WIIMOTE)
target_sources(CemuInput PRIVATE
api/Wiimote/WiimoteControllerProvider.h
@ -70,13 +70,17 @@ if (ENABLE_WIIMOTE)
api/Wiimote/NativeWiimoteController.h
api/Wiimote/NativeWiimoteController.cpp
api/Wiimote/WiimoteDevice.h
api/Wiimote/hidapi/HidapiWiimote.cpp
api/Wiimote/hidapi/HidapiWiimote.h
)
if (UNIX AND NOT APPLE)
if (ENABLE_HIDAPI)
target_sources(CemuInput PRIVATE
api/Wiimote/l2cap/L2CapWiimote.cpp
api/Wiimote/l2cap/L2CapWiimote.h)
api/Wiimote/hidapi/HidapiWiimote.cpp
api/Wiimote/hidapi/HidapiWiimote.h)
endif ()
if (ENABLE_BLUEZ)
target_sources(CemuInput PRIVATE
api/Wiimote/l2cap/L2CapWiimote.cpp
api/Wiimote/l2cap/L2CapWiimote.h)
endif()
endif ()
@ -95,6 +99,7 @@ target_link_libraries(CemuInput PRIVATE
pugixml::pugixml
SDL2::SDL2
)
if (ENABLE_HIDAPI)
target_link_libraries(CemuInput PRIVATE hidapi::hidapi)
endif()
@ -103,7 +108,6 @@ if (ENABLE_WXWIDGETS)
target_link_libraries(CemuInput PRIVATE wx::base wx::core)
endif()
if (UNIX AND NOT APPLE)
if (ENABLE_BLUEZ)
target_link_libraries(CemuInput PRIVATE bluez::bluez)
endif ()

View file

@ -15,9 +15,6 @@ DirectInputController::DirectInputController(const GUID& guid, std::string_view
DirectInputController::~DirectInputController()
{
if (m_effect)
m_effect->Release();
if (m_device)
{
m_device->Unacquire();
@ -39,8 +36,8 @@ DirectInputController::~DirectInputController()
if (kGameCubeController == m_product_guid)
should_release_device = false;
if (should_release_device)
m_device->Release();
if (!should_release_device)
m_device.Detach();
}
}
@ -104,7 +101,6 @@ bool DirectInputController::connect()
// set data format
if (FAILED(m_device->SetDataFormat(m_provider->get_data_format())))
{
SAFE_RELEASE(m_device);
return false;
}
@ -115,7 +111,6 @@ bool DirectInputController::connect()
{
if (FAILED(m_device->SetCooperativeLevel(hwndMainWindow, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
{
SAFE_RELEASE(m_device);
return false;
}
// rumble can only be used with exclusive access

View file

@ -2,6 +2,7 @@
#include "input/api/DirectInput/DirectInputControllerProvider.h"
#include "input/api/Controller.h"
#include <wrl/client.h>
class DirectInputController : public Controller<DirectInputControllerProvider>
{
@ -41,9 +42,9 @@ private:
GUID m_product_guid{};
std::shared_mutex m_mutex;
LPDIRECTINPUTDEVICE8 m_device = nullptr;
LPDIRECTINPUTEFFECT m_effect = nullptr;
Microsoft::WRL::ComPtr<IDirectInputDevice8> m_device;
Microsoft::WRL::ComPtr<IDirectInputEffect> m_effect;
std::array<LONG, 6> m_min_axis{};
std::array<LONG, 6> m_max_axis{};
};
};

View file

@ -19,7 +19,7 @@ DirectInputControllerProvider::DirectInputControllerProvider()
const auto r = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_dinput8, nullptr);
if (FAILED(r) || !m_dinput8)
if (FAILED(r))
{
const auto error = GetLastError();
//FreeLibrary(m_module);
@ -29,9 +29,6 @@ DirectInputControllerProvider::DirectInputControllerProvider()
DirectInputControllerProvider::~DirectInputControllerProvider()
{
if (m_dinput8)
m_dinput8->Release();
/*if (m_module)
FreeLibrary(m_module);
*/

View file

@ -4,6 +4,7 @@
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <wrl/client.h>
#include "input/api/ControllerProvider.h"
@ -22,7 +23,7 @@ public:
std::vector<std::shared_ptr<ControllerBase>> get_controllers() override;
IDirectInput8* get_dinput() const { return m_dinput8; }
IDirectInput8* get_dinput() const { return m_dinput8.Get(); }
LPCDIDATAFORMAT get_data_format() const;
private:
@ -31,7 +32,7 @@ private:
decltype(&DirectInput8Create) m_DirectInput8Create;
decltype(&GetdfDIJoystick) m_GetdfDIJoystick = nullptr;
IDirectInput8* m_dinput8 = nullptr;
Microsoft::WRL::ComPtr<IDirectInput8> m_dinput8;
};
#endif
#endif

View file

@ -1,7 +1,7 @@
#pragma once
#include <dxgi1_4.h>
//#include <atlbase.h>
#include <wrl/client.h>
class DXGIWrapper
{
@ -23,34 +23,28 @@ public:
throw std::runtime_error("can't find CreateDXGIFactory1 in dxgi module");
}
IDXGIFactory1* dxgiFactory = nullptr;
Microsoft::WRL::ComPtr<IDXGIFactory1> dxgiFactory;
pCreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory));
IDXGIAdapter1* tmpDxgiAdapter = nullptr;
Microsoft::WRL::ComPtr<IDXGIAdapter1> dxgiAdapter;
UINT adapterIndex = 0;
while (dxgiFactory->EnumAdapters1(adapterIndex, &tmpDxgiAdapter) != DXGI_ERROR_NOT_FOUND)
while (dxgiFactory->EnumAdapters1(adapterIndex, &dxgiAdapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC1 desc;
tmpDxgiAdapter->GetDesc1(&desc);
dxgiAdapter->GetDesc1(&desc);
if (deviceLUID == nullptr || memcmp(&desc.AdapterLuid, deviceLUID, sizeof(LUID)) == 0)
{
tmpDxgiAdapter->QueryInterface(IID_PPV_ARGS(&m_dxgiAdapter));
tmpDxgiAdapter->Release();
if (FAILED(dxgiAdapter.As(&m_dxgiAdapter)))
{
Cleanup();
throw std::runtime_error("can't create dxgi adapter");
}
break;
}
tmpDxgiAdapter->Release();
++adapterIndex;
}
dxgiFactory->Release();
if (!m_dxgiAdapter)
{
Cleanup();
throw std::runtime_error("can't create dxgi adapter");
}
}
~DXGIWrapper()
@ -65,15 +59,11 @@ public:
private:
HMODULE m_moduleHandle = nullptr;
IDXGIAdapter3* m_dxgiAdapter = nullptr;
Microsoft::WRL::ComPtr<IDXGIAdapter3> m_dxgiAdapter;
void Cleanup()
{
if (m_dxgiAdapter)
{
m_dxgiAdapter->Release();
m_dxgiAdapter = nullptr;
}
m_dxgiAdapter.Reset();
if (m_moduleHandle)
{
@ -81,4 +71,4 @@ private:
m_moduleHandle = nullptr;
}
}
};
};

View file

@ -15,7 +15,12 @@ Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* pri
ctx->uc_stack.ss_sp = m_stackPtr;
ctx->uc_stack.ss_size = stackSize;
ctx->uc_link = &ctx[0];
#ifdef __arm64__
// https://www.man7.org/linux/man-pages/man3/makecontext.3.html#NOTES
makecontext(ctx, (void(*)())FiberEntryPoint, 2, (uint64) userParam >> 32, userParam);
#else
makecontext(ctx, (void(*)())FiberEntryPoint, 1, userParam);
#endif
this->m_implData = (void*)ctx;
}

View file

@ -45,7 +45,11 @@ namespace MemMapper
void* r;
if(fromReservation)
{
if( mprotect(baseAddr, size, GetProt(permissionFlags)) == 0 )
uint64 page_size = sysconf(_SC_PAGESIZE);
void* page = baseAddr;
if ( (uint64) baseAddr % page_size != 0 )
page = (void*) ((uint64)baseAddr & ~(page_size - 1));
if( mprotect(page, size, GetProt(permissionFlags)) == 0 )
r = baseAddr;
else
r = nullptr;

View file

@ -111,9 +111,9 @@ namespace StringHelpers
}
// convert utf8 string to Wii U big-endian wchar_t string
static std::basic_string<uint16be> FromUtf8(std::string_view str)
static std::vector<uint16be> FromUtf8(std::string_view str)
{
std::basic_string<uint16be> tmpStr;
std::vector<uint16be> tmpStr;
std::wstring w = boost::nowide::widen(str.data(), str.size());
for (auto& c : w)
tmpStr.push_back((uint16)c);

View file

@ -27,6 +27,8 @@ uint64 HighResolutionTimer::m_freq = []() -> uint64 {
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
return (uint64)(freq.QuadPart);
#elif BOOST_OS_MACOS
return 1000000000;
#else
timespec pc;
clock_getres(CLOCK_MONOTONIC_RAW, &pc);