diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88b2d1a0..e1b4a1d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index ca1f2062..c0a6af56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/dependencies/ih264d/CMakeLists.txt b/dependencies/ih264d/CMakeLists.txt index 686a9d08..64ac0931 100644 --- a/dependencies/ih264d/CMakeLists.txt +++ b/dependencies/ih264d/CMakeLists.txt @@ -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() diff --git a/dependencies/ih264d/common/armv8/ih264_intra_pred_chroma_av8.s b/dependencies/ih264d/common/armv8/ih264_intra_pred_chroma_av8.s index 39c02560..c0d9cf99 100644 --- a/dependencies/ih264d/common/armv8/ih264_intra_pred_chroma_av8.s +++ b/dependencies/ih264d/common/armv8/ih264_intra_pred_chroma_av8.s @@ -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 diff --git a/dependencies/ih264d/common/armv8/ih264_intra_pred_luma_16x16_av8.s b/dependencies/ih264d/common/armv8/ih264_intra_pred_luma_16x16_av8.s index fa19c121..2422d8cd 100644 --- a/dependencies/ih264d/common/armv8/ih264_intra_pred_luma_16x16_av8.s +++ b/dependencies/ih264d/common/armv8/ih264_intra_pred_luma_16x16_av8.s @@ -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] diff --git a/dependencies/ih264d/common/armv8/ih264_intra_pred_luma_8x8_av8.s b/dependencies/ih264d/common/armv8/ih264_intra_pred_luma_8x8_av8.s index 273aa81b..6fa31ded 100644 --- a/dependencies/ih264d/common/armv8/ih264_intra_pred_luma_8x8_av8.s +++ b/dependencies/ih264d/common/armv8/ih264_intra_pred_luma_8x8_av8.s @@ -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 diff --git a/dependencies/ih264d/common/armv8/ih264_weighted_bi_pred_av8.s b/dependencies/ih264d/common/armv8/ih264_weighted_bi_pred_av8.s index 475f690e..8d6aa995 100644 --- a/dependencies/ih264d/common/armv8/ih264_weighted_bi_pred_av8.s +++ b/dependencies/ih264d/common/armv8/ih264_weighted_bi_pred_av8.s @@ -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 diff --git a/dependencies/ih264d/common/armv8/macos_arm_symbol_aliases.s b/dependencies/ih264d/common/armv8/macos_arm_symbol_aliases.s new file mode 100644 index 00000000..3639f1b3 --- /dev/null +++ b/dependencies/ih264d/common/armv8/macos_arm_symbol_aliases.s @@ -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 \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 62fd97df..3fa9f81d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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}" diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index ca477c15..c7edc196 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -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 diff --git a/src/Cafe/Filesystem/FST/FST.cpp b/src/Cafe/Filesystem/FST/FST.cpp index f1255778..ec112b9a 100644 --- a/src/Cafe/Filesystem/FST/FST.cpp +++ b/src/Cafe/Filesystem/FST/FST.cpp @@ -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) { diff --git a/src/Cafe/Filesystem/FST/FST.h b/src/Cafe/Filesystem/FST/FST.h index 601799ce..26201c32 100644 --- a/src/Cafe/Filesystem/FST/FST.h +++ b/src/Cafe/Filesystem/FST/FST.h @@ -83,7 +83,6 @@ public: } private: - /* FST data (in memory) */ enum class ClusterHashMode : uint8 { @@ -193,7 +192,7 @@ private: std::unordered_map 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); diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp index f21bb89d..6ae05c5b 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2.cpp @@ -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()); } } diff --git a/src/Cafe/GraphicPack/GraphicPack2.h b/src/Cafe/GraphicPack/GraphicPack2.h index 9b6a86d4..fc9603cd 100644 --- a/src/Cafe/GraphicPack/GraphicPack2.h +++ b/src/Cafe/GraphicPack/GraphicPack2.h @@ -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> m_ramMappings; diff --git a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp index 24219e66..cf7ba195 100644 --- a/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp +++ b/src/Cafe/HW/Espresso/Interpreter/PPCInterpreterHLE.cpp @@ -2,62 +2,70 @@ #include "PPCInterpreterInternal.h" #include "PPCInterpreterHelper.h" -std::unordered_set sUnsupportedHLECalls; +std::unordered_set 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* 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(); - 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); } diff --git a/src/Cafe/HW/Espresso/PPCState.h b/src/Cafe/HW/Espresso/PPCState.h index 179e2687..fd943d39 100644 --- a/src/Cafe/HW/Espresso/PPCState.h +++ b/src/Cafe/HW/Espresso/PPCState.h @@ -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); diff --git a/src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp b/src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp index cb71234d..728460a4 100644 --- a/src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp +++ b/src/Cafe/HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp @@ -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); diff --git a/src/Cafe/HW/Latte/Core/LatteIndices.cpp b/src/Cafe/HW/Latte/Core/LatteIndices.cpp index aec51725..2bbb617d 100644 --- a/src/Cafe/HW/Latte/Core/LatteIndices.cpp +++ b/src/Cafe/HW/Latte/Core/LatteIndices.cpp @@ -6,6 +6,8 @@ #if defined(ARCH_X86_64) && defined(__GNUC__) #include +#elif defined(__aarch64__) +#include #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 @@ -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(indexData, indexOutputPtr, count, indexMin, indexMax); - #else +#elif defined(__aarch64__) + LatteIndices_fastConvertU16_NEON(indexData, indexOutputPtr, count, indexMin, indexMax); +#else LatteIndices_convertBE(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(indexData, indexOutputPtr, count, indexMin, indexMax); - #else +#elif defined(__aarch64__) + LatteIndices_fastConvertU32_NEON(indexData, indexOutputPtr, count, indexMin, indexMax); +#else LatteIndices_convertBE(indexData, indexOutputPtr, count, indexMin, indexMax); - #endif +#endif } else if (indexType == LatteIndexType::U16_LE) { diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index 0d427e34..86035973 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -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; } diff --git a/src/Cafe/IOSU/PDM/iosu_pdm.cpp b/src/Cafe/IOSU/PDM/iosu_pdm.cpp index d94b1dbf..b9dda445 100644 --- a/src/Cafe/IOSU/PDM/iosu_pdm.cpp +++ b/src/Cafe/IOSU/PDM/iosu_pdm.cpp @@ -464,5 +464,34 @@ namespace iosu return static_cast(&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; + }; }; diff --git a/src/Cafe/IOSU/PDM/iosu_pdm.h b/src/Cafe/IOSU/PDM/iosu_pdm.h index 0dd8a39d..63f99a4a 100644 --- a/src/Cafe/IOSU/PDM/iosu_pdm.h +++ b/src/Cafe/IOSU/PDM/iosu_pdm.h @@ -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; }; diff --git a/src/Cafe/IOSU/legacy/iosu_boss.cpp b/src/Cafe/IOSU/legacy/iosu_boss.cpp index 7ab25f68..212d42a0 100644 --- a/src/Cafe/IOSU/legacy/iosu_boss.cpp +++ b/src/Cafe/IOSU/legacy/iosu_boss.cpp @@ -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); diff --git a/src/Cafe/IOSU/legacy/iosu_fpd.cpp b/src/Cafe/IOSU/legacy/iosu_fpd.cpp index 28d248ae..a667e61c 100644 --- a/src/Cafe/IOSU/legacy/iosu_fpd.cpp +++ b/src/Cafe/IOSU/legacy/iosu_fpd.cpp @@ -132,7 +132,7 @@ namespace iosu void convertMultiByteStringToBigEndianWidechar(const char* input, uint16be* output, sint32 maxOutputLength) { - std::basic_string beStr = StringHelpers::FromUtf8(input); + std::vector 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 myComment; + std::vector 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; } diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 870d1850..2f89000b 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -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(); diff --git a/src/Cafe/OS/libs/gx2/GX2_Command.cpp b/src/Cafe/OS/libs/gx2/GX2_Command.cpp index 6699e1e1..d12bf210 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Command.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Command.cpp @@ -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(numU32s, 0x100); diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp index db1885af..6e7632e9 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_DownloadCommunityTypes.cpp @@ -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(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(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(screen_name_utf16.size(), 32)); if (screen_name_utf16.size() != 0) { for (int i = 0; i < screen_name_utf16.size(); i++) diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp index 6f3c43b9..21952ceb 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadCommunityTypes.cpp @@ -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(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(description_utf16.size(), 256)); if (description_utf16.size() != 0) { for (int i = 0; i < description_utf16.size(); i++) diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp index 1e2d40ab..912e7a11 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_UploadFavoriteTypes.cpp @@ -1,5 +1,6 @@ #include "nn_olv_UploadFavoriteTypes.h" #include +#include 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(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(description_utf16.size(), 256)); if (description_utf16.size() != 0) { for (int i = 0; i < description_utf16.size(); i++) diff --git a/src/Cafe/OS/libs/nsyshid/Skylander.cpp b/src/Cafe/OS/libs/nsyshid/Skylander.cpp index 9fab17b6..78337962 100644 --- a/src/Cafe/OS/libs/nsyshid/Skylander.cpp +++ b/src/Cafe/OS/libs/nsyshid/Skylander.cpp @@ -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 mono_samples; + for (unsigned int i = 0; i < mono_samples.size(); ++i) + { + sint16 sample = static_cast(message->data[i * 2 + 1]) << 8 | static_cast(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; } diff --git a/src/Cafe/OS/libs/snd_core/ax_out.cpp b/src/Cafe/OS/libs/snd_core/ax_out.cpp index a88807f2..fe32cfb4 100644 --- a/src/Cafe/OS/libs/snd_core/ax_out.cpp +++ b/src/Cafe/OS/libs/snd_core/ax_out.cpp @@ -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 diff --git a/src/Cemu/Logging/CemuLogging.cpp b/src/Cemu/Logging/CemuLogging.cpp index 5cde2a7f..f3575cc9 100644 --- a/src/Cemu/Logging/CemuLogging.cpp +++ b/src/Cemu/Logging/CemuLogging.cpp @@ -3,6 +3,7 @@ #include "util/helpers/helpers.h" #include "config/CemuConfig.h" #include "config/ActiveSettings.h" +#include "config/LaunchSettings.h" #include #include @@ -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(), diff --git a/src/Cemu/ncrypto/ncrypto.h b/src/Cemu/ncrypto/ncrypto.h index 5f399ad7..1ed7e91b 100644 --- a/src/Cemu/ncrypto/ncrypto.h +++ b/src/Cemu/ncrypto/ncrypto.h @@ -13,10 +13,17 @@ namespace NCrypto std::string base64Encode(const void* inputMem, size_t inputLen); std::vector 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 */ diff --git a/src/Common/CafeString.h b/src/Common/CafeString.h index d902d721..57fc72da 100644 --- a/src/Common/CafeString.h +++ b/src/Common/CafeString.h @@ -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 beStr = StringHelpers::FromUtf8(sv); - if(beStr.length() > N-1) + std::vector 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; } diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 9e5c60f5..26fdfd28 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -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 constexpr bool HAS_FLAG(T1 flags, T2 test_flag) { return (flags & (T1)test_flag) == (T1)test_flag; } template constexpr bool HAS_BIT(T1 value, T2 index) { return (value & ((T1)1 << index)) != 0; } -template -constexpr void SAFE_RELEASE(T& p) { if (p) { p->Release(); p = nullptr; } } template constexpr uint32_t ppcsizeof() { return (uint32_t) sizeof(T); } diff --git a/src/audio/DirectSoundAPI.cpp b/src/audio/DirectSoundAPI.cpp index eabd3a7e..64042515 100644 --- a/src/audio/DirectSoundAPI.cpp +++ b/src/audio/DirectSoundAPI.cpp @@ -1,9 +1,8 @@ #include "DirectSoundAPI.h" -#include "gui/wxgui.h" - #include "util/helpers/helpers.h" #include "gui/guiWrapper.h" +#include #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 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 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**)¬ify8); - 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"); diff --git a/src/audio/DirectSoundAPI.h b/src/audio/DirectSoundAPI.h index c5ad0d6f..52817fbe 100644 --- a/src/audio/DirectSoundAPI.h +++ b/src/audio/DirectSoundAPI.h @@ -2,8 +2,8 @@ #define DIRECTSOUND_VERSION 0x0800 #include -//#include #include +#include #include "IAudioAPI.h" @@ -41,15 +41,10 @@ public: static std::vector GetInputDevices(); private: - struct DirectSoundDeleter - { - void operator()(IUnknown* ptr) const { if (ptr) ptr->Release(); } - }; - - std::unique_ptr m_direct_sound; - //std::unique_ptr m_direct_sound_capture; - std::unique_ptr m_sound_buffer; - std::unique_ptr m_notify; + Microsoft::WRL::ComPtr m_direct_sound; + //Microsoft::WRL::ComPtr m_direct_sound_capture; + Microsoft::WRL::ComPtr m_sound_buffer; + Microsoft::WRL::ComPtr m_notify; DWORD m_sound_buffer_size = 0; uint32_t m_offset = 0; diff --git a/src/audio/IAudioAPI.cpp b/src/audio/IAudioAPI.cpp index 587526ab..dc266eed 100644 --- a/src/audio/IAudioAPI.cpp +++ b/src/audio/IAudioAPI.cpp @@ -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 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(device); - return std::make_unique(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample); - } + case Cubeb: + { + const auto tmp = std::dynamic_pointer_cast(device); + return std::make_unique(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::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; + } +} diff --git a/src/audio/IAudioAPI.h b/src/audio/IAudioAPI.h index 8fb510db..34df421a 100644 --- a/src/audio/IAudioAPI.h +++ b/src/audio/IAudioAPI.h @@ -4,6 +4,8 @@ #include #endif +#include "config/CemuConfig.h" + class IAudioAPI { friend class GeneralSettings2; @@ -30,6 +32,13 @@ public: using DeviceDescriptionPtr = std::shared_ptr; + 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 CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample); - static std::unique_ptr CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample); + static std::unique_ptr CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample); + static std::unique_ptr CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample); static std::unique_ptr CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample); static std::vector 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; diff --git a/src/audio/XAudio2API.cpp b/src/audio/XAudio2API.cpp index c92fd451..6b25baed 100644 --- a/src/audio/XAudio2API.cpp +++ b/src/audio/XAudio2API.cpp @@ -2,6 +2,7 @@ //#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) +#include #include #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::RefreshDevices( try { - struct IWbemLocator *wbem_locator = nullptr; + Microsoft::WRL::ComPtr 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 path(SysAllocString(LR"(\\.\root\cimv2)"), SysFreeString); @@ -191,20 +180,19 @@ const std::vector& XAudio2API::RefreshDevices( std::shared_ptr name_row(SysAllocString(L"Name"), SysFreeString); std::shared_ptr device_id_row(SysAllocString(L"DeviceID"), SysFreeString); - IWbemServices *wbem_services = nullptr; + Microsoft::WRL::ComPtr 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 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::RefreshDevices( auto default_device = std::make_shared(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) { diff --git a/src/audio/XAudio2API.h b/src/audio/XAudio2API.h index b5bb0296..5bb01b10 100644 --- a/src/audio/XAudio2API.h +++ b/src/audio/XAudio2API.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "IAudioAPI.h" @@ -50,11 +51,6 @@ private: static const std::vector& 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 s_devices; - std::unique_ptr m_xaudio; + Microsoft::WRL::ComPtr m_xaudio; std::wstring m_device_id; std::unique_ptr m_mastering_voice; std::unique_ptr m_source_voice; diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 23256370..b859be5e 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -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"); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index c4b01a57..e643eb0f 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -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 diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index b2b0c08a..fde20539 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -59,6 +59,9 @@ bool LaunchSettings::HandleCommandline(const std::vector& 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(), "Path of game to launch") ("title-id,t", po::value(), "Title ID of the title to be launched (overridden by --game)") @@ -125,6 +128,9 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) return false; // exit in main } + if (vm.count("verbose")) + s_verbose = true; + if (vm.count("game")) { std::wstring tmp = vm["game"].as(); diff --git a/src/config/LaunchSettings.h b/src/config/LaunchSettings.h index d1bed9e1..13665cb7 100644 --- a/src/config/LaunchSettings.h +++ b/src/config/LaunchSettings.h @@ -22,6 +22,8 @@ public: static std::optional RenderUpsideDownEnabled() { return s_render_upside_down; } static std::optional 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 s_render_upside_down{}; inline static std::optional s_fullscreen{}; + + inline static bool s_verbose = false; inline static bool s_enable_gdbstub = false; inline static bool s_nsight_mode = false; diff --git a/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp b/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp index d40e5e5e..53e3b995 100644 --- a/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp +++ b/src/gui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp @@ -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 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, 7> EmulatedUSBDeviceFrame::GetCurrentMinifigs() { return m_dimSlots; -} \ No newline at end of file +} diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index 9b763229..4c23aedc 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -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) diff --git a/src/gui/GeneralSettings2.h b/src/gui/GeneralSettings2.h index 7fbfecc1..5e27d6e2 100644 --- a/src/gui/GeneralSettings2.h +++ b/src/gui/GeneralSettings2.h @@ -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; diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 92ac331a..de5104f4 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -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)); diff --git a/src/gui/MemorySearcherTool.cpp b/src/gui/MemorySearcherTool.cpp index fadebc44..8506c591 100644 --- a/src/gui/MemorySearcherTool.cpp +++ b/src/gui/MemorySearcherTool.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #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()); diff --git a/src/gui/MemorySearcherTool.h b/src/gui/MemorySearcherTool.h index 78b5cb77..27e7d27d 100644 --- a/src/gui/MemorySearcherTool.h +++ b/src/gui/MemorySearcherTool.h @@ -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; diff --git a/src/gui/TitleManager.cpp b/src/gui/TitleManager.cpp index 1dd41b3b..67f13e05 100644 --- a/src/gui/TitleManager.cpp +++ b/src/gui/TitleManager.cpp @@ -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) diff --git a/src/gui/components/wxDownloadManagerList.cpp b/src/gui/components/wxDownloadManagerList.cpp index 14bf5cbe..d447310c 100644 --- a/src/gui/components/wxDownloadManagerList.cpp +++ b/src/gui/components/wxDownloadManagerList.cpp @@ -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 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 wxDownloadManagerList: boost::optional 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 sortColumnOrder, const Type_ #include -void wxDownloadManagerList::SortEntries() +void wxDownloadManagerList::SortEntries(int column) { boost::container::small_vector 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(); } diff --git a/src/gui/components/wxDownloadManagerList.h b/src/gui/components/wxDownloadManagerList.h index 3a6b853a..4febb461 100644 --- a/src/gui/components/wxDownloadManagerList.h +++ b/src/gui/components/wxDownloadManagerList.h @@ -9,7 +9,7 @@ #include #include -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 m_data; std::vector> 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; diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index e418ca0a..587374f6 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #endif // public events @@ -82,8 +84,58 @@ std::list _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(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 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( + "\n" + "\n" + "\n" + "\n" + " CFBundleDisplayName\n" + " {0}\n" + " CFBundleExecutable\n" + " run.sh\n" + " CFBundleIconFile\n" + " shortcut.icns\n" + " CFBundleName\n" + " {0}\n" + " CFBundlePackageType\n" + " APPL\n" + " CFBundleSignature\n" + " \?\?\?\?\n" + " LSApplicationCategoryType\n" + " public.app-category.games\n" + " CFBundleShortVersionString\n" + " {1}\n" + " CFBundleVersion\n" + " {1}\n" + "\n" + "\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(&shellLink)); + Microsoft::WRL::ComPtr 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 shellLinkFile; // save the shortcut - hres = shellLink->QueryInterface(IID_IPersistFile, reinterpret_cast(&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 \ No newline at end of file +#endif diff --git a/src/gui/components/wxGameList.h b/src/gui/components/wxGameList.h index b285d259..625f7976 100644 --- a/src/gui/components/wxGameList.h +++ b/src/gui/components/wxGameList.h @@ -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; diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/components/wxTitleManagerList.cpp index c0bf5778..a5774b14 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/components/wxTitleManagerList.cpp @@ -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 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 wxTitleManagerList::GetSe boost::optional 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(entry1.version) < std::underlying_type_t(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(entry1.region) < std::underlying_type_t(entry2.region); + + return std::underlying_type_t(entry1.region) < std::underlying_type_t(entry2.region); } else if (column == ColumnFormat) { if(entry1.format == entry2.format) return SortFunc(ColumnType, v1, v2); - return std::underlying_type_t(entry1.format) < std::underlying_type_t(entry2.format); + return std::underlying_type_t(entry1.format) < std::underlying_type_t(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(); } diff --git a/src/gui/components/wxTitleManagerList.h b/src/gui/components/wxTitleManagerList.h index 2780a9ce..c21145b7 100644 --- a/src/gui/components/wxTitleManagerList.h +++ b/src/gui/components/wxTitleManagerList.h @@ -9,7 +9,7 @@ #include #include -class wxTitleManagerList : public wxListCtrl +class wxTitleManagerList : public wxListView { friend class TitleManager; public: @@ -127,8 +127,6 @@ private: std::vector m_data; std::vector> m_sorted_data; - int m_last_column_sorted = -1; - bool m_sort_less = true; using Type_t = std::reference_wrapper; bool SortFunc(int column, const Type_t& v1, const Type_t& v2); diff --git a/src/gui/debugger/BreakpointWindow.cpp b/src/gui/debugger/BreakpointWindow.cpp index 658a51ad..c693477d 100644 --- a/src/gui/debugger/BreakpointWindow.cpp +++ b/src/gui/debugger/BreakpointWindow.cpp @@ -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; diff --git a/src/gui/debugger/DisasmCtrl.cpp b/src/gui/debugger/DisasmCtrl.cpp index e74d64b9..1f4b739f 100644 --- a/src/gui/debugger/DisasmCtrl.cpp +++ b/src/gui/debugger/DisasmCtrl.cpp @@ -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() } } } -} \ No newline at end of file +} diff --git a/src/gui/debugger/DumpCtrl.cpp b/src/gui/debugger/DumpCtrl.cpp index 16fdd87d..fad93bd3 100644 --- a/src/gui/debugger/DumpCtrl.cpp +++ b/src/gui/debugger/DumpCtrl.cpp @@ -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(); -} \ No newline at end of file +} diff --git a/src/gui/debugger/ModuleWindow.cpp b/src/gui/debugger/ModuleWindow.cpp index c24517bd..8183c40c 100644 --- a/src/gui/debugger/ModuleWindow.cpp +++ b/src/gui/debugger/ModuleWindow.cpp @@ -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); diff --git a/src/gui/debugger/RegisterCtrl.cpp b/src/gui/debugger/RegisterCtrl.cpp index bcf6fb5a..24cae60b 100644 --- a/src/gui/debugger/RegisterCtrl.cpp +++ b/src/gui/debugger/RegisterCtrl.cpp @@ -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()); diff --git a/src/gui/debugger/RegisterWindow.cpp b/src/gui/debugger/RegisterWindow.cpp index 55c6386f..b18d4e27 100644 --- a/src/gui/debugger/RegisterWindow.cpp +++ b/src/gui/debugger/RegisterWindow.cpp @@ -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()); diff --git a/src/gui/debugger/SymbolCtrl.cpp b/src/gui/debugger/SymbolCtrl.cpp index aa862987..57cc96f6 100644 --- a/src/gui/debugger/SymbolCtrl.cpp +++ b/src/gui/debugger/SymbolCtrl.cpp @@ -2,6 +2,7 @@ #include "gui/guiWrapper.h" #include "Cafe/OS/RPL/rpl_symbol_storage.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" +#include 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(visible_entries - 1, GetTopItem() + GetCountPerPage() + 1)); -} \ No newline at end of file +} diff --git a/src/gui/debugger/SymbolCtrl.h b/src/gui/debugger/SymbolCtrl.h index 81ccd326..8a0161bd 100644 --- a/src/gui/debugger/SymbolCtrl.h +++ b/src/gui/debugger/SymbolCtrl.h @@ -2,7 +2,7 @@ #include -class SymbolListCtrl : public wxListCtrl +class SymbolListCtrl : public wxListView { public: SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size); diff --git a/src/gui/helpers/wxLogEvent.h b/src/gui/helpers/wxLogEvent.h index 8cef1d2d..23f6533d 100644 --- a/src/gui/helpers/wxLogEvent.h +++ b/src/gui/helpers/wxLogEvent.h @@ -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); } diff --git a/src/gui/input/InputAPIAddWindow.cpp b/src/gui/input/InputAPIAddWindow.cpp index 688ee14e..6bfb589b 100644 --- a/src/gui/input/InputAPIAddWindow.cpp +++ b/src/gui/input/InputAPIAddWindow.cpp @@ -23,7 +23,7 @@ using wxControllerData = wxCustomData; InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position, const std::vector& 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); diff --git a/src/gui/input/PairingDialog.cpp b/src/gui/input/PairingDialog.cpp index 350fce81..ecbfc110 100644 --- a/src/gui/input/PairingDialog.cpp +++ b/src/gui/input/PairingDialog.cpp @@ -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}; diff --git a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp b/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp index f4e5b7af..0669321c 100644 --- a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp +++ b/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp @@ -9,6 +9,7 @@ #include #include +#include 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(); diff --git a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h b/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h index 649780c5..f6dc9060 100644 --- a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h +++ b/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h @@ -23,7 +23,7 @@ private: void PresentProfileResults(OSThread_t* thread, const std::unordered_map& 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(); -}; \ No newline at end of file +}; diff --git a/src/gui/wxcomponents/checkedlistctrl.h b/src/gui/wxcomponents/checkedlistctrl.h index 215c0004..e09536f9 100644 --- a/src/gui/wxcomponents/checkedlistctrl.h +++ b/src/gui/wxcomponents/checkedlistctrl.h @@ -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, diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 004dc2ba..af8b8181 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -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 () \ No newline at end of file diff --git a/src/input/api/DirectInput/DirectInputController.cpp b/src/input/api/DirectInput/DirectInputController.cpp index dbb7c80c..234696b6 100644 --- a/src/input/api/DirectInput/DirectInputController.cpp +++ b/src/input/api/DirectInput/DirectInputController.cpp @@ -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 diff --git a/src/input/api/DirectInput/DirectInputController.h b/src/input/api/DirectInput/DirectInputController.h index 2ec371a2..d2c3dba2 100644 --- a/src/input/api/DirectInput/DirectInputController.h +++ b/src/input/api/DirectInput/DirectInputController.h @@ -2,6 +2,7 @@ #include "input/api/DirectInput/DirectInputControllerProvider.h" #include "input/api/Controller.h" +#include class DirectInputController : public Controller { @@ -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 m_device; + Microsoft::WRL::ComPtr m_effect; std::array m_min_axis{}; std::array m_max_axis{}; -}; \ No newline at end of file +}; diff --git a/src/input/api/DirectInput/DirectInputControllerProvider.cpp b/src/input/api/DirectInput/DirectInputControllerProvider.cpp index 063cb779..79f31354 100644 --- a/src/input/api/DirectInput/DirectInputControllerProvider.cpp +++ b/src/input/api/DirectInput/DirectInputControllerProvider.cpp @@ -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); */ diff --git a/src/input/api/DirectInput/DirectInputControllerProvider.h b/src/input/api/DirectInput/DirectInputControllerProvider.h index 5db883c0..de8c3216 100644 --- a/src/input/api/DirectInput/DirectInputControllerProvider.h +++ b/src/input/api/DirectInput/DirectInputControllerProvider.h @@ -4,6 +4,7 @@ #define DIRECTINPUT_VERSION 0x0800 #include +#include #include "input/api/ControllerProvider.h" @@ -22,7 +23,7 @@ public: std::vector> 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 m_dinput8; }; -#endif \ No newline at end of file +#endif diff --git a/src/util/DXGIWrapper/DXGIWrapper.h b/src/util/DXGIWrapper/DXGIWrapper.h index 54f2454d..363733e7 100644 --- a/src/util/DXGIWrapper/DXGIWrapper.h +++ b/src/util/DXGIWrapper/DXGIWrapper.h @@ -1,7 +1,7 @@ #pragma once #include -//#include +#include class DXGIWrapper { @@ -23,34 +23,28 @@ public: throw std::runtime_error("can't find CreateDXGIFactory1 in dxgi module"); } - IDXGIFactory1* dxgiFactory = nullptr; + Microsoft::WRL::ComPtr dxgiFactory; pCreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)); - IDXGIAdapter1* tmpDxgiAdapter = nullptr; + Microsoft::WRL::ComPtr 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 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; } } -}; \ No newline at end of file +}; diff --git a/src/util/Fiber/FiberUnix.cpp b/src/util/Fiber/FiberUnix.cpp index 0d527069..36430449 100644 --- a/src/util/Fiber/FiberUnix.cpp +++ b/src/util/Fiber/FiberUnix.cpp @@ -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; } diff --git a/src/util/MemMapper/MemMapperUnix.cpp b/src/util/MemMapper/MemMapperUnix.cpp index 0ade291d..8e800e53 100644 --- a/src/util/MemMapper/MemMapperUnix.cpp +++ b/src/util/MemMapper/MemMapperUnix.cpp @@ -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; diff --git a/src/util/helpers/StringHelpers.h b/src/util/helpers/StringHelpers.h index a98344d6..fb858f4d 100644 --- a/src/util/helpers/StringHelpers.h +++ b/src/util/helpers/StringHelpers.h @@ -111,9 +111,9 @@ namespace StringHelpers } // convert utf8 string to Wii U big-endian wchar_t string - static std::basic_string FromUtf8(std::string_view str) + static std::vector FromUtf8(std::string_view str) { - std::basic_string tmpStr; + std::vector tmpStr; std::wstring w = boost::nowide::widen(str.data(), str.size()); for (auto& c : w) tmpStr.push_back((uint16)c); diff --git a/src/util/highresolutiontimer/HighResolutionTimer.cpp b/src/util/highresolutiontimer/HighResolutionTimer.cpp index 67ffa349..bb4a40ab 100644 --- a/src/util/highresolutiontimer/HighResolutionTimer.cpp +++ b/src/util/highresolutiontimer/HighResolutionTimer.cpp @@ -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);