Merge branch 'main' into metal

This commit is contained in:
SamoZ256 2025-06-26 08:42:24 +02:00 committed by GitHub
commit 4281959b64
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 1316 additions and 488 deletions

View file

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

View file

@ -176,7 +176,7 @@ if (UNIX AND NOT APPLE)
if(ENABLE_BLUEZ) if(ENABLE_BLUEZ)
find_package(bluez REQUIRED) find_package(bluez REQUIRED)
set(ENABLE_WIIMOTE ON) set(SUPPORTS_WIIMOTE ON)
add_compile_definitions(HAS_BLUEZ) add_compile_definitions(HAS_BLUEZ)
endif() endif()
@ -204,7 +204,7 @@ endif()
if (ENABLE_HIDAPI) if (ENABLE_HIDAPI)
find_package(hidapi REQUIRED) find_package(hidapi REQUIRED)
set(ENABLE_WIIMOTE ON) set(SUPPORTS_WIIMOTE ON)
add_compile_definitions(HAS_HIDAPI) add_compile_definitions(HAS_HIDAPI)
endif () endif ()
@ -238,7 +238,12 @@ endif()
add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL) add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL)
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)")
add_subdirectory("dependencies/xbyak_aarch64" EXCLUDE_FROM_ALL) add_subdirectory("dependencies/xbyak_aarch64" EXCLUDE_FROM_ALL)
endif() endif()

View file

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

View file

@ -429,8 +429,13 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
rev64 v7.4h, v2.4h rev64 v7.4h, v2.4h
ld1 {v3.2s}, [x10] ld1 {v3.2s}, [x10]
sub x5, x3, #8 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 adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs1
ldr x12, [x12, #:got_lo12: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 usubl v10.8h, v5.8b, v1.8b
ld1 {v8.8b, v9.8b}, [x12] // Load multiplication factors 1 to 8 into D3 ld1 {v8.8b, v9.8b}, [x12] // Load multiplication factors 1 to 8 into D3
mov v8.d[1], v9.d[0] 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 zip1 v1.8h, v0.8h, v2.8h
zip2 v2.8h, v0.8h, v2.8h zip2 v2.8h, v0.8h, v2.8h
mov v0.16b, v1.16b 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 adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs2
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs2] ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs2]
#endif
ld1 {v8.2s, v9.2s}, [x12] ld1 {v8.2s, v9.2s}, [x12]
mov v8.d[1], v9.d[0] mov v8.d[1], v9.d[0]
mov v10.16b, v8.16b mov v10.16b, v8.16b

View file

@ -431,10 +431,13 @@ ih264_intra_pred_luma_16x16_mode_plane_av8:
mov x10, x1 //top_left mov x10, x1 //top_left
mov x4, #-1 mov x4, #-1
ld1 {v2.2s}, [x1], x8 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 adrp x7, :got:ih264_gai1_intrapred_luma_plane_coeffs
ldr x7, [x7, #:got_lo12:ih264_gai1_intrapred_luma_plane_coeffs] ldr x7, [x7, #:got_lo12:ih264_gai1_intrapred_luma_plane_coeffs]
#endif
ld1 {v0.2s}, [x1] ld1 {v0.2s}, [x1]
rev64 v2.8b, v2.8b rev64 v2.8b, v2.8b
ld1 {v6.2s, v7.2s}, [x7] ld1 {v6.2s, v7.2s}, [x7]

View file

@ -1029,9 +1029,13 @@ ih264_intra_pred_luma_8x8_mode_horz_u_av8:
mov v3.d[0], v2.d[1] mov v3.d[0], v2.d[1]
ext v4.16b, v2.16b , v2.16b , #1 ext v4.16b, v2.16b , v2.16b , #1
mov v5.d[0], v4.d[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 adrp x12, :got:ih264_gai1_intrapred_luma_8x8_horz_u
ldr x12, [x12, #:got_lo12: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 v20.8h, v0.8b, v2.8b
uaddl v22.8h, v1.8b, v3.8b uaddl v22.8h, v1.8b, v3.8b
uaddl v24.8h, v2.8b, v4.8b uaddl v24.8h, v2.8b, v4.8b

View file

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

View file

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

View file

@ -101,13 +101,21 @@ if (MACOS_BUNDLE)
endforeach(folder) endforeach(folder)
if(CMAKE_BUILD_TYPE STREQUAL "Debug") 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() 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() 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 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 "${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 ${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}" COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}"

View file

@ -599,7 +599,7 @@ if(ENABLE_METAL)
) )
endif() endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "(aarch64)|(AARCH64)") if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
target_sources(CemuCafe PRIVATE target_sources(CemuCafe PRIVATE
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.h HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.h

View file

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

View file

@ -83,7 +83,6 @@ public:
} }
private: private:
/* FST data (in memory) */ /* FST data (in memory) */
enum class ClusterHashMode : uint8 enum class ClusterHashMode : uint8
{ {
@ -193,7 +192,7 @@ private:
std::unordered_map<uint64, struct FSTCachedHashedBlock*> m_cacheDecryptedHashedBlocks; std::unordered_map<uint64, struct FSTCachedHashedBlock*> m_cacheDecryptedHashedBlocks;
uint64 m_cacheAccessCounter{}; 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 FSTCachedRawBlock* GetDecryptedRawBlock(uint32 clusterIndex, uint32 blockIndex);
struct FSTCachedHashedBlock* GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex); struct FSTCachedHashedBlock* GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex);

View file

@ -825,7 +825,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 currentTitleId = CafeSystem::GetForegroundTitleId();
uint64 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull; uint64 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull;
@ -840,7 +840,7 @@ void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC
} }
else 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); fscDeviceRedirect_add(virtualMountPath.generic_string(), it.file_size(), it.path().generic_string(), m_fs_priority);
} }
@ -865,7 +865,7 @@ void GraphicPack2::LoadReplacedFiles()
{ {
// setup redirections // setup redirections
fscDeviceRedirect_map(); fscDeviceRedirect_map();
_iterateReplacedFiles(contentPath, false); _iterateReplacedFiles(contentPath, false, "vol/content/");
} }
// /aoc/ // /aoc/
fs::path aocPath(gfxPackPath); fs::path aocPath(gfxPackPath);
@ -878,7 +878,18 @@ void GraphicPack2::LoadReplacedFiles()
aocTitleId |= 0x0005000c00000000ULL; aocTitleId |= 0x0005000c00000000ULL;
// setup redirections // setup redirections
fscDeviceRedirect_map(); fscDeviceRedirect_map();
_iterateReplacedFiles(aocPath, true); _iterateReplacedFiles(aocPath, true, nullptr);
}
// /code/
fs::path codePath(gfxPackPath);
codePath.append("code");
if (fs::exists(codePath, ec))
{
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(codePath, false, CafeSystem::GetInternalVirtualCodeFolder().c_str());
} }
} }

View file

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

View file

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

View file

@ -230,9 +230,9 @@ static inline float flushDenormalToZero(float f)
// HLE interface // 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); HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName);
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex); HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex);

View file

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

View file

@ -6,6 +6,8 @@
#if defined(ARCH_X86_64) && defined(__GNUC__) #if defined(ARCH_X86_64) && defined(__GNUC__)
#include <immintrin.h> #include <immintrin.h>
#elif defined(__aarch64__)
#include <arm_neon.h>
#endif #endif
struct struct
@ -555,6 +557,114 @@ void LatteIndices_fastConvertU32_AVX2(const void* indexDataInput, void* indexDat
indexMax = std::max(indexMax, _maxIndex); indexMax = std::max(indexMax, _maxIndex);
indexMin = std::min(indexMin, _minIndex); 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 #endif
template<typename T> template<typename T>
@ -764,27 +874,31 @@ void LatteIndices_decode(const void* indexData, LatteIndexType indexType, uint32
{ {
if (indexType == LatteIndexType::U16_BE) if (indexType == LatteIndexType::U16_BE)
{ {
#if defined(ARCH_X86_64) #if defined(ARCH_X86_64)
if (g_CPUFeatures.x86.avx2) if (g_CPUFeatures.x86.avx2)
LatteIndices_fastConvertU16_AVX2(indexData, indexOutputPtr, count, indexMin, indexMax); LatteIndices_fastConvertU16_AVX2(indexData, indexOutputPtr, count, indexMin, indexMax);
else if (g_CPUFeatures.x86.sse4_1 && g_CPUFeatures.x86.ssse3) else if (g_CPUFeatures.x86.sse4_1 && g_CPUFeatures.x86.ssse3)
LatteIndices_fastConvertU16_SSE41(indexData, indexOutputPtr, count, indexMin, indexMax); LatteIndices_fastConvertU16_SSE41(indexData, indexOutputPtr, count, indexMin, indexMax);
else else
LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax); LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax);
#else #elif defined(__aarch64__)
LatteIndices_fastConvertU16_NEON(indexData, indexOutputPtr, count, indexMin, indexMax);
#else
LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax); LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax);
#endif #endif
} }
else if (indexType == LatteIndexType::U32_BE) else if (indexType == LatteIndexType::U32_BE)
{ {
#if defined(ARCH_X86_64) #if defined(ARCH_X86_64)
if (g_CPUFeatures.x86.avx2) if (g_CPUFeatures.x86.avx2)
LatteIndices_fastConvertU32_AVX2(indexData, indexOutputPtr, count, indexMin, indexMax); LatteIndices_fastConvertU32_AVX2(indexData, indexOutputPtr, count, indexMin, indexMax);
else else
LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax); LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax);
#else #elif defined(__aarch64__)
LatteIndices_fastConvertU32_NEON(indexData, indexOutputPtr, count, indexMin, indexMax);
#else
LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax); LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax);
#endif #endif
} }
else if (indexType == LatteIndexType::U16_LE) else if (indexType == LatteIndexType::U16_LE)
{ {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -25,7 +25,11 @@ void nnNfp_update();
namespace coreinit namespace coreinit
{ {
#ifdef __arm64__
void __OSFiberThreadEntry(uint32, uint32);
#else
void __OSFiberThreadEntry(void* thread); void __OSFiberThreadEntry(void* thread);
#endif
void __OSAddReadyThreadToRunQueue(OSThread_t* thread); void __OSAddReadyThreadToRunQueue(OSThread_t* thread);
void __OSRemoveThreadFromRunQueues(OSThread_t* thread); void __OSRemoveThreadFromRunQueues(OSThread_t* thread);
}; };
@ -49,7 +53,7 @@ namespace coreinit
struct OSHostThread 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; thread->id = 0x8000;
if (!thread->deallocatorFunc.IsNull()) if (!thread->deallocatorFunc.IsNull())
{
__OSQueueThreadDeallocation(thread); __OSQueueThreadDeallocation(thread);
PPCCore_switchToSchedulerWithLock(); // make sure the deallocation function runs before we return
}
__OSUnlockScheduler(); __OSUnlockScheduler();
@ -1304,8 +1311,14 @@ namespace coreinit
__OSThreadStartTimeslice(hostThread->m_thread, &hostThread->ppcInstance); __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) void __OSFiberThreadEntry(void* _thread)
{ {
#endif
OSHostThread* hostThread = (OSHostThread*)_thread; OSHostThread* hostThread = (OSHostThread*)_thread;
#if defined(ARCH_X86_64) #if defined(ARCH_X86_64)
@ -1515,7 +1528,7 @@ namespace coreinit
} }
// queue thread deallocation to run after current thread finishes // 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) void __OSQueueThreadDeallocation(OSThread_t* thread)
{ {
uint32 coreIndex = OSGetCoreId(); uint32 coreIndex = OSGetCoreId();

View file

@ -144,6 +144,11 @@ namespace GX2
void GX2Command_StartNewCommandBuffer(uint32 numU32s) 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(); uint32 coreIndex = coreinit::OSGetCoreId();
auto& coreCBState = s_perCoreCBState[coreIndex]; auto& coreCBState = s_perCoreCBState[coreIndex];
numU32s = std::max<uint32>(numU32s, 0x100); numU32s = std::max<uint32>(numU32s, 0x100);

View file

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

View file

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

View file

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

View file

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

View file

@ -404,7 +404,7 @@ namespace snd_core
{ {
try 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) catch (std::runtime_error& ex)
{ {
@ -417,7 +417,7 @@ namespace snd_core
{ {
try 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) if(g_padAudio)
g_padVolume = g_padAudio->GetVolume(); g_padVolume = g_padAudio->GetVolume();
} }
@ -442,6 +442,11 @@ namespace snd_core
g_padAudio->Stop(); g_padAudio->Stop();
g_padAudio.reset(); g_padAudio.reset();
} }
if (g_portalAudio)
{
g_portalAudio->Stop();
g_portalAudio.reset();
}
} }
void AXOut_updateDevicePlayState(bool isPlaying) void AXOut_updateDevicePlayState(bool isPlaying)
@ -462,6 +467,14 @@ namespace snd_core
else else
g_padAudio->Stop(); g_padAudio->Stop();
} }
if (g_portalAudio)
{
if (isPlaying)
g_portalAudio->Play();
else
g_portalAudio->Stop();
}
} }
// called periodically to check for AX updates // called periodically to check for AX updates

View file

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

View file

@ -13,10 +13,17 @@ namespace NCrypto
std::string base64Encode(const void* inputMem, size_t inputLen); std::string base64Encode(const void* inputMem, size_t inputLen);
std::vector<uint8> base64Decode(std::string_view inputStr); std::vector<uint8> base64Decode(std::string_view inputStr);
/* key helper struct */ /* key and iv helper struct */
struct AesKey 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 */ /* ECC Certificate */

View file

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

View file

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

View file

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

View file

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

View file

@ -13,6 +13,7 @@
std::shared_mutex g_audioMutex; std::shared_mutex g_audioMutex;
AudioAPIPtr g_tvAudio; AudioAPIPtr g_tvAudio;
AudioAPIPtr g_padAudio; AudioAPIPtr g_padAudio;
AudioAPIPtr g_portalAudio;
std::atomic_int32_t g_padVolume = 0; std::atomic_int32_t g_padVolume = 0;
uint32 IAudioAPI::s_audioDelay = 2; uint32 IAudioAPI::s_audioDelay = 2;
@ -80,7 +81,7 @@ void IAudioAPI::InitializeStatic()
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
s_availableApis[DirectSound] = true; s_availableApis[DirectSound] = true;
s_availableApis[XAudio2] = XAudio2API::InitializeStatic(); 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(); s_availableApis[XAudio27] = XAudio27API::InitializeStatic();
#endif #endif
#if HAS_CUBEB #if HAS_CUBEB
@ -97,30 +98,29 @@ bool IAudioAPI::IsAudioAPIAvailable(AudioAPI api)
return false; 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(AudioTypeToChannels(type));
sint32 channels = CemuConfig::AudioChannelsToNChannels(TV ? config.tv_channels : config.pad_channels); return CreateDeviceFromConfig(type, rate, channels, samples_per_block, bits_per_sample);
return CreateDeviceFromConfig(TV, 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; AudioAPIPtr audioAPIDev;
auto& config = GetConfig(); auto& config = GetConfig();
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api; 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 {}; return {};
IAudioAPI::DeviceDescriptionPtr device_description; IAudioAPI::DeviceDescriptionPtr device_description;
if (IAudioAPI::IsAudioAPIAvailable(audio_api)) if (IAudioAPI::IsAudioAPIAvailable(audio_api))
{ {
auto devices = IAudioAPI::GetDevices(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()) if (it != devices.end())
device_description = *it; 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"); 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 = 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; return audioAPIDev;
} }
@ -137,7 +138,7 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
if (!IsAudioAPIAvailable(api)) if (!IsAudioAPIAvailable(api))
return {}; return {};
switch(api) switch (api)
{ {
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
case DirectSound: case DirectSound:
@ -157,11 +158,11 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
} }
#endif #endif
#if HAS_CUBEB #if HAS_CUBEB
case Cubeb: case Cubeb:
{ {
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device); const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample); return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
} }
#endif #endif
default: default:
throw std::runtime_error(fmt::format("invalid audio api: {}", api)); throw std::runtime_error(fmt::format("invalid audio api: {}", api));
@ -173,7 +174,7 @@ std::vector<IAudioAPI::DeviceDescriptionPtr> IAudioAPI::GetDevices(AudioAPI api)
if (!IsAudioAPIAvailable(api)) if (!IsAudioAPIAvailable(api))
return {}; return {};
switch(api) switch (api)
{ {
#if BOOST_OS_WINDOWS #if BOOST_OS_WINDOWS
case DirectSound: case DirectSound:
@ -209,3 +210,51 @@ uint32 IAudioAPI::GetAudioDelay() const
{ {
return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay; return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay;
} }
AudioChannels IAudioAPI::AudioTypeToChannels(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_channels;
case Gamepad:
return config.pad_channels;
case Portal:
return kMono;
default:
return kMono;
}
}
std::wstring IAudioAPI::GetDeviceFromType(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_device;
case Gamepad:
return config.pad_device;
case Portal:
return config.portal_device;
default:
return L"";
}
}
sint32 IAudioAPI::GetVolumeFromType(AudioType type)
{
auto& config = GetConfig();
switch (type)
{
case TV:
return config.tv_volume;
case Gamepad:
return config.pad_volume;
case Portal:
return config.portal_volume;
default:
return 0;
}
}

View file

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

View file

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

View file

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

View file

@ -280,6 +280,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
tv_volume = audio.get("TVVolume", 20); tv_volume = audio.get("TVVolume", 20);
pad_volume = audio.get("PadVolume", 0); pad_volume = audio.get("PadVolume", 0);
input_volume = audio.get("InputVolume", 20); input_volume = audio.get("InputVolume", 20);
portal_volume = audio.get("PortalVolume", 20);
const auto tv = audio.get("TVDevice", ""); const auto tv = audio.get("TVDevice", "");
try try
@ -311,6 +312,16 @@ void CemuConfig::Load(XMLConfigParser& parser)
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", input_device_name); 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 // account
auto acc = parser.get("Account"); auto acc = parser.get("Account");
account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id); account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id);
@ -517,9 +528,11 @@ void CemuConfig::Save(XMLConfigParser& parser)
audio.set("TVVolume", tv_volume); audio.set("TVVolume", tv_volume);
audio.set("PadVolume", pad_volume); audio.set("PadVolume", pad_volume);
audio.set("InputVolume", input_volume); audio.set("InputVolume", input_volume);
audio.set("PortalVolume", portal_volume);
audio.set("TVDevice", boost::nowide::narrow(tv_device).c_str()); audio.set("TVDevice", boost::nowide::narrow(tv_device).c_str());
audio.set("PadDevice", boost::nowide::narrow(pad_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("InputDevice", boost::nowide::narrow(input_device).c_str());
audio.set("PortalDevice", boost::nowide::narrow(portal_device).c_str());
// account // account
auto acc = config.set("Account"); auto acc = config.set("Account");

View file

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

View file

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

View file

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

View file

@ -90,9 +90,9 @@ wxPanel* EmulatedUSBDeviceFrame::AddSkylanderPage(wxNotebook* notebook)
wxPanel* EmulatedUSBDeviceFrame::AddInfinityPage(wxNotebook* notebook) wxPanel* EmulatedUSBDeviceFrame::AddInfinityPage(wxNotebook* notebook)
{ {
auto* panel = new wxPanel(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* 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); auto* row = new wxBoxSizer(wxHORIZONTAL);
@ -283,7 +283,7 @@ void EmulatedUSBDeviceFrame::LoadSkylanderPath(uint8 slot, wxString path)
std::unique_ptr<FileStream> skyFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true)); std::unique_ptr<FileStream> skyFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true));
if (!skyFile) if (!skyFile)
{ {
wxMessageDialog open_error(this, "Error Opening File: " + path.c_str()); wxMessageDialog open_error(this, "Error Opening File: " + path);
open_error.ShowModal(); open_error.ShowModal();
return; return;
} }

View file

@ -565,6 +565,36 @@ wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook)
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); 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); audio_panel->SetSizerAndFit(audio_panel_sizer);
return audio_panel; return audio_panel;
} }
@ -1042,6 +1072,7 @@ void GeneralSettings2::StoreConfig()
config.tv_volume = m_tv_volume->GetValue(); config.tv_volume = m_tv_volume->GetValue();
config.pad_volume = m_pad_volume->GetValue(); config.pad_volume = m_pad_volume->GetValue();
config.input_volume = m_input_volume->GetValue(); config.input_volume = m_input_volume->GetValue();
config.portal_volume = m_portal_volume->GetValue();
config.tv_device.clear(); config.tv_device.clear();
const auto tv_device = m_tv_device->GetSelection(); const auto tv_device = m_tv_device->GetSelection();
@ -1070,6 +1101,15 @@ void GeneralSettings2::StoreConfig()
config.input_device = device_description->GetDescription()->GetIdentifier(); 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 // graphics
config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection(); config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection();
@ -1201,11 +1241,16 @@ void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
g_padVolume = event.GetInt(); g_padVolume = event.GetInt();
} }
} }
else else if (event.GetEventObject() == m_tv_volume)
{ {
if (g_tvAudio) if (g_tvAudio)
g_tvAudio->SetVolume(event.GetInt()); g_tvAudio->SetVolume(event.GetInt());
} }
else
{
if(g_portalAudio)
g_portalAudio->SetVolume(event.GetInt());
}
} }
@ -1265,10 +1310,12 @@ void GeneralSettings2::UpdateAudioDeviceList()
m_tv_device->Clear(); m_tv_device->Clear();
m_pad_device->Clear(); m_pad_device->Clear();
m_input_device->Clear(); m_input_device->Clear();
m_portal_device->Clear();
m_tv_device->Append(_("Disabled")); m_tv_device->Append(_("Disabled"));
m_pad_device->Append(_("Disabled")); m_pad_device->Append(_("Disabled"));
m_input_device->Append(_("Disabled")); m_input_device->Append(_("Disabled"));
m_portal_device->Append(_("Disabled"));
const auto audio_api = (IAudioAPI::AudioAPI)GetConfig().audio_api; const auto audio_api = (IAudioAPI::AudioAPI)GetConfig().audio_api;
const auto devices = IAudioAPI::GetDevices(audio_api); const auto devices = IAudioAPI::GetDevices(audio_api);
@ -1276,6 +1323,7 @@ void GeneralSettings2::UpdateAudioDeviceList()
{ {
m_tv_device->Append(device->GetName(), new wxDeviceDescription(device)); m_tv_device->Append(device->GetName(), new wxDeviceDescription(device));
m_pad_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; const auto input_audio_api = IAudioInputAPI::Cubeb; //(IAudioAPI::AudioAPI)GetConfig().input_audio_api;
@ -1295,6 +1343,8 @@ void GeneralSettings2::UpdateAudioDeviceList()
m_input_device->SetSelection(0); m_input_device->SetSelection(0);
m_portal_device->SetSelection(0);
// todo reset global instance of audio device // todo reset global instance of audio device
} }
@ -1817,6 +1867,22 @@ void GeneralSettings2::ApplyConfig()
else else
m_input_device->SetSelection(0); 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 // account
UpdateOnlineAccounts(); UpdateOnlineAccounts();
m_active_account->SetSelection(0); m_active_account->SetSelection(0);
@ -1977,6 +2043,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) void GeneralSettings2::OnAudioDeviceSelected(wxCommandEvent& event)

View file

@ -63,9 +63,9 @@ private:
// Audio // Audio
wxChoice* m_audio_api; wxChoice* m_audio_api;
wxSlider *m_audio_latency; 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_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 // Account
wxButton* m_create_account, * m_delete_account; wxButton* m_create_account, * m_delete_account;

View file

@ -96,6 +96,7 @@ enum
MAINFRAME_MENU_ID_OPTIONS_GENERAL2, MAINFRAME_MENU_ID_OPTIONS_GENERAL2,
MAINFRAME_MENU_ID_OPTIONS_AUDIO, MAINFRAME_MENU_ID_OPTIONS_AUDIO,
MAINFRAME_MENU_ID_OPTIONS_INPUT, MAINFRAME_MENU_ID_OPTIONS_INPUT,
MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS,
// options -> account // options -> account
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350, MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11, MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
@ -145,6 +146,7 @@ enum
MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE, MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE,
// debug->logging // debug->logging
MAINFRAME_MENU_ID_DEBUG_LOGGING_MESSAGE = 21499,
MAINFRAME_MENU_ID_DEBUG_LOGGING0 = 21500, MAINFRAME_MENU_ID_DEBUG_LOGGING0 = 21500,
MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO = 21599, MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO = 21599,
// debug->dump // debug->dump
@ -193,6 +195,7 @@ EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, 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_AUDIO, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput) EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS, MainWindow::OnOptionsInput)
// tools menu // tools menu
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput) EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput) EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
@ -293,8 +296,13 @@ private:
}; };
MainWindow::MainWindow() 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); gui_initHandleContextFromWxWidgetsWindow(g_window_info.window_main, this);
g_mainFrame = this; g_mainFrame = this;
CafeSystem::SetImplementation(this); CafeSystem::SetImplementation(this);
@ -918,6 +926,7 @@ void MainWindow::OnOptionsInput(wxCommandEvent& event)
break; break;
} }
case MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS:
case MAINFRAME_MENU_ID_OPTIONS_GENERAL2: case MAINFRAME_MENU_ID_OPTIONS_GENERAL2:
{ {
OpenSettings(); OpenSettings();
@ -1878,7 +1887,7 @@ public:
auto versionString = formatWxString(_("Cemu\nVersion {0}\nCompiled on {1}\nOriginal authors: {2}"), BUILD_VERSION_STRING, BUILD_DATE, "Exzap, Petergov"); 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 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->AddSpacer(3);
sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4)); sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4));
@ -1898,95 +1907,105 @@ public:
// zLib // zLib
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "zLib ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "zLib ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.zlib.net", "https://www.zlib.net"), 0); lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.zlib.net", "https://www.zlib.net"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// wxWidgets // wxWidgets
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "wxWidgets ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "wxWidgets ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.wxwidgets.org/", "https://www.wxwidgets.org/"), 0); lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.wxwidgets.org/", "https://www.wxwidgets.org/"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// OpenSSL // OpenSSL
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "OpenSSL ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "OpenSSL ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.openssl.org/", "https://www.openssl.org/"), 0); lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.openssl.org/", "https://www.openssl.org/"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// libcurl // libcurl
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "libcurl ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "libcurl ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://curl.haxx.se/libcurl/", "https://curl.haxx.se/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, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// imgui // imgui
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "imgui ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "imgui ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/ocornut/imgui", "https://github.com/ocornut/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, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// fontawesome // fontawesome
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "fontawesome ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "fontawesome ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/FortAwesome/Font-Awesome", "https://github.com/FortAwesome/Font-Awesome"), 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, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// boost // boost
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "boost ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "boost ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.boost.org", "https://www.boost.org"), 0); lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.boost.org", "https://www.boost.org"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// libusb // libusb
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "libusb ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "libusb ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://libusb.info", "https://libusb.info"), 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); lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
#endif
// icons // icons
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "icons from "), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "icons from "), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://icons8.com", "https://icons8.com"), 0); lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://icons8.com", "https://icons8.com"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// Lato font (are we still using it?) // Lato font (are we still using it?)
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); 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); sizer->Add(lineSizer);
} }
// SDL // SDL
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "SDL ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "SDL ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/libsdl-org/SDL", "https://github.com/libsdl-org/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, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
// IH264 // IH264
{ {
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "Modified ih264 from Android project ("), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, "Modified ih264 from Android project ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "Source", "https://cemu.info/oss/ih264d.zip"), 0); lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "Source", "https://cemu.info/oss/ih264d.zip"), 0);
lineSizer->Add(new wxStaticText(parent, -1, " "), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, " "), 0);
wxHyperlinkCtrl* noticeLink = new wxHyperlinkCtrl(parent, -1, "NOTICE", ""); wxHyperlinkCtrl* noticeLink = new wxHyperlinkCtrl(parent, wxID_ANY, "NOTICE", "");
noticeLink->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& event) noticeLink->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& event)
{ {
fs::path tempPath = fs::temp_directory_path(); fs::path tempPath = fs::temp_directory_path();
@ -2020,7 +2039,7 @@ public:
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(tempPath)))); wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(tempPath))));
}); });
lineSizer->Add(noticeLink, 0); lineSizer->Add(noticeLink, 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0); lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
sizer->Add(lineSizer); sizer->Add(lineSizer);
} }
} }
@ -2054,7 +2073,7 @@ public:
wxString& nameList = ((i % 2) == 0) ? nameListLeft : nameListRight; wxString& nameList = ((i % 2) == 0) ? nameListLeft : nameListRight;
if (i >= 2) if (i >= 2)
nameList.append("\n"); nameList.append("\n");
nameList.append(name); nameList.append(wxString::FromUTF8(name));
} }
gridSizer->Add(new wxStaticText(parent, wxID_ANY, nameListLeft), wxSizerFlags()); gridSizer->Add(new wxStaticText(parent, wxID_ANY, nameListLeft), wxSizerFlags());
@ -2184,6 +2203,9 @@ void MainWindow::RecreateMenu()
m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString); m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString);
m_padViewMenuItem->Check(GetConfig().pad_open); m_padViewMenuItem->Check(GetConfig().pad_open);
optionsMenu->AppendSeparator(); 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_GENERAL2, _("&General settings"));
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings")); optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings"));
@ -2232,7 +2254,7 @@ void MainWindow::RecreateMenu()
debugLoggingMenu->AppendSeparator(); debugLoggingMenu->AppendSeparator();
wxMenu* logCosModulesMenu = new wxMenu(); 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->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::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)); logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThreadSync), _("coreinit Thread-Synchronization API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThreadSync));

View file

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

View file

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

View file

@ -356,7 +356,7 @@ void TitleManager::OnTitleSearchComplete(wxCommandEvent& event)
void TitleManager::OnSetStatusBarText(wxSetStatusBarTextEvent& 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) void TitleManager::OnFilterChanged(wxCommandEvent& event)

View file

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

View file

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

View file

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

View file

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

View file

@ -38,7 +38,7 @@ wxDEFINE_EVENT(wxEVT_TITLE_REMOVED, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent); wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent);
wxTitleManagerList::wxTitleManagerList(wxWindow* parent, wxWindowID id) 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(); 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_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); m_callbackIdSaveList = CafeSaveList::RegisterCallback([](CafeSaveListCallbackEvent* evt, void* ctx) { ((wxTitleManagerList*)ctx)->HandleSaveListCallback(evt); }, this);
ShowSortIndicator(ColumnTitleId);
} }
wxTitleManagerList::~wxTitleManagerList() wxTitleManagerList::~wxTitleManagerList()
@ -74,7 +76,7 @@ wxTitleManagerList::~wxTitleManagerList()
boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry() const boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry() const
{ {
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); const auto selection = GetFirstSelected();
if (selection != wxNOT_FOUND) if (selection != wxNOT_FOUND)
{ {
const auto tmp = GetTitleEntry(selection); const auto tmp = GetTitleEntry(selection);
@ -87,7 +89,7 @@ boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSe
boost::optional<wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry() boost::optional<wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry()
{ {
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); const auto selection = GetFirstSelected();
if (selection != wxNOT_FOUND) if (selection != wxNOT_FOUND)
{ {
const auto tmp = GetTitleEntry(selection); const auto tmp = GetTitleEntry(selection);
@ -574,7 +576,7 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId, uint64 righ
} }
else 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()) if (progressDialog.WasCancelled())
writerContext.cancelled.store(true); writerContext.cancelled.store(true);
@ -757,7 +759,7 @@ void wxTitleManagerList::OnContextMenu(wxContextMenuEvent& event)
wxMenu menu; wxMenu menu;
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxTitleManagerList::OnContextMenuSelected, this); 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) if (selection == wxNOT_FOUND)
return; return;
@ -856,7 +858,7 @@ void wxTitleManagerList::OnContextMenuSelected(wxCommandEvent& event)
if (m_context_worker.valid() && !future_is_ready(m_context_worker)) if (m_context_worker.valid() && !future_is_ready(m_context_worker))
return; return;
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); const auto selection = GetFirstSelected();
if (selection == wxNOT_FOUND) if (selection == wxNOT_FOUND)
return; return;
@ -1174,53 +1176,47 @@ bool wxTitleManagerList::SortFunc(int column, const Type_t& v1, const Type_t& v2
if(entry1.version == entry2.version) if(entry1.version == entry2.version)
return SortFunc(ColumnTitleId, v1, v2); return SortFunc(ColumnTitleId, v1, v2);
return std::underlying_type_t<EntryType>(entry1.version) < std::underlying_type_t<EntryType>(entry2.version); return entry1.version < entry2.version;
} }
else if (column == ColumnRegion) else if (column == ColumnRegion)
{ {
if(entry1.region == entry2.region) if(entry1.region == entry2.region)
return SortFunc(ColumnTitleId, v1, v2); return SortFunc(ColumnTitleId, v1, v2);
return std::underlying_type_t<EntryType>(entry1.region) < std::underlying_type_t<EntryType>(entry2.region); return std::underlying_type_t<CafeConsoleRegion>(entry1.region) < std::underlying_type_t<CafeConsoleRegion>(entry2.region);
} }
else if (column == ColumnFormat) else if (column == ColumnFormat)
{ {
if(entry1.format == entry2.format) if(entry1.format == entry2.format)
return SortFunc(ColumnType, v1, v2); return SortFunc(ColumnType, v1, v2);
return std::underlying_type_t<EntryType>(entry1.format) < std::underlying_type_t<EntryType>(entry2.format); return std::underlying_type_t<EntryFormat>(entry1.format) < std::underlying_type_t<EntryFormat>(entry2.format);
} }
return false; return false;
} }
void wxTitleManagerList::SortEntries(int column) void wxTitleManagerList::SortEntries(int column)
{ {
if(column == -1) bool ascending;
if (column == -1)
{ {
column = m_last_column_sorted; column = GetSortIndicator();
m_last_column_sorted = -1;
if (column == -1) if (column == -1)
column = ColumnTitleId; column = ColumnTitleId;
ascending = IsAscendingSortIndicator();
} }
else
ascending = GetUpdatedAscendingSortIndicator(column);
if (column != ColumnTitleId && column != ColumnName && column != ColumnType && column != ColumnVersion && column != ColumnRegion && column != ColumnFormat) if (column != ColumnTitleId && column != ColumnName && column != ColumnType && column != ColumnVersion && column != ColumnRegion && column != ColumnFormat)
return; 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(), std::sort(m_sorted_data.begin(), m_sorted_data.end(),
[this, column](const Type_t& v1, const Type_t& v2) -> bool [this, column, ascending](const Type_t& v1, const Type_t& v2) -> bool {
{ return ascending ? SortFunc(column, v1, v2) : SortFunc(column, v2, v1);
const bool result = SortFunc(column, v1, v2); });
return m_sort_less ? result : !result;
});
ShowSortIndicator(column, ascending);
RefreshPage(); RefreshPage();
} }

View file

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

View file

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

View file

@ -615,7 +615,7 @@ void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
{ {
// double-clicked on disassembly (operation and operand data) // double-clicked on disassembly (operation and operand data)
wxString currentInstruction = wxEmptyString; 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
PPCAssemblerInOut ctx = { 0 }; PPCAssemblerInOut ctx = { 0 };
@ -637,7 +637,7 @@ void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
if (comment && comment->type == RplDebugSymbolComment) if (comment && comment->type == RplDebugSymbolComment)
old_comment = comment->comment; 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
rplDebugSymbol_createComment(virtualAddress, set_value_dialog.GetValue().wc_str()); rplDebugSymbol_createComment(virtualAddress, set_value_dialog.GetValue().wc_str());

View file

@ -196,7 +196,7 @@ void DumpCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
const uint32 offset = LineToOffset(line) + byte_index; const uint32 offset = LineToOffset(line) + byte_index;
const uint8 value = memory_readU8(offset); 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
const uint8 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16); const uint8 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);

View file

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

View file

@ -201,7 +201,7 @@ void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
if (position.x <= OFFSET_REGISTER + OFFSET_REGISTER_LABEL) if (position.x <= OFFSET_REGISTER + OFFSET_REGISTER_LABEL)
{ {
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index]; 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16); 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) if (position.x <= OFFSET_REGISTER + OFFSET_FPR)
{ {
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0; 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString()); const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
@ -234,7 +234,7 @@ void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
else else
{ {
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1; 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString()); const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());

View file

@ -339,7 +339,7 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event)
{ {
const uint32 register_index = id - kRegisterValueR0; const uint32 register_index = id - kRegisterValueR0;
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index]; 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16); 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 uint32 register_index = id - kRegisterValueFPR0_0;
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0; 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString()); 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 uint32 register_index = id - kRegisterValueFPR1_0;
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1; 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) if (set_value_dialog.ShowModal() == wxID_OK)
{ {
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString()); const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());

View file

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

View file

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

View file

@ -12,7 +12,7 @@ public:
: wxCommandEvent(EVT_LOG), m_filter(filter), m_message(message) { } : wxCommandEvent(EVT_LOG), m_filter(filter), m_message(message) { }
wxLogEvent(const wxLogEvent& event) 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); } wxEvent* Clone() const { return new wxLogEvent(*this); }

View file

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

View file

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

View file

@ -9,6 +9,7 @@
#include <cinttypes> #include <cinttypes>
#include <helpers/wxHelpers.h> #include <helpers/wxHelpers.h>
#include <wx/listctrl.h>
enum enum
{ {
@ -42,7 +43,7 @@ DebugPPCThreadsWindow::DebugPPCThreadsWindow(wxFrame& parent)
wxFrame::SetBackgroundColour(*wxWHITE); wxFrame::SetBackgroundColour(*wxWHITE);
auto* sizer = new wxBoxSizer(wxVERTICAL); 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)); 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); wxWindowUpdateLocker lock(m_thread_list);
long selected_thread = 0; 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) if (selection != wxNOT_FOUND)
selected_thread = m_thread_list->GetItemData(selection); selected_thread = m_thread_list->GetItemData(selection);
@ -267,12 +268,15 @@ void DebugPPCThreadsWindow::RefreshThreadList()
m_thread_list->SetItem(i, 12, tempStr); m_thread_list->SetItem(i, 12, tempStr);
if(selected_thread != 0 && selected_thread == (long)threadItrMPTR) 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); {
} m_thread_list->Select(i);
srwlock_activeThreadList.UnlockWrite(); m_thread_list->Focus(i);
__OSUnlockScheduler(); }
} }
srwlock_activeThreadList.UnlockWrite();
__OSUnlockScheduler();
}
m_thread_list->SetScrollPos(0, scrollPos, true); m_thread_list->SetScrollPos(0, scrollPos, true);
} }
@ -436,12 +440,11 @@ void DebugPPCThreadsWindow::OnThreadListRightClick(wxMouseEvent& event)
if (itemIndex == wxNOT_FOUND) if (itemIndex == wxNOT_FOUND)
return; return;
// select item // select item
m_thread_list->SetItemState(itemIndex, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED); m_thread_list->Focus(itemIndex);
long sel = m_thread_list->GetNextItem(-1, wxLIST_NEXT_ALL, long sel = m_thread_list->GetFirstSelected();
wxLIST_STATE_SELECTED); if (sel != wxNOT_FOUND)
if (sel != -1) m_thread_list->Select(sel, false);
m_thread_list->SetItemState(sel, 0, wxLIST_STATE_SELECTED); m_thread_list->Select(itemIndex);
m_thread_list->SetItemState(itemIndex, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
// check if thread is still on the list of active threads // check if thread is still on the list of active threads
MPTR threadMPTR = (MPTR)m_thread_list->GetItemData(itemIndex); MPTR threadMPTR = (MPTR)m_thread_list->GetItemData(itemIndex);
__OSLockScheduler(); __OSLockScheduler();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,7 +15,12 @@ Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* pri
ctx->uc_stack.ss_sp = m_stackPtr; ctx->uc_stack.ss_sp = m_stackPtr;
ctx->uc_stack.ss_size = stackSize; ctx->uc_stack.ss_size = stackSize;
ctx->uc_link = &ctx[0]; 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); makecontext(ctx, (void(*)())FiberEntryPoint, 1, userParam);
#endif
this->m_implData = (void*)ctx; this->m_implData = (void*)ctx;
} }

View file

@ -45,7 +45,11 @@ namespace MemMapper
void* r; void* r;
if(fromReservation) 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; r = baseAddr;
else else
r = nullptr; r = nullptr;

View file

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

View file

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