mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-02 13:01:18 +12:00
Merge branch 'main' into metal
This commit is contained in:
commit
4281959b64
81 changed files with 1316 additions and 488 deletions
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
|
@ -177,6 +177,9 @@ jobs:
|
|||
|
||||
build-macos:
|
||||
runs-on: macos-14
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
steps:
|
||||
- name: "Checkout repo"
|
||||
uses: actions/checkout@v4
|
||||
|
@ -236,7 +239,7 @@ jobs:
|
|||
cd build
|
||||
cmake .. ${{ env.BUILD_FLAGS }} \
|
||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \
|
||||
-DCMAKE_OSX_ARCHITECTURES=x86_64 \
|
||||
-DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \
|
||||
-DMACOS_BUNDLE=ON \
|
||||
-G Ninja
|
||||
|
||||
|
@ -259,5 +262,5 @@ jobs:
|
|||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cemu-bin-macos-x64
|
||||
name: cemu-bin-macos-${{ matrix.arch }}
|
||||
path: ./bin/Cemu.dmg
|
||||
|
|
|
@ -176,7 +176,7 @@ if (UNIX AND NOT APPLE)
|
|||
|
||||
if(ENABLE_BLUEZ)
|
||||
find_package(bluez REQUIRED)
|
||||
set(ENABLE_WIIMOTE ON)
|
||||
set(SUPPORTS_WIIMOTE ON)
|
||||
add_compile_definitions(HAS_BLUEZ)
|
||||
endif()
|
||||
|
||||
|
@ -204,7 +204,7 @@ endif()
|
|||
|
||||
if (ENABLE_HIDAPI)
|
||||
find_package(hidapi REQUIRED)
|
||||
set(ENABLE_WIIMOTE ON)
|
||||
set(SUPPORTS_WIIMOTE ON)
|
||||
add_compile_definitions(HAS_HIDAPI)
|
||||
endif ()
|
||||
|
||||
|
@ -238,7 +238,12 @@ endif()
|
|||
|
||||
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)
|
||||
endif()
|
||||
|
||||
|
@ -247,4 +252,4 @@ if (NOT ZArchive_FOUND)
|
|||
add_subdirectory("dependencies/ZArchive" EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(src)
|
3
dependencies/ih264d/CMakeLists.txt
vendored
3
dependencies/ih264d/CMakeLists.txt
vendored
|
@ -183,6 +183,9 @@ target_sources(ih264d PRIVATE
|
|||
"decoder/arm/ih264d_function_selector.c"
|
||||
)
|
||||
target_compile_options(ih264d PRIVATE -DARMV8)
|
||||
if(APPLE)
|
||||
target_sources(ih264d PRIVATE "common/armv8/macos_arm_symbol_aliases.s")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
|
||||
endif()
|
||||
|
|
|
@ -429,8 +429,13 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
|
|||
rev64 v7.4h, v2.4h
|
||||
ld1 {v3.2s}, [x10]
|
||||
sub x5, x3, #8
|
||||
#ifdef __APPLE__
|
||||
adrp x12, _ih264_gai1_intrapred_chroma_plane_coeffs1@GOTPAGE
|
||||
ldr x12, [x12, _ih264_gai1_intrapred_chroma_plane_coeffs1@GOTPAGEOFF]
|
||||
#else
|
||||
adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs1
|
||||
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs1]
|
||||
#endif
|
||||
usubl v10.8h, v5.8b, v1.8b
|
||||
ld1 {v8.8b, v9.8b}, [x12] // Load multiplication factors 1 to 8 into D3
|
||||
mov v8.d[1], v9.d[0]
|
||||
|
@ -484,10 +489,13 @@ ih264_intra_pred_chroma_8x8_mode_plane_av8:
|
|||
zip1 v1.8h, v0.8h, v2.8h
|
||||
zip2 v2.8h, v0.8h, v2.8h
|
||||
mov v0.16b, v1.16b
|
||||
|
||||
#ifdef __APPLE__
|
||||
adrp x12, _ih264_gai1_intrapred_chroma_plane_coeffs2@GOTPAGE
|
||||
ldr x12, [x12, _ih264_gai1_intrapred_chroma_plane_coeffs2@GOTPAGEOFF]
|
||||
#else
|
||||
adrp x12, :got:ih264_gai1_intrapred_chroma_plane_coeffs2
|
||||
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_chroma_plane_coeffs2]
|
||||
|
||||
#endif
|
||||
ld1 {v8.2s, v9.2s}, [x12]
|
||||
mov v8.d[1], v9.d[0]
|
||||
mov v10.16b, v8.16b
|
||||
|
|
|
@ -431,10 +431,13 @@ ih264_intra_pred_luma_16x16_mode_plane_av8:
|
|||
mov x10, x1 //top_left
|
||||
mov x4, #-1
|
||||
ld1 {v2.2s}, [x1], x8
|
||||
|
||||
#ifdef __APPLE__
|
||||
adrp x7, _ih264_gai1_intrapred_luma_plane_coeffs@GOTPAGE
|
||||
ldr x7, [x7, _ih264_gai1_intrapred_luma_plane_coeffs@GOTPAGEOFF]
|
||||
#else
|
||||
adrp x7, :got:ih264_gai1_intrapred_luma_plane_coeffs
|
||||
ldr x7, [x7, #:got_lo12:ih264_gai1_intrapred_luma_plane_coeffs]
|
||||
|
||||
#endif
|
||||
ld1 {v0.2s}, [x1]
|
||||
rev64 v2.8b, v2.8b
|
||||
ld1 {v6.2s, v7.2s}, [x7]
|
||||
|
|
|
@ -1029,9 +1029,13 @@ ih264_intra_pred_luma_8x8_mode_horz_u_av8:
|
|||
mov v3.d[0], v2.d[1]
|
||||
ext v4.16b, v2.16b , v2.16b , #1
|
||||
mov v5.d[0], v4.d[1]
|
||||
|
||||
#ifdef __APPLE__
|
||||
adrp x12, _ih264_gai1_intrapred_luma_8x8_horz_u@GOTPAGE
|
||||
ldr x12, [x12, _ih264_gai1_intrapred_luma_8x8_horz_u@GOTPAGEOFF]
|
||||
#else
|
||||
adrp x12, :got:ih264_gai1_intrapred_luma_8x8_horz_u
|
||||
ldr x12, [x12, #:got_lo12:ih264_gai1_intrapred_luma_8x8_horz_u]
|
||||
#endif
|
||||
uaddl v20.8h, v0.8b, v2.8b
|
||||
uaddl v22.8h, v1.8b, v3.8b
|
||||
uaddl v24.8h, v2.8b, v4.8b
|
||||
|
|
|
@ -142,14 +142,22 @@ ih264_weighted_bi_pred_luma_av8:
|
|||
sxtw x4, w4
|
||||
sxtw x5, w5
|
||||
stp x19, x20, [sp, #-16]!
|
||||
#ifndef __APPLE__
|
||||
ldr w8, [sp, #80] //Load wt2 in w8
|
||||
ldr w9, [sp, #88] //Load ofst1 in w9
|
||||
add w6, w6, #1 //w6 = log_WD + 1
|
||||
neg w10, w6 //w10 = -(log_WD + 1)
|
||||
dup v0.8h, w10 //Q0 = -(log_WD + 1) (32-bit)
|
||||
ldr w10, [sp, #96] //Load ofst2 in w10
|
||||
ldr w11, [sp, #104] //Load ht in w11
|
||||
ldr w12, [sp, #112] //Load wd in w12
|
||||
#else
|
||||
ldr w8, [sp, #80] //Load wt2 in w8
|
||||
ldr w9, [sp, #84] //Load ofst1 in w9
|
||||
ldr w10, [sp, #88] //Load ofst2 in w10
|
||||
ldr w11, [sp, #92] //Load ht in w11
|
||||
ldr w12, [sp, #96] //Load wd in w12
|
||||
#endif
|
||||
add w6, w6, #1 //w6 = log_WD + 1
|
||||
neg w10, w6 //w10 = -(log_WD + 1)
|
||||
dup v0.8h, w10 //Q0 = -(log_WD + 1) (32-bit)
|
||||
add w9, w9, #1 //w9 = ofst1 + 1
|
||||
add w9, w9, w10 //w9 = ofst1 + ofst2 + 1
|
||||
mov v2.s[0], w7
|
||||
|
@ -424,17 +432,24 @@ ih264_weighted_bi_pred_chroma_av8:
|
|||
sxtw x5, w5
|
||||
stp x19, x20, [sp, #-16]!
|
||||
|
||||
|
||||
#ifndef __APPLE__
|
||||
ldr w8, [sp, #80] //Load wt2 in w8
|
||||
ldr w9, [sp, #88] //Load ofst1 in w9
|
||||
ldr w10, [sp, #96] //Load ofst2 in w10
|
||||
ldr w11, [sp, #104] //Load ht in w11
|
||||
ldr w12, [sp, #112] //Load wd in w12
|
||||
#else
|
||||
ldr w8, [sp, #80] //Load wt2 in w8
|
||||
ldr w9, [sp, #84] //Load ofst1 in w9
|
||||
ldr w10, [sp, #88] //Load ofst2 in w10
|
||||
ldr w11, [sp, #92] //Load ht in w11
|
||||
ldr w12, [sp, #96] //Load wd in w12
|
||||
#endif
|
||||
dup v4.4s, w8 //Q2 = (wt2_u, wt2_v) (32-bit)
|
||||
dup v2.4s, w7 //Q1 = (wt1_u, wt1_v) (32-bit)
|
||||
add w6, w6, #1 //w6 = log_WD + 1
|
||||
ldr w9, [sp, #88] //Load ofst1 in w9
|
||||
ldr w10, [sp, #96] //Load ofst2 in w10
|
||||
neg w20, w6 //w20 = -(log_WD + 1)
|
||||
dup v0.8h, w20 //Q0 = -(log_WD + 1) (16-bit)
|
||||
ldr w11, [sp, #104] //Load ht in x11
|
||||
ldr w12, [sp, #112] //Load wd in x12
|
||||
dup v20.8h, w9 //0ffset1
|
||||
dup v21.8h, w10 //0ffset2
|
||||
srhadd v6.8b, v20.8b, v21.8b
|
||||
|
|
185
dependencies/ih264d/common/armv8/macos_arm_symbol_aliases.s
vendored
Normal file
185
dependencies/ih264d/common/armv8/macos_arm_symbol_aliases.s
vendored
Normal 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
|
|
@ -101,13 +101,21 @@ if (MACOS_BUNDLE)
|
|||
endforeach(folder)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/debug/lib/libusb-1.0.0.dylib")
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/lib/libusb-1.0.0.dylib")
|
||||
else()
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-osx/lib/libusb-1.0.0.dylib")
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/lib/libusb-1.0.0.dylib")
|
||||
endif()
|
||||
|
||||
if (EXISTS "/usr/local/lib/libMoltenVK.dylib")
|
||||
set(MOLTENVK_PATH "/usr/local/lib/libMoltenVK.dylib")
|
||||
elseif (EXISTS "/opt/homebrew/lib/libMoltenVK.dylib")
|
||||
set(MOLTENVK_PATH "/opt/homebrew/lib/libMoltenVK.dylib")
|
||||
else()
|
||||
message(FATAL_ERROR "failed to find libMoltenVK.dylib")
|
||||
endif ()
|
||||
|
||||
add_custom_command (TARGET CemuBin POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "/usr/local/lib/libMoltenVK.dylib" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${MOLTENVK_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libMoltenVK.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${LIBUSB_PATH}" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/Frameworks/libusb-1.0.0.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy "${CMAKE_SOURCE_DIR}/src/resource/update.sh" "${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/update.sh"
|
||||
COMMAND bash -c "install_name_tool -add_rpath @executable_path/../Frameworks ${CMAKE_SOURCE_DIR}/bin/${OUTPUT_NAME}.app/Contents/MacOS/${OUTPUT_NAME}"
|
||||
|
|
|
@ -599,7 +599,7 @@ if(ENABLE_METAL)
|
|||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "(aarch64)|(AARCH64)")
|
||||
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
|
||||
target_sources(CemuCafe PRIVATE
|
||||
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
|
||||
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.h
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#define SET_FST_ERROR(__code) if (errorCodeOut) *errorCodeOut = ErrorCode::__code
|
||||
|
||||
static_assert(sizeof(NCrypto::AesIv) == 16); // make sure IV is actually 16 bytes
|
||||
|
||||
class FSTDataSource
|
||||
{
|
||||
public:
|
||||
|
@ -868,7 +870,7 @@ static_assert(sizeof(FSTHashedBlock) == BLOCK_SIZE);
|
|||
struct FSTCachedRawBlock
|
||||
{
|
||||
FSTRawBlock blockData;
|
||||
uint8 ivForNextBlock[16];
|
||||
NCrypto::AesIv ivForNextBlock;
|
||||
uint64 lastAccess;
|
||||
};
|
||||
|
||||
|
@ -919,13 +921,13 @@ void FSTVolume::TrimCacheIfRequired(FSTCachedRawBlock** droppedRawBlock, FSTCach
|
|||
}
|
||||
}
|
||||
|
||||
void FSTVolume::DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, uint8 ivOut[16])
|
||||
void FSTVolume::DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, NCrypto::AesIv& ivOut)
|
||||
{
|
||||
memset(ivOut, 0, sizeof(ivOut));
|
||||
ivOut = {};
|
||||
if(blockIndex == 0)
|
||||
{
|
||||
ivOut[0] = (uint8)(clusterIndex >> 8);
|
||||
ivOut[1] = (uint8)(clusterIndex >> 0);
|
||||
ivOut.iv[0] = (uint8)(clusterIndex >> 8);
|
||||
ivOut.iv[1] = (uint8)(clusterIndex >> 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -936,20 +938,20 @@ void FSTVolume::DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex,
|
|||
auto itr = m_cacheDecryptedRawBlocks.find(cacheBlockId);
|
||||
if (itr != m_cacheDecryptedRawBlocks.end())
|
||||
{
|
||||
memcpy(ivOut, itr->second->ivForNextBlock, 16);
|
||||
ivOut = itr->second->ivForNextBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert(m_sectorSize >= 16);
|
||||
cemu_assert(m_sectorSize >= NCrypto::AesIv::SIZE);
|
||||
uint64 clusterOffset = (uint64)m_cluster[clusterIndex].offset * m_sectorSize;
|
||||
uint8 prevIV[16];
|
||||
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * m_sectorSize - 16, prevIV, 16) != 16)
|
||||
NCrypto::AesIv prevIV{};
|
||||
if (m_dataSource->readData(clusterIndex, clusterOffset, blockIndex * m_sectorSize - NCrypto::AesIv::SIZE, prevIV.iv, NCrypto::AesIv::SIZE) != NCrypto::AesIv::SIZE)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to read IV for raw FST block");
|
||||
m_detectedCorruption = true;
|
||||
return;
|
||||
}
|
||||
memcpy(ivOut, prevIV, 16);
|
||||
ivOut = prevIV;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -984,10 +986,10 @@ FSTCachedRawBlock* FSTVolume::GetDecryptedRawBlock(uint32 clusterIndex, uint32 b
|
|||
return nullptr;
|
||||
}
|
||||
// decrypt hash data
|
||||
uint8 iv[16]{};
|
||||
NCrypto::AesIv iv{};
|
||||
DetermineUnhashedBlockIV(clusterIndex, blockIndex, iv);
|
||||
memcpy(block->ivForNextBlock, block->blockData.rawData.data() + m_sectorSize - 16, 16);
|
||||
AES128_CBC_decrypt(block->blockData.rawData.data(), block->blockData.rawData.data(), m_sectorSize, m_partitionTitlekey.b, iv);
|
||||
std::copy(block->blockData.rawData.data() + m_sectorSize - NCrypto::AesIv::SIZE, block->blockData.rawData.data() + m_sectorSize, block->ivForNextBlock.iv);
|
||||
AES128_CBC_decrypt(block->blockData.rawData.data(), block->blockData.rawData.data(), m_sectorSize, m_partitionTitlekey.b, iv.iv);
|
||||
// if this is the next block, then hash it
|
||||
if(cluster.hasContentHash)
|
||||
{
|
||||
|
|
|
@ -83,7 +83,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
/* FST data (in memory) */
|
||||
enum class ClusterHashMode : uint8
|
||||
{
|
||||
|
@ -193,7 +192,7 @@ private:
|
|||
std::unordered_map<uint64, struct FSTCachedHashedBlock*> m_cacheDecryptedHashedBlocks;
|
||||
uint64 m_cacheAccessCounter{};
|
||||
|
||||
void DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, uint8 ivOut[16]);
|
||||
void DetermineUnhashedBlockIV(uint32 clusterIndex, uint32 blockIndex, NCrypto::AesIv& ivOut);
|
||||
|
||||
struct FSTCachedRawBlock* GetDecryptedRawBlock(uint32 clusterIndex, uint32 blockIndex);
|
||||
struct FSTCachedHashedBlock* GetDecryptedHashedBlock(uint32 clusterIndex, uint32 blockIndex);
|
||||
|
|
|
@ -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 aocTitleId = (currentTitleId & 0xFFFFFFFFull) | 0x0005000c00000000ull;
|
||||
|
@ -840,7 +840,7 @@ void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC
|
|||
}
|
||||
else
|
||||
{
|
||||
virtualMountPath = fs::path("vol/content/") / virtualMountPath;
|
||||
virtualMountPath = fs::path(virtualMountBase) / virtualMountPath;
|
||||
}
|
||||
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.file_size(), it.path().generic_string(), m_fs_priority);
|
||||
}
|
||||
|
@ -865,7 +865,7 @@ void GraphicPack2::LoadReplacedFiles()
|
|||
{
|
||||
// setup redirections
|
||||
fscDeviceRedirect_map();
|
||||
_iterateReplacedFiles(contentPath, false);
|
||||
_iterateReplacedFiles(contentPath, false, "vol/content/");
|
||||
}
|
||||
// /aoc/
|
||||
fs::path aocPath(gfxPackPath);
|
||||
|
@ -878,7 +878,18 @@ void GraphicPack2::LoadReplacedFiles()
|
|||
aocTitleId |= 0x0005000c00000000ULL;
|
||||
// setup redirections
|
||||
fscDeviceRedirect_map();
|
||||
_iterateReplacedFiles(aocPath, true);
|
||||
_iterateReplacedFiles(aocPath, true, nullptr);
|
||||
}
|
||||
|
||||
// /code/
|
||||
fs::path codePath(gfxPackPath);
|
||||
codePath.append("code");
|
||||
|
||||
if (fs::exists(codePath, ec))
|
||||
{
|
||||
// setup redirections
|
||||
fscDeviceRedirect_map();
|
||||
_iterateReplacedFiles(codePath, false, CafeSystem::GetInternalVirtualCodeFolder().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
void ApplyShaderPresets(std::string& shader_source) const;
|
||||
void LoadReplacedFiles();
|
||||
void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC);
|
||||
void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC, const char* virtualMountBase);
|
||||
|
||||
// ram mappings
|
||||
std::vector<std::pair<MPTR, MPTR>> m_ramMappings;
|
||||
|
|
|
@ -2,62 +2,70 @@
|
|||
#include "PPCInterpreterInternal.h"
|
||||
#include "PPCInterpreterHelper.h"
|
||||
|
||||
std::unordered_set<std::string> sUnsupportedHLECalls;
|
||||
std::unordered_set<std::string> s_unsupportedHLECalls;
|
||||
|
||||
void PPCInterpreter_handleUnsupportedHLECall(PPCInterpreter_t* hCPU)
|
||||
{
|
||||
const char* libFuncName = (char*)memory_getPointerFromVirtualOffset(hCPU->instructionPointer + 8);
|
||||
std::string tempString = fmt::format("Unsupported lib call: {}", libFuncName);
|
||||
if (sUnsupportedHLECalls.find(tempString) == sUnsupportedHLECalls.end())
|
||||
if (s_unsupportedHLECalls.find(tempString) == s_unsupportedHLECalls.end())
|
||||
{
|
||||
cemuLog_log(LogType::UnsupportedAPI, "{}", tempString);
|
||||
sUnsupportedHLECalls.emplace(tempString);
|
||||
s_unsupportedHLECalls.emplace(tempString);
|
||||
}
|
||||
hCPU->gpr[3] = 0;
|
||||
PPCInterpreter_nextInstruction(hCPU);
|
||||
}
|
||||
|
||||
std::vector<void(*)(PPCInterpreter_t* hCPU)>* sPPCHLETable{};
|
||||
static constexpr size_t HLE_TABLE_CAPACITY = 0x4000;
|
||||
HLECALL s_ppcHleTable[HLE_TABLE_CAPACITY]{};
|
||||
sint32 s_ppcHleTableWriteIndex = 0;
|
||||
std::mutex s_ppcHleTableMutex;
|
||||
|
||||
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName)
|
||||
{
|
||||
if (!sPPCHLETable)
|
||||
sPPCHLETable = new std::vector<void(*)(PPCInterpreter_t* hCPU)>();
|
||||
for (sint32 i = 0; i < sPPCHLETable->size(); i++)
|
||||
std::unique_lock _l(s_ppcHleTableMutex);
|
||||
if (s_ppcHleTableWriteIndex >= HLE_TABLE_CAPACITY)
|
||||
{
|
||||
if ((*sPPCHLETable)[i] == hleCall)
|
||||
return i;
|
||||
cemuLog_log(LogType::Force, "HLE table is full");
|
||||
cemu_assert(false);
|
||||
}
|
||||
HLEIDX newFuncIndex = (sint32)sPPCHLETable->size();
|
||||
sPPCHLETable->resize(sPPCHLETable->size() + 1);
|
||||
(*sPPCHLETable)[newFuncIndex] = hleCall;
|
||||
return newFuncIndex;
|
||||
for (sint32 i = 0; i < s_ppcHleTableWriteIndex; i++)
|
||||
{
|
||||
if (s_ppcHleTable[i] == hleCall)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
cemu_assert(s_ppcHleTableWriteIndex < HLE_TABLE_CAPACITY);
|
||||
s_ppcHleTable[s_ppcHleTableWriteIndex] = hleCall;
|
||||
HLEIDX funcIndex = s_ppcHleTableWriteIndex;
|
||||
s_ppcHleTableWriteIndex++;
|
||||
return funcIndex;
|
||||
}
|
||||
|
||||
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex)
|
||||
{
|
||||
if (funcIndex < 0 || funcIndex >= sPPCHLETable->size())
|
||||
if (funcIndex < 0 || funcIndex >= HLE_TABLE_CAPACITY)
|
||||
return nullptr;
|
||||
return sPPCHLETable->data()[funcIndex];
|
||||
return s_ppcHleTable[funcIndex];
|
||||
}
|
||||
|
||||
std::mutex g_hleLogMutex;
|
||||
std::mutex s_hleLogMutex;
|
||||
|
||||
void PPCInterpreter_virtualHLE(PPCInterpreter_t* hCPU, unsigned int opcode)
|
||||
{
|
||||
uint32 hleFuncId = opcode & 0xFFFF;
|
||||
if (hleFuncId == 0xFFD0)
|
||||
if (hleFuncId == 0xFFD0) [[unlikely]]
|
||||
{
|
||||
g_hleLogMutex.lock();
|
||||
s_hleLogMutex.lock();
|
||||
PPCInterpreter_handleUnsupportedHLECall(hCPU);
|
||||
g_hleLogMutex.unlock();
|
||||
return;
|
||||
s_hleLogMutex.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
// os lib function
|
||||
cemu_assert(hleFuncId < sPPCHLETable->size());
|
||||
auto hleCall = (*sPPCHLETable)[hleFuncId];
|
||||
auto hleCall = PPCInterpreter_getHLECall(hleFuncId);
|
||||
cemu_assert(hleCall);
|
||||
hleCall(hCPU);
|
||||
}
|
||||
|
|
|
@ -230,9 +230,9 @@ static inline float flushDenormalToZero(float f)
|
|||
|
||||
// HLE interface
|
||||
|
||||
typedef void(*HLECALL)(PPCInterpreter_t* hCPU);
|
||||
using HLECALL = void(*)(PPCInterpreter_t*);
|
||||
using HLEIDX = sint32;
|
||||
|
||||
typedef sint32 HLEIDX;
|
||||
HLEIDX PPCInterpreter_registerHLECall(HLECALL hleCall, std::string hleName);
|
||||
HLECALL PPCInterpreter_getHLECall(HLEIDX funcIndex);
|
||||
|
||||
|
|
|
@ -169,8 +169,10 @@ struct AArch64GenContext_t : CodeGenerator
|
|||
|
||||
bool processAllJumps()
|
||||
{
|
||||
for (auto&& [jumpStart, jumpInfo] : jumps)
|
||||
for (auto jump : jumps)
|
||||
{
|
||||
auto jumpStart = jump.first;
|
||||
auto jumpInfo = jump.second;
|
||||
bool success = std::visit(
|
||||
[&, this](const auto& jump) {
|
||||
setSize(jumpStart);
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#if defined(ARCH_X86_64) && defined(__GNUC__)
|
||||
#include <immintrin.h>
|
||||
#elif defined(__aarch64__)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
struct
|
||||
|
@ -555,6 +557,114 @@ void LatteIndices_fastConvertU32_AVX2(const void* indexDataInput, void* indexDat
|
|||
indexMax = std::max(indexMax, _maxIndex);
|
||||
indexMin = std::min(indexMin, _minIndex);
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
void LatteIndices_fastConvertU16_NEON(const void* indexDataInput, void* indexDataOutput, uint32 count, uint32& indexMin, uint32& indexMax)
|
||||
{
|
||||
const uint16* indicesU16BE = (const uint16*)indexDataInput;
|
||||
uint16* indexOutput = (uint16*)indexDataOutput;
|
||||
sint32 count8 = count >> 3;
|
||||
sint32 countRemaining = count & 7;
|
||||
|
||||
if (count8)
|
||||
{
|
||||
uint16x8_t mMin = vdupq_n_u16(0xFFFF);
|
||||
uint16x8_t mMax = vdupq_n_u16(0x0000);
|
||||
uint16x8_t mTemp;
|
||||
uint16x8_t* mRawIndices = (uint16x8_t*) indicesU16BE;
|
||||
indicesU16BE += count8 * 8;
|
||||
uint16x8_t* mOutputIndices = (uint16x8_t*) indexOutput;
|
||||
indexOutput += count8 * 8;
|
||||
|
||||
while (count8--)
|
||||
{
|
||||
mTemp = vld1q_u16((uint16*)mRawIndices);
|
||||
mRawIndices++;
|
||||
mTemp = vrev16q_u8(mTemp);
|
||||
mMin = vminq_u16(mMin, mTemp);
|
||||
mMax = vmaxq_u16(mMax, mTemp);
|
||||
vst1q_u16((uint16*)mOutputIndices, mTemp);
|
||||
mOutputIndices++;
|
||||
}
|
||||
|
||||
uint16* mMaxU16 = (uint16*)&mMax;
|
||||
uint16* mMinU16 = (uint16*)&mMin;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
indexMax = std::max(indexMax, (uint32)mMaxU16[i]);
|
||||
indexMin = std::min(indexMin, (uint32)mMinU16[i]);
|
||||
}
|
||||
}
|
||||
// process remaining indices
|
||||
uint32 _minIndex = 0xFFFFFFFF;
|
||||
uint32 _maxIndex = 0;
|
||||
for (sint32 i = countRemaining; (--i) >= 0;)
|
||||
{
|
||||
uint16 idx = _swapEndianU16(*indicesU16BE);
|
||||
*indexOutput = idx;
|
||||
indexOutput++;
|
||||
indicesU16BE++;
|
||||
_maxIndex = std::max(_maxIndex, (uint32)idx);
|
||||
_minIndex = std::min(_minIndex, (uint32)idx);
|
||||
}
|
||||
// update min/max
|
||||
indexMax = std::max(indexMax, _maxIndex);
|
||||
indexMin = std::min(indexMin, _minIndex);
|
||||
}
|
||||
|
||||
void LatteIndices_fastConvertU32_NEON(const void* indexDataInput, void* indexDataOutput, uint32 count, uint32& indexMin, uint32& indexMax)
|
||||
{
|
||||
const uint32* indicesU32BE = (const uint32*)indexDataInput;
|
||||
uint32* indexOutput = (uint32*)indexDataOutput;
|
||||
sint32 count8 = count >> 2;
|
||||
sint32 countRemaining = count & 3;
|
||||
|
||||
if (count8)
|
||||
{
|
||||
uint32x4_t mMin = vdupq_n_u32(0xFFFFFFFF);
|
||||
uint32x4_t mMax = vdupq_n_u32(0x00000000);
|
||||
uint32x4_t mTemp;
|
||||
uint32x4_t* mRawIndices = (uint32x4_t*) indicesU32BE;
|
||||
indicesU32BE += count8 * 4;
|
||||
uint32x4_t* mOutputIndices = (uint32x4_t*) indexOutput;
|
||||
indexOutput += count8 * 4;
|
||||
|
||||
while (count8--)
|
||||
{
|
||||
mTemp = vld1q_u32((uint32*)mRawIndices);
|
||||
mRawIndices++;
|
||||
mTemp = vrev32q_u8(mTemp);
|
||||
mMin = vminq_u32(mMin, mTemp);
|
||||
mMax = vmaxq_u32(mMax, mTemp);
|
||||
vst1q_u32((uint32*)mOutputIndices, mTemp);
|
||||
mOutputIndices++;
|
||||
}
|
||||
|
||||
uint32* mMaxU32 = (uint32*)&mMax;
|
||||
uint32* mMinU32 = (uint32*)&mMin;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
indexMax = std::max(indexMax, mMaxU32[i]);
|
||||
indexMin = std::min(indexMin, mMinU32[i]);
|
||||
}
|
||||
}
|
||||
// process remaining indices
|
||||
uint32 _minIndex = 0xFFFFFFFF;
|
||||
uint32 _maxIndex = 0;
|
||||
for (sint32 i = countRemaining; (--i) >= 0;)
|
||||
{
|
||||
uint32 idx = _swapEndianU32(*indicesU32BE);
|
||||
*indexOutput = idx;
|
||||
indexOutput++;
|
||||
indicesU32BE++;
|
||||
_maxIndex = std::max(_maxIndex, idx);
|
||||
_minIndex = std::min(_minIndex, idx);
|
||||
}
|
||||
// update min/max
|
||||
indexMax = std::max(indexMax, _maxIndex);
|
||||
indexMin = std::min(indexMin, _minIndex);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
|
@ -764,27 +874,31 @@ void LatteIndices_decode(const void* indexData, LatteIndexType indexType, uint32
|
|||
{
|
||||
if (indexType == LatteIndexType::U16_BE)
|
||||
{
|
||||
#if defined(ARCH_X86_64)
|
||||
#if defined(ARCH_X86_64)
|
||||
if (g_CPUFeatures.x86.avx2)
|
||||
LatteIndices_fastConvertU16_AVX2(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
else if (g_CPUFeatures.x86.sse4_1 && g_CPUFeatures.x86.ssse3)
|
||||
LatteIndices_fastConvertU16_SSE41(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
else
|
||||
LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
#else
|
||||
LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
#endif
|
||||
#elif defined(__aarch64__)
|
||||
LatteIndices_fastConvertU16_NEON(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
#else
|
||||
LatteIndices_convertBE<uint16>(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
#endif
|
||||
}
|
||||
else if (indexType == LatteIndexType::U32_BE)
|
||||
{
|
||||
#if defined(ARCH_X86_64)
|
||||
#if defined(ARCH_X86_64)
|
||||
if (g_CPUFeatures.x86.avx2)
|
||||
LatteIndices_fastConvertU32_AVX2(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
else
|
||||
LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
#else
|
||||
LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
#endif
|
||||
#elif defined(__aarch64__)
|
||||
LatteIndices_fastConvertU32_NEON(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
#else
|
||||
LatteIndices_convertBE<uint32>(indexData, indexOutputPtr, count, indexMin, indexMax);
|
||||
#endif
|
||||
}
|
||||
else if (indexType == LatteIndexType::U16_LE)
|
||||
{
|
||||
|
|
|
@ -213,7 +213,7 @@ class BootSoundPlayer
|
|||
|
||||
try
|
||||
{
|
||||
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(true, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
|
||||
bootSndAudioDev = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::TV, sampleRate, nChannels, samplesPerBlock, bitsPerSample);
|
||||
if(!bootSndAudioDev)
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -464,5 +464,34 @@ namespace iosu
|
|||
return static_cast<IOSUModule*>(&sIOSUModuleNNPDM);
|
||||
}
|
||||
|
||||
|
||||
bool GameListStat::LastPlayDate::operator<(const LastPlayDate& b) const
|
||||
{
|
||||
const auto& a = *this;
|
||||
|
||||
if(a.year < b.year)
|
||||
return true;
|
||||
if(a.year > b.year)
|
||||
return false;
|
||||
|
||||
// same year
|
||||
if(a.month < b.month)
|
||||
return true;
|
||||
if(a.month > b.month)
|
||||
return false;
|
||||
|
||||
// same year and month
|
||||
return a.day < b.day;
|
||||
}
|
||||
|
||||
bool GameListStat::LastPlayDate::operator==(const LastPlayDate& b) const
|
||||
{
|
||||
const auto& a = *this;
|
||||
return a.year == b.year &&
|
||||
a.month == b.month &&
|
||||
a.day == b.day;
|
||||
}
|
||||
std::weak_ordering GameListStat::LastPlayDate::operator<=>(const LastPlayDate& b) const = default;
|
||||
|
||||
};
|
||||
};
|
||||
|
|
|
@ -21,11 +21,15 @@ namespace iosu
|
|||
/* Helper for UI game list */
|
||||
struct GameListStat
|
||||
{
|
||||
struct
|
||||
struct LastPlayDate
|
||||
{
|
||||
uint32 year; // if 0 -> never played
|
||||
uint32 month;
|
||||
uint32 day;
|
||||
|
||||
bool operator<(const LastPlayDate& b) const;
|
||||
bool operator==(const LastPlayDate& b) const;
|
||||
std::weak_ordering operator<=>(const LastPlayDate& b) const;
|
||||
}last_played;
|
||||
uint32 numMinutesPlayed;
|
||||
};
|
||||
|
|
|
@ -502,6 +502,7 @@ namespace iosu
|
|||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, task_header_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &(*it));
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0x3C);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
||||
if (IsNetworkServiceSSLDisabled(ActiveSettings::GetNetworkService()))
|
||||
{
|
||||
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
|
||||
|
|
|
@ -132,7 +132,7 @@ namespace iosu
|
|||
|
||||
void convertMultiByteStringToBigEndianWidechar(const char* input, uint16be* output, sint32 maxOutputLength)
|
||||
{
|
||||
std::basic_string<uint16be> beStr = StringHelpers::FromUtf8(input);
|
||||
std::vector<uint16be> beStr = StringHelpers::FromUtf8(input);
|
||||
if (beStr.size() >= maxOutputLength - 1)
|
||||
beStr.resize(maxOutputLength-1);
|
||||
for (size_t i = 0; i < beStr.size(); i++)
|
||||
|
@ -723,7 +723,7 @@ namespace iosu
|
|||
{
|
||||
if(numVecIn != 0 || numVecOut != 1)
|
||||
return FPResult_InvalidIPCParam;
|
||||
std::basic_string<uint16be> myComment;
|
||||
std::vector<uint16be> myComment;
|
||||
if(g_fpd.nexFriendSession)
|
||||
{
|
||||
if(vecOut->size != MY_COMMENT_LENGTH * sizeof(uint16be))
|
||||
|
@ -735,8 +735,8 @@ namespace iosu
|
|||
g_fpd.nexFriendSession->getMyComment(myNexComment);
|
||||
myComment = StringHelpers::FromUtf8(myNexComment.commentString);
|
||||
}
|
||||
myComment.insert(0, 1, '\0');
|
||||
memcpy(vecOut->basePhys.GetPtr(), myComment.c_str(), MY_COMMENT_LENGTH * sizeof(uint16be));
|
||||
myComment.insert(myComment.begin(), '\0');
|
||||
memcpy(vecOut->basePhys.GetPtr(), myComment.data(), MY_COMMENT_LENGTH * sizeof(uint16be));
|
||||
return FPResult_Ok;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,11 @@ void nnNfp_update();
|
|||
|
||||
namespace coreinit
|
||||
{
|
||||
#ifdef __arm64__
|
||||
void __OSFiberThreadEntry(uint32, uint32);
|
||||
#else
|
||||
void __OSFiberThreadEntry(void* thread);
|
||||
#endif
|
||||
void __OSAddReadyThreadToRunQueue(OSThread_t* thread);
|
||||
void __OSRemoveThreadFromRunQueues(OSThread_t* thread);
|
||||
};
|
||||
|
@ -49,7 +53,7 @@ namespace coreinit
|
|||
|
||||
struct OSHostThread
|
||||
{
|
||||
OSHostThread(OSThread_t* thread) : m_thread(thread), m_fiber(__OSFiberThreadEntry, this, this)
|
||||
OSHostThread(OSThread_t* thread) : m_thread(thread), m_fiber((void(*)(void*))__OSFiberThreadEntry, this, this)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -713,7 +717,10 @@ namespace coreinit
|
|||
thread->id = 0x8000;
|
||||
|
||||
if (!thread->deallocatorFunc.IsNull())
|
||||
{
|
||||
__OSQueueThreadDeallocation(thread);
|
||||
PPCCore_switchToSchedulerWithLock(); // make sure the deallocation function runs before we return
|
||||
}
|
||||
|
||||
__OSUnlockScheduler();
|
||||
|
||||
|
@ -1304,8 +1311,14 @@ namespace coreinit
|
|||
__OSThreadStartTimeslice(hostThread->m_thread, &hostThread->ppcInstance);
|
||||
}
|
||||
|
||||
#ifdef __arm64__
|
||||
void __OSFiberThreadEntry(uint32 _high, uint32 _low)
|
||||
{
|
||||
uint64 _thread = (uint64) _high << 32 | _low;
|
||||
#else
|
||||
void __OSFiberThreadEntry(void* _thread)
|
||||
{
|
||||
#endif
|
||||
OSHostThread* hostThread = (OSHostThread*)_thread;
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
|
@ -1515,7 +1528,7 @@ namespace coreinit
|
|||
}
|
||||
|
||||
// queue thread deallocation to run after current thread finishes
|
||||
// the termination threads run at a higher priority on the same threads
|
||||
// the termination threads run at a higher priority on the same core
|
||||
void __OSQueueThreadDeallocation(OSThread_t* thread)
|
||||
{
|
||||
uint32 coreIndex = OSGetCoreId();
|
||||
|
|
|
@ -144,6 +144,11 @@ namespace GX2
|
|||
|
||||
void GX2Command_StartNewCommandBuffer(uint32 numU32s)
|
||||
{
|
||||
// On submission command buffers are padded to 32 byte alignment
|
||||
// but nowhere is it guaranteed that internal command buffers have their size aligned to 32 byte (even on console, but testing is required)
|
||||
// Thus the padding can write out of bounds but this seems to trigger only very rarely in partice. As a workaround we always pad the command buffer size to 32 bytes here
|
||||
numU32s = (numU32s + 7) & ~0x7;
|
||||
|
||||
uint32 coreIndex = coreinit::OSGetCoreId();
|
||||
auto& coreCBState = s_perCoreCBState[coreIndex];
|
||||
numU32s = std::max<uint32>(numU32s, 0x100);
|
||||
|
|
|
@ -145,7 +145,8 @@ namespace nn
|
|||
|
||||
if (name.size() != 0)
|
||||
{
|
||||
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
|
||||
auto name_utf16 = StringHelpers::FromUtf8(name);
|
||||
name_utf16.resize(std::min<size_t>(name_utf16.size(), 128));
|
||||
if (name_utf16.size() != 0)
|
||||
{
|
||||
for (int i = 0; i < name_utf16.size(); i++)
|
||||
|
@ -160,7 +161,8 @@ namespace nn
|
|||
|
||||
if (description.size() != 0)
|
||||
{
|
||||
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
|
||||
auto description_utf16 = StringHelpers::FromUtf8(description);
|
||||
description_utf16.resize(std::min<size_t>(description_utf16.size(), 256));
|
||||
if (description_utf16.size() != 0)
|
||||
{
|
||||
for (int i = 0; i < description_utf16.size(); i++)
|
||||
|
@ -206,7 +208,8 @@ namespace nn
|
|||
|
||||
if (screen_name.size() != 0)
|
||||
{
|
||||
auto screen_name_utf16 = StringHelpers::FromUtf8(screen_name).substr(0, 32);
|
||||
auto screen_name_utf16 = StringHelpers::FromUtf8(screen_name);
|
||||
screen_name_utf16.resize(std::min<size_t>(screen_name_utf16.size(), 32));
|
||||
if (screen_name_utf16.size() != 0)
|
||||
{
|
||||
for (int i = 0; i < screen_name_utf16.size(); i++)
|
||||
|
|
|
@ -250,7 +250,8 @@ namespace nn
|
|||
|
||||
if (name.size() != 0)
|
||||
{
|
||||
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
|
||||
auto name_utf16 = StringHelpers::FromUtf8(name);
|
||||
name_utf16.resize(std::min<size_t>(name_utf16.size(), 128));
|
||||
if (name_utf16.size() != 0)
|
||||
{
|
||||
for (int i = 0; i < name_utf16.size(); i++)
|
||||
|
@ -265,7 +266,8 @@ namespace nn
|
|||
|
||||
if (description.size() != 0)
|
||||
{
|
||||
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
|
||||
auto description_utf16 = StringHelpers::FromUtf8(description);
|
||||
description_utf16.resize(std::min<size_t>(description_utf16.size(), 256));
|
||||
if (description_utf16.size() != 0)
|
||||
{
|
||||
for (int i = 0; i < description_utf16.size(); i++)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "nn_olv_UploadFavoriteTypes.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
namespace nn
|
||||
{
|
||||
|
@ -115,7 +116,8 @@ namespace nn
|
|||
|
||||
if (name.size() != 0)
|
||||
{
|
||||
auto name_utf16 = StringHelpers::FromUtf8(name).substr(0, 128);
|
||||
auto name_utf16 = StringHelpers::FromUtf8(name);
|
||||
name_utf16.resize(std::min<size_t>(name_utf16.size(), 128));
|
||||
if (name_utf16.size() != 0)
|
||||
{
|
||||
for (int i = 0; i < name_utf16.size(); i++)
|
||||
|
@ -130,7 +132,8 @@ namespace nn
|
|||
|
||||
if (description.size() != 0)
|
||||
{
|
||||
auto description_utf16 = StringHelpers::FromUtf8(description).substr(0, 256);
|
||||
auto description_utf16 = StringHelpers::FromUtf8(description);
|
||||
description_utf16.resize(std::min<size_t>(description_utf16.size(), 256));
|
||||
if (description_utf16.size() != 0)
|
||||
{
|
||||
for (int i = 0; i < description_utf16.size(); i++)
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "Backend.h"
|
||||
|
||||
#include "Common/FileStream.h"
|
||||
#include "audio/IAudioAPI.h"
|
||||
#include "config/CemuConfig.h"
|
||||
|
||||
namespace nsyshid
|
||||
{
|
||||
|
@ -558,6 +560,26 @@ namespace nsyshid
|
|||
|
||||
Device::WriteResult SkylanderPortalDevice::Write(WriteMessage* message)
|
||||
{
|
||||
if (message->length != 64) {
|
||||
cemu_assert_error();
|
||||
}
|
||||
|
||||
if (!g_portalAudio)
|
||||
{
|
||||
// Portal audio is mono channel, 16 bit audio.
|
||||
// Audio is unsigned 16 bit, supplied as 64 bytes which is 32 samples per block
|
||||
g_portalAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::Portal, 8000, 32, 16);
|
||||
}
|
||||
std::array<sint16, 32> mono_samples;
|
||||
for (unsigned int i = 0; i < mono_samples.size(); ++i)
|
||||
{
|
||||
sint16 sample = static_cast<uint16>(message->data[i * 2 + 1]) << 8 | static_cast<uint16>(message->data[i * 2]);
|
||||
mono_samples[i] = sample;
|
||||
}
|
||||
if (g_portalAudio)
|
||||
{
|
||||
g_portalAudio->FeedBlock(mono_samples.data());
|
||||
}
|
||||
message->bytesWritten = message->length;
|
||||
return Device::WriteResult::Success;
|
||||
}
|
||||
|
@ -604,20 +626,20 @@ namespace nsyshid
|
|||
*(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength
|
||||
currentWritePtr = currentWritePtr + 9;
|
||||
// endpoint descriptor 1
|
||||
*(uint8*)(currentWritePtr + 0) = 7; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
||||
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
|
||||
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
||||
*(uint8*)(currentWritePtr + 0) = 7; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
||||
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
|
||||
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
||||
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
|
||||
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
||||
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
||||
currentWritePtr = currentWritePtr + 7;
|
||||
// endpoint descriptor 2
|
||||
*(uint8*)(currentWritePtr + 0) = 7; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
||||
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
|
||||
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
||||
*(uint8*)(currentWritePtr + 0) = 7; // bLength
|
||||
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
||||
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
|
||||
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
||||
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
|
||||
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
||||
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
||||
currentWritePtr = currentWritePtr + 7;
|
||||
|
||||
cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29);
|
||||
|
@ -628,8 +650,8 @@ namespace nsyshid
|
|||
}
|
||||
|
||||
bool SkylanderPortalDevice::SetIdle(uint8 ifIndex,
|
||||
uint8 reportId,
|
||||
uint8 duration)
|
||||
uint8 reportId,
|
||||
uint8 duration)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -404,7 +404,7 @@ namespace snd_core
|
|||
{
|
||||
try
|
||||
{
|
||||
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(true, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
||||
g_tvAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::TV, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
||||
}
|
||||
catch (std::runtime_error& ex)
|
||||
{
|
||||
|
@ -417,7 +417,7 @@ namespace snd_core
|
|||
{
|
||||
try
|
||||
{
|
||||
g_padAudio = IAudioAPI::CreateDeviceFromConfig(false, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
||||
g_padAudio = IAudioAPI::CreateDeviceFromConfig(IAudioAPI::AudioType::Gamepad, 48000, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
||||
if(g_padAudio)
|
||||
g_padVolume = g_padAudio->GetVolume();
|
||||
}
|
||||
|
@ -442,6 +442,11 @@ namespace snd_core
|
|||
g_padAudio->Stop();
|
||||
g_padAudio.reset();
|
||||
}
|
||||
if (g_portalAudio)
|
||||
{
|
||||
g_portalAudio->Stop();
|
||||
g_portalAudio.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AXOut_updateDevicePlayState(bool isPlaying)
|
||||
|
@ -462,6 +467,14 @@ namespace snd_core
|
|||
else
|
||||
g_padAudio->Stop();
|
||||
}
|
||||
|
||||
if (g_portalAudio)
|
||||
{
|
||||
if (isPlaying)
|
||||
g_portalAudio->Play();
|
||||
else
|
||||
g_portalAudio->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
// called periodically to check for AX updates
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "util/helpers/helpers.h"
|
||||
#include "config/CemuConfig.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "config/LaunchSettings.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
@ -144,6 +145,9 @@ bool cemuLog_log(LogType type, std::string_view text)
|
|||
if (!cemuLog_isLoggingEnabled(type))
|
||||
return false;
|
||||
|
||||
if (LaunchSettings::Verbose())
|
||||
std::cout << text << std::endl;
|
||||
|
||||
cemuLog_writeLineToLog(text);
|
||||
|
||||
const auto it = std::find_if(g_logging_window_mapping.cbegin(), g_logging_window_mapping.cend(),
|
||||
|
|
|
@ -13,10 +13,17 @@ namespace NCrypto
|
|||
std::string base64Encode(const void* inputMem, size_t inputLen);
|
||||
std::vector<uint8> base64Decode(std::string_view inputStr);
|
||||
|
||||
/* key helper struct */
|
||||
/* key and iv helper struct */
|
||||
struct AesKey
|
||||
{
|
||||
uint8 b[16];
|
||||
static constexpr size_t SIZE = 16;
|
||||
uint8 b[SIZE];
|
||||
};
|
||||
|
||||
struct AesIv
|
||||
{
|
||||
static constexpr size_t SIZE = 16;
|
||||
uint8 iv[SIZE];
|
||||
};
|
||||
|
||||
/* ECC Certificate */
|
||||
|
|
|
@ -51,15 +51,15 @@ class CafeWideString // fixed buffer size, null-terminated, PPC wchar_t (16bit b
|
|||
|
||||
bool assignFromUTF8(std::string_view sv)
|
||||
{
|
||||
std::basic_string<uint16be> beStr = StringHelpers::FromUtf8(sv);
|
||||
if(beStr.length() > N-1)
|
||||
std::vector<uint16be> beStr = StringHelpers::FromUtf8(sv);
|
||||
if(beStr.size() > N-1)
|
||||
{
|
||||
memcpy(data, beStr.data(), (N-1)*sizeof(uint16be));
|
||||
data[N-1] = 0;
|
||||
return false;
|
||||
}
|
||||
memcpy(data, beStr.data(), beStr.length()*sizeof(uint16be));
|
||||
data[beStr.length()] = '\0';
|
||||
memcpy(data, beStr.data(), beStr.size()*sizeof(uint16be));
|
||||
data[beStr.size()] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -310,7 +310,8 @@ inline uint64 __rdtsc()
|
|||
|
||||
inline void _mm_mfence()
|
||||
{
|
||||
|
||||
asm volatile("" ::: "memory");
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
inline unsigned char _addcarry_u64(unsigned char carry, unsigned long long a, unsigned long long b, unsigned long long *result)
|
||||
|
@ -385,8 +386,6 @@ template <typename T1, typename T2>
|
|||
constexpr bool HAS_FLAG(T1 flags, T2 test_flag) { return (flags & (T1)test_flag) == (T1)test_flag; }
|
||||
template <typename T1, typename T2>
|
||||
constexpr bool HAS_BIT(T1 value, T2 index) { return (value & ((T1)1 << index)) != 0; }
|
||||
template <typename T>
|
||||
constexpr void SAFE_RELEASE(T& p) { if (p) { p->Release(); p = nullptr; } }
|
||||
|
||||
template <typename T>
|
||||
constexpr uint32_t ppcsizeof() { return (uint32_t) sizeof(T); }
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#include "DirectSoundAPI.h"
|
||||
|
||||
#include "gui/wxgui.h"
|
||||
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "gui/guiWrapper.h"
|
||||
#include <wrl/client.h>
|
||||
|
||||
#pragma comment(lib, "Dsound.lib")
|
||||
|
||||
|
@ -15,12 +14,9 @@ std::wstring DirectSoundAPI::DirectSoundDeviceDescription::GetIdentifier() const
|
|||
DirectSoundAPI::DirectSoundAPI(GUID* guid, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
: IAudioAPI(samplerate, channels, samples_per_block, bits_per_sample)
|
||||
{
|
||||
LPDIRECTSOUND8 direct_sound;
|
||||
if (DirectSoundCreate8(guid, &direct_sound, nullptr) != DS_OK)
|
||||
if (DirectSoundCreate8(guid, &m_direct_sound, nullptr) != DS_OK)
|
||||
throw std::runtime_error("can't create directsound device");
|
||||
|
||||
m_direct_sound = decltype(m_direct_sound)(direct_sound);
|
||||
|
||||
if (FAILED(m_direct_sound->SetCooperativeLevel(gui_getWindowInfo().window_main.hwnd, DSSCL_PRIORITY)))
|
||||
throw std::runtime_error("can't set directsound priority");
|
||||
|
||||
|
@ -30,7 +26,7 @@ DirectSoundAPI::DirectSoundAPI(GUID* guid, sint32 samplerate, sint32 channels, s
|
|||
bd.dwBufferBytes = kBufferCount * m_bytesPerBlock; // kBlockCount * (samples_per_block * channels * (bits_per_sample / 8));
|
||||
bd.lpwfxFormat = (LPWAVEFORMATEX)&m_wfx;
|
||||
|
||||
LPDIRECTSOUNDBUFFER sound_buffer;
|
||||
Microsoft::WRL::ComPtr<IDirectSoundBuffer> sound_buffer;
|
||||
if (FAILED(m_direct_sound->CreateSoundBuffer(&bd, &sound_buffer, nullptr)))
|
||||
throw std::runtime_error("can't create directsound soundbuffer");
|
||||
|
||||
|
@ -41,27 +37,17 @@ DirectSoundAPI::DirectSoundAPI(GUID* guid, sint32 samplerate, sint32 channels, s
|
|||
|
||||
m_sound_buffer_size = caps.dwBufferBytes;
|
||||
|
||||
LPDIRECTSOUNDBUFFER8 sound_buffer8;
|
||||
LPDIRECTSOUNDNOTIFY8 notify8;
|
||||
sound_buffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&sound_buffer8);
|
||||
Microsoft::WRL::ComPtr<IDirectSoundNotify8> notify8;
|
||||
|
||||
if (!sound_buffer8)
|
||||
if (FAILED(sound_buffer->QueryInterface(IID_IDirectSoundBuffer8, &m_sound_buffer)))
|
||||
{
|
||||
sound_buffer->Release();
|
||||
throw std::runtime_error("can't get directsound buffer interface");
|
||||
}
|
||||
|
||||
m_sound_buffer = decltype(m_sound_buffer)(sound_buffer8);
|
||||
|
||||
sound_buffer->QueryInterface(IID_IDirectSoundNotify8, (void**)¬ify8);
|
||||
if (!notify8)
|
||||
if (FAILED(sound_buffer->QueryInterface(IID_IDirectSoundNotify8, &m_notify)))
|
||||
{
|
||||
sound_buffer->Release();
|
||||
throw std::runtime_error("can't get directsound notify interface");
|
||||
}
|
||||
m_notify = decltype(m_notify)(notify8);
|
||||
|
||||
sound_buffer->Release();
|
||||
|
||||
{ // initialize sound buffer
|
||||
void *ptr1, *ptr2;
|
||||
|
@ -155,10 +141,6 @@ DirectSoundAPI::~DirectSoundAPI()
|
|||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
|
||||
m_notify.reset();
|
||||
m_sound_buffer.reset();
|
||||
m_direct_sound.reset();
|
||||
|
||||
for(auto entry : m_notify_event)
|
||||
{
|
||||
if (entry)
|
||||
|
@ -186,7 +168,7 @@ bool DirectSoundAPI::Stop()
|
|||
|
||||
bool DirectSoundAPI::FeedBlock(sint16* data)
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
std::lock_guard lock(m_mutex);
|
||||
if (m_buffer.size() > kBlockCount)
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "dropped direct sound block since too many buffers are queued");
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#define DIRECTSOUND_VERSION 0x0800
|
||||
#include <mmsystem.h>
|
||||
//#include <mmreg.h>
|
||||
#include <dsound.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "IAudioAPI.h"
|
||||
|
||||
|
@ -41,15 +41,10 @@ public:
|
|||
static std::vector<DeviceDescriptionPtr> GetInputDevices();
|
||||
|
||||
private:
|
||||
struct DirectSoundDeleter
|
||||
{
|
||||
void operator()(IUnknown* ptr) const { if (ptr) ptr->Release(); }
|
||||
};
|
||||
|
||||
std::unique_ptr<IDirectSound8, DirectSoundDeleter> m_direct_sound;
|
||||
//std::unique_ptr<IDirectSoundCapture8, DirectSoundDeleter> m_direct_sound_capture;
|
||||
std::unique_ptr<IDirectSoundBuffer8, DirectSoundDeleter> m_sound_buffer;
|
||||
std::unique_ptr<IDirectSoundNotify8, DirectSoundDeleter> m_notify;
|
||||
Microsoft::WRL::ComPtr<IDirectSound8> m_direct_sound;
|
||||
//Microsoft::WRL::ComPtr<IDirectSoundCapture8> m_direct_sound_capture;
|
||||
Microsoft::WRL::ComPtr<IDirectSoundBuffer8> m_sound_buffer;
|
||||
Microsoft::WRL::ComPtr<IDirectSoundNotify8> m_notify;
|
||||
|
||||
DWORD m_sound_buffer_size = 0;
|
||||
uint32_t m_offset = 0;
|
||||
|
|
|
@ -13,13 +13,14 @@
|
|||
std::shared_mutex g_audioMutex;
|
||||
AudioAPIPtr g_tvAudio;
|
||||
AudioAPIPtr g_padAudio;
|
||||
AudioAPIPtr g_portalAudio;
|
||||
std::atomic_int32_t g_padVolume = 0;
|
||||
|
||||
uint32 IAudioAPI::s_audioDelay = 2;
|
||||
std::array<bool, IAudioAPI::AudioAPIEnd> IAudioAPI::s_availableApis{};
|
||||
|
||||
IAudioAPI::IAudioAPI(uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
|
||||
: m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample)
|
||||
: m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample)
|
||||
{
|
||||
m_bytesPerBlock = samples_per_block * channels * (bits_per_sample / 8);
|
||||
InitWFX(m_samplerate, m_channels, m_bitsPerSample);
|
||||
|
@ -80,7 +81,7 @@ void IAudioAPI::InitializeStatic()
|
|||
#if BOOST_OS_WINDOWS
|
||||
s_availableApis[DirectSound] = true;
|
||||
s_availableApis[XAudio2] = XAudio2API::InitializeStatic();
|
||||
if(!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available
|
||||
if (!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available
|
||||
s_availableApis[XAudio27] = XAudio27API::InitializeStatic();
|
||||
#endif
|
||||
#if HAS_CUBEB
|
||||
|
@ -97,30 +98,29 @@ bool IAudioAPI::IsAudioAPIAvailable(AudioAPI api)
|
|||
return false;
|
||||
}
|
||||
|
||||
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
{
|
||||
auto& config = GetConfig();
|
||||
sint32 channels = CemuConfig::AudioChannelsToNChannels(TV ? config.tv_channels : config.pad_channels);
|
||||
return CreateDeviceFromConfig(TV, rate, channels, samples_per_block, bits_per_sample);
|
||||
sint32 channels = CemuConfig::AudioChannelsToNChannels(AudioTypeToChannels(type));
|
||||
return CreateDeviceFromConfig(type, rate, channels, samples_per_block, bits_per_sample);
|
||||
}
|
||||
|
||||
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
{
|
||||
AudioAPIPtr audioAPIDev;
|
||||
|
||||
auto& config = GetConfig();
|
||||
|
||||
const auto audio_api = (IAudioAPI::AudioAPI)config.audio_api;
|
||||
auto& selectedDevice = TV ? config.tv_device : config.pad_device;
|
||||
auto selectedDevice = GetDeviceFromType(type);
|
||||
|
||||
if(selectedDevice.empty())
|
||||
if (selectedDevice.empty())
|
||||
return {};
|
||||
|
||||
IAudioAPI::DeviceDescriptionPtr device_description;
|
||||
if (IAudioAPI::IsAudioAPIAvailable(audio_api))
|
||||
{
|
||||
auto devices = IAudioAPI::GetDevices(audio_api);
|
||||
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) {return d->GetIdentifier() == selectedDevice; });
|
||||
const auto it = std::find_if(devices.begin(), devices.end(), [&selectedDevice](const auto& d) { return d->GetIdentifier() == selectedDevice; });
|
||||
if (it != devices.end())
|
||||
device_description = *it;
|
||||
}
|
||||
|
@ -128,7 +128,8 @@ AudioAPIPtr IAudioAPI::CreateDeviceFromConfig(bool TV, sint32 rate, sint32 chann
|
|||
throw std::runtime_error("failed to find selected device while trying to create audio device");
|
||||
|
||||
audioAPIDev = CreateDevice(audio_api, device_description, rate, channels, samples_per_block, bits_per_sample);
|
||||
audioAPIDev->SetVolume(TV ? config.tv_volume : config.pad_volume);
|
||||
audioAPIDev->SetVolume(GetVolumeFromType(type));
|
||||
|
||||
return audioAPIDev;
|
||||
}
|
||||
|
||||
|
@ -137,7 +138,7 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
|
|||
if (!IsAudioAPIAvailable(api))
|
||||
return {};
|
||||
|
||||
switch(api)
|
||||
switch (api)
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
case DirectSound:
|
||||
|
@ -157,11 +158,11 @@ AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& de
|
|||
}
|
||||
#endif
|
||||
#if HAS_CUBEB
|
||||
case Cubeb:
|
||||
{
|
||||
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
|
||||
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
|
||||
}
|
||||
case Cubeb:
|
||||
{
|
||||
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
|
||||
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
throw std::runtime_error(fmt::format("invalid audio api: {}", api));
|
||||
|
@ -172,8 +173,8 @@ std::vector<IAudioAPI::DeviceDescriptionPtr> IAudioAPI::GetDevices(AudioAPI api)
|
|||
{
|
||||
if (!IsAudioAPIAvailable(api))
|
||||
return {};
|
||||
|
||||
switch(api)
|
||||
|
||||
switch (api)
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
case DirectSound:
|
||||
|
@ -209,3 +210,51 @@ uint32 IAudioAPI::GetAudioDelay() const
|
|||
{
|
||||
return m_audioDelayOverride > 0 ? m_audioDelayOverride : s_audioDelay;
|
||||
}
|
||||
|
||||
AudioChannels IAudioAPI::AudioTypeToChannels(AudioType type)
|
||||
{
|
||||
auto& config = GetConfig();
|
||||
switch (type)
|
||||
{
|
||||
case TV:
|
||||
return config.tv_channels;
|
||||
case Gamepad:
|
||||
return config.pad_channels;
|
||||
case Portal:
|
||||
return kMono;
|
||||
default:
|
||||
return kMono;
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring IAudioAPI::GetDeviceFromType(AudioType type)
|
||||
{
|
||||
auto& config = GetConfig();
|
||||
switch (type)
|
||||
{
|
||||
case TV:
|
||||
return config.tv_device;
|
||||
case Gamepad:
|
||||
return config.pad_device;
|
||||
case Portal:
|
||||
return config.portal_device;
|
||||
default:
|
||||
return L"";
|
||||
}
|
||||
}
|
||||
|
||||
sint32 IAudioAPI::GetVolumeFromType(AudioType type)
|
||||
{
|
||||
auto& config = GetConfig();
|
||||
switch (type)
|
||||
{
|
||||
case TV:
|
||||
return config.tv_volume;
|
||||
case Gamepad:
|
||||
return config.pad_volume;
|
||||
case Portal:
|
||||
return config.portal_volume;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <mmreg.h>
|
||||
#endif
|
||||
|
||||
#include "config/CemuConfig.h"
|
||||
|
||||
class IAudioAPI
|
||||
{
|
||||
friend class GeneralSettings2;
|
||||
|
@ -30,6 +32,13 @@ public:
|
|||
|
||||
using DeviceDescriptionPtr = std::shared_ptr<DeviceDescription>;
|
||||
|
||||
enum AudioType
|
||||
{
|
||||
TV = 0,
|
||||
Gamepad,
|
||||
Portal
|
||||
};
|
||||
|
||||
enum AudioAPI
|
||||
{
|
||||
DirectSound = 0,
|
||||
|
@ -62,8 +71,8 @@ public:
|
|||
static void InitializeStatic();
|
||||
static bool IsAudioAPIAvailable(AudioAPI api);
|
||||
|
||||
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(bool TV, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::unique_ptr<IAudioAPI> CreateDeviceFromConfig(AudioType type, sint32 rate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::unique_ptr<IAudioAPI> CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::vector<DeviceDescriptionPtr> GetDevices(AudioAPI api);
|
||||
|
||||
|
@ -84,6 +93,9 @@ protected:
|
|||
private:
|
||||
static uint32 s_audioDelay;
|
||||
void InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample);
|
||||
static AudioChannels AudioTypeToChannels(AudioType type);
|
||||
static std::wstring GetDeviceFromType(AudioType type);
|
||||
static sint32 GetVolumeFromType(AudioType type);
|
||||
|
||||
};
|
||||
|
||||
|
@ -93,3 +105,5 @@ extern AudioAPIPtr g_tvAudio;
|
|||
|
||||
extern AudioAPIPtr g_padAudio;
|
||||
extern std::atomic_int32_t g_padVolume;
|
||||
|
||||
extern AudioAPIPtr g_portalAudio;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
//#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
|
||||
|
||||
#include <wrl/client.h>
|
||||
#include <xaudio2.h>
|
||||
|
||||
#ifndef XAUDIO2_DLL
|
||||
|
@ -33,17 +34,15 @@ XAudio2API::XAudio2API(std::wstring device_id, uint32 samplerate, uint32 channel
|
|||
throw std::runtime_error("can't find XAudio2Create import");
|
||||
|
||||
HRESULT hres;
|
||||
IXAudio2* xaudio;
|
||||
if (FAILED((hres = _XAudio2Create(&xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR))))
|
||||
if (FAILED((hres = _XAudio2Create(&m_xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR))))
|
||||
throw std::runtime_error(fmt::format("can't create xaudio device (hres: {:#x})", hres));
|
||||
|
||||
m_xaudio = decltype(m_xaudio)(xaudio);
|
||||
|
||||
IXAudio2MasteringVoice* mastering_voice;
|
||||
if (FAILED((hres = m_xaudio->CreateMasteringVoice(&mastering_voice, channels, samplerate, 0, m_device_id.empty() ? nullptr : m_device_id.c_str()))))
|
||||
throw std::runtime_error(fmt::format("can't create xaudio mastering voice (hres: {:#x})", hres));
|
||||
|
||||
m_mastering_voice = decltype(m_mastering_voice)(mastering_voice);
|
||||
m_mastering_voice.reset(mastering_voice);
|
||||
|
||||
m_wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
m_wfx.Format.nChannels = channels;
|
||||
|
@ -88,12 +87,6 @@ XAudio2API::XAudio2API(std::wstring device_id, uint32 samplerate, uint32 channel
|
|||
m_xaudio->StartEngine();
|
||||
}
|
||||
|
||||
void XAudio2API::XAudioDeleter::operator()(IXAudio2* ptr) const
|
||||
{
|
||||
if (ptr)
|
||||
ptr->Release();
|
||||
}
|
||||
|
||||
void XAudio2API::VoiceDeleter::operator()(IXAudio2Voice* ptr) const
|
||||
{
|
||||
if (ptr)
|
||||
|
@ -106,10 +99,6 @@ XAudio2API::~XAudio2API()
|
|||
m_xaudio->StopEngine();
|
||||
|
||||
XAudio2API::Stop();
|
||||
|
||||
m_source_voice.reset();
|
||||
m_mastering_voice.reset();
|
||||
m_xaudio.reset();
|
||||
}
|
||||
|
||||
void XAudio2API::SetVolume(sint32 volume)
|
||||
|
@ -179,10 +168,10 @@ const std::vector<XAudio2API::DeviceDescriptionPtr>& XAudio2API::RefreshDevices(
|
|||
|
||||
try
|
||||
{
|
||||
struct IWbemLocator *wbem_locator = nullptr;
|
||||
Microsoft::WRL::ComPtr<IWbemLocator> wbem_locator;
|
||||
|
||||
HRESULT hres = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&wbem_locator);
|
||||
if (FAILED(hres) || !wbem_locator)
|
||||
HRESULT hres = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&wbem_locator));
|
||||
if (FAILED(hres))
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
std::shared_ptr<OLECHAR> path(SysAllocString(LR"(\\.\root\cimv2)"), SysFreeString);
|
||||
|
@ -191,20 +180,19 @@ const std::vector<XAudio2API::DeviceDescriptionPtr>& XAudio2API::RefreshDevices(
|
|||
std::shared_ptr<OLECHAR> name_row(SysAllocString(L"Name"), SysFreeString);
|
||||
std::shared_ptr<OLECHAR> device_id_row(SysAllocString(L"DeviceID"), SysFreeString);
|
||||
|
||||
IWbemServices *wbem_services = nullptr;
|
||||
Microsoft::WRL::ComPtr<IWbemServices> wbem_services;
|
||||
hres = wbem_locator->ConnectServer(path.get(), nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbem_services);
|
||||
wbem_locator->Release(); // Free memory resources.
|
||||
|
||||
if (FAILED(hres) || !wbem_services)
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
hres = CoSetProxyBlanket(wbem_services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
|
||||
if (FAILED(hres))
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
IEnumWbemClassObject* wbem_enum = nullptr;
|
||||
hres = CoSetProxyBlanket(wbem_services.Get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
|
||||
if (FAILED(hres))
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
Microsoft::WRL::ComPtr<IEnumWbemClassObject> wbem_enum;
|
||||
hres = wbem_services->ExecQuery(language.get(), query.get(), WBEM_FLAG_RETURN_WBEM_COMPLETE | WBEM_FLAG_FORWARD_ONLY, nullptr, &wbem_enum);
|
||||
if (FAILED(hres) || !wbem_enum)
|
||||
if (FAILED(hres))
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
ULONG returned;
|
||||
|
@ -250,11 +238,6 @@ const std::vector<XAudio2API::DeviceDescriptionPtr>& XAudio2API::RefreshDevices(
|
|||
auto default_device = std::make_shared<XAudio2DeviceDescription>(L"Primary Sound Driver", L"");
|
||||
s_devices.insert(s_devices.begin(), default_device);
|
||||
}
|
||||
|
||||
wbem_enum->Release();
|
||||
|
||||
// Clean up
|
||||
wbem_services->Release();
|
||||
}
|
||||
catch (const std::system_error& ex)
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <mmsystem.h>
|
||||
#include <mmreg.h>
|
||||
#include <dsound.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "IAudioAPI.h"
|
||||
|
||||
|
@ -50,11 +51,6 @@ private:
|
|||
|
||||
static const std::vector<DeviceDescriptionPtr>& RefreshDevices();
|
||||
|
||||
struct XAudioDeleter
|
||||
{
|
||||
void operator()(IXAudio2* ptr) const;
|
||||
};
|
||||
|
||||
struct VoiceDeleter
|
||||
{
|
||||
void operator()(IXAudio2Voice* ptr) const;
|
||||
|
@ -63,7 +59,7 @@ private:
|
|||
static HMODULE s_xaudio_dll;
|
||||
static std::vector<DeviceDescriptionPtr> s_devices;
|
||||
|
||||
std::unique_ptr<IXAudio2, XAudioDeleter> m_xaudio;
|
||||
Microsoft::WRL::ComPtr<IXAudio2> m_xaudio;
|
||||
std::wstring m_device_id;
|
||||
std::unique_ptr<IXAudio2MasteringVoice, VoiceDeleter> m_mastering_voice;
|
||||
std::unique_ptr<IXAudio2SourceVoice, VoiceDeleter> m_source_voice;
|
||||
|
|
|
@ -280,6 +280,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
|||
tv_volume = audio.get("TVVolume", 20);
|
||||
pad_volume = audio.get("PadVolume", 0);
|
||||
input_volume = audio.get("InputVolume", 20);
|
||||
portal_volume = audio.get("PortalVolume", 20);
|
||||
|
||||
const auto tv = audio.get("TVDevice", "");
|
||||
try
|
||||
|
@ -311,6 +312,16 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
|||
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", input_device_name);
|
||||
}
|
||||
|
||||
const auto portal_device_name = audio.get("PortalDevice", "");
|
||||
try
|
||||
{
|
||||
portal_device = boost::nowide::widen(portal_device_name);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "config load error: can't load input device: {}", portal_device_name);
|
||||
}
|
||||
|
||||
// account
|
||||
auto acc = parser.get("Account");
|
||||
account.m_persistent_id = acc.get("PersistentId", account.m_persistent_id);
|
||||
|
@ -517,9 +528,11 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
|||
audio.set("TVVolume", tv_volume);
|
||||
audio.set("PadVolume", pad_volume);
|
||||
audio.set("InputVolume", input_volume);
|
||||
audio.set("PortalVolume", portal_volume);
|
||||
audio.set("TVDevice", boost::nowide::narrow(tv_device).c_str());
|
||||
audio.set("PadDevice", boost::nowide::narrow(pad_device).c_str());
|
||||
audio.set("InputDevice", boost::nowide::narrow(input_device).c_str());
|
||||
audio.set("PortalDevice", boost::nowide::narrow(portal_device).c_str());
|
||||
|
||||
// account
|
||||
auto acc = config.set("Account");
|
||||
|
|
|
@ -531,8 +531,8 @@ struct CemuConfig
|
|||
sint32 audio_api = 0;
|
||||
sint32 audio_delay = 2;
|
||||
AudioChannels tv_channels = kStereo, pad_channels = kStereo, input_channels = kMono;
|
||||
sint32 tv_volume = 50, pad_volume = 0, input_volume = 50;
|
||||
std::wstring tv_device{ L"default" }, pad_device, input_device;
|
||||
sint32 tv_volume = 50, pad_volume = 0, input_volume = 50, portal_volume = 50;
|
||||
std::wstring tv_device{ L"default" }, pad_device, input_device, portal_device;
|
||||
|
||||
// account
|
||||
struct
|
||||
|
|
|
@ -59,6 +59,9 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
|
|||
desc.add_options()
|
||||
("help,h", "This help screen")
|
||||
("version,v", "Displays the version of Cemu")
|
||||
#if !BOOST_OS_WINDOWS
|
||||
("verbose", "Log to stdout")
|
||||
#endif
|
||||
|
||||
("game,g", po::wvalue<std::wstring>(), "Path of game to launch")
|
||||
("title-id,t", po::value<std::string>(), "Title ID of the title to be launched (overridden by --game)")
|
||||
|
@ -125,6 +128,9 @@ bool LaunchSettings::HandleCommandline(const std::vector<std::wstring>& args)
|
|||
return false; // exit in main
|
||||
}
|
||||
|
||||
if (vm.count("verbose"))
|
||||
s_verbose = true;
|
||||
|
||||
if (vm.count("game"))
|
||||
{
|
||||
std::wstring tmp = vm["game"].as<std::wstring>();
|
||||
|
|
|
@ -22,6 +22,8 @@ public:
|
|||
static std::optional<bool> RenderUpsideDownEnabled() { return s_render_upside_down; }
|
||||
static std::optional<bool> FullscreenEnabled() { return s_fullscreen; }
|
||||
|
||||
static bool Verbose() { return s_verbose; }
|
||||
|
||||
static bool GDBStubEnabled() { return s_enable_gdbstub; }
|
||||
static bool NSightModeEnabled() { return s_nsight_mode; }
|
||||
|
||||
|
@ -40,6 +42,8 @@ private:
|
|||
|
||||
inline static std::optional<bool> s_render_upside_down{};
|
||||
inline static std::optional<bool> s_fullscreen{};
|
||||
|
||||
inline static bool s_verbose = false;
|
||||
|
||||
inline static bool s_enable_gdbstub = false;
|
||||
inline static bool s_nsight_mode = false;
|
||||
|
|
|
@ -90,9 +90,9 @@ wxPanel* EmulatedUSBDeviceFrame::AddSkylanderPage(wxNotebook* notebook)
|
|||
wxPanel* EmulatedUSBDeviceFrame::AddInfinityPage(wxNotebook* notebook)
|
||||
{
|
||||
auto* panel = new wxPanel(notebook);
|
||||
auto* panelSizer = new wxBoxSizer(wxBOTH);
|
||||
auto* panelSizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto* box = new wxStaticBox(panel, wxID_ANY, _("Infinity Manager"));
|
||||
auto* boxSizer = new wxStaticBoxSizer(box, wxBOTH);
|
||||
auto* boxSizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||
|
||||
auto* row = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
|
@ -283,7 +283,7 @@ void EmulatedUSBDeviceFrame::LoadSkylanderPath(uint8 slot, wxString path)
|
|||
std::unique_ptr<FileStream> skyFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true));
|
||||
if (!skyFile)
|
||||
{
|
||||
wxMessageDialog open_error(this, "Error Opening File: " + path.c_str());
|
||||
wxMessageDialog open_error(this, "Error Opening File: " + path);
|
||||
open_error.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
@ -831,4 +831,4 @@ uint8 MoveDimensionFigureDialog::GetNewIndex() const
|
|||
std::array<std::optional<uint32>, 7> EmulatedUSBDeviceFrame::GetCurrentMinifigs()
|
||||
{
|
||||
return m_dimSlots;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -565,6 +565,36 @@ wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook)
|
|||
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
||||
}
|
||||
|
||||
{
|
||||
auto box = new wxStaticBox(audio_panel, wxID_ANY, _("Trap Team Portal"));
|
||||
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||
|
||||
auto portal_audio_row = new wxFlexGridSizer(0, 3, 0, 0);
|
||||
portal_audio_row->SetFlexibleDirection(wxBOTH);
|
||||
portal_audio_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
||||
|
||||
portal_audio_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
m_portal_device = new wxChoice(box, wxID_ANY, wxDefaultPosition);
|
||||
m_portal_device->SetMinSize(wxSize(300, -1));
|
||||
m_portal_device->SetToolTip(_("Select the active audio output device for Wii U GamePad"));
|
||||
portal_audio_row->Add(m_portal_device, 0, wxEXPAND | wxALL, 5);
|
||||
portal_audio_row->AddSpacer(0);
|
||||
|
||||
m_portal_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this);
|
||||
|
||||
portal_audio_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
m_portal_volume = new wxSlider(box, wxID_ANY, 100, 0, 100);
|
||||
portal_audio_row->Add(m_portal_volume, 0, wxEXPAND | wxALL, 5);
|
||||
auto audio_pad_volume_text = new wxStaticText(box, wxID_ANY, "100%");
|
||||
portal_audio_row->Add(audio_pad_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5);
|
||||
|
||||
m_portal_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_pad_volume_text));
|
||||
m_portal_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this);
|
||||
|
||||
box_sizer->Add(portal_audio_row, 1, wxEXPAND, 5);
|
||||
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
||||
}
|
||||
|
||||
audio_panel->SetSizerAndFit(audio_panel_sizer);
|
||||
return audio_panel;
|
||||
}
|
||||
|
@ -1042,6 +1072,7 @@ void GeneralSettings2::StoreConfig()
|
|||
config.tv_volume = m_tv_volume->GetValue();
|
||||
config.pad_volume = m_pad_volume->GetValue();
|
||||
config.input_volume = m_input_volume->GetValue();
|
||||
config.portal_volume = m_portal_volume->GetValue();
|
||||
|
||||
config.tv_device.clear();
|
||||
const auto tv_device = m_tv_device->GetSelection();
|
||||
|
@ -1070,6 +1101,15 @@ void GeneralSettings2::StoreConfig()
|
|||
config.input_device = device_description->GetDescription()->GetIdentifier();
|
||||
}
|
||||
|
||||
config.portal_device.clear();
|
||||
const auto portal_device = m_portal_device->GetSelection();
|
||||
if (portal_device != wxNOT_FOUND && portal_device != 0 && m_portal_device->HasClientObjectData())
|
||||
{
|
||||
const auto* device_description = (wxDeviceDescription*)m_portal_device->GetClientObject(portal_device);
|
||||
if (device_description)
|
||||
config.portal_device = device_description->GetDescription()->GetIdentifier();
|
||||
}
|
||||
|
||||
// graphics
|
||||
config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection();
|
||||
|
||||
|
@ -1201,11 +1241,16 @@ void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
|
|||
g_padVolume = event.GetInt();
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (event.GetEventObject() == m_tv_volume)
|
||||
{
|
||||
if (g_tvAudio)
|
||||
g_tvAudio->SetVolume(event.GetInt());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_portalAudio)
|
||||
g_portalAudio->SetVolume(event.GetInt());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1265,10 +1310,12 @@ void GeneralSettings2::UpdateAudioDeviceList()
|
|||
m_tv_device->Clear();
|
||||
m_pad_device->Clear();
|
||||
m_input_device->Clear();
|
||||
m_portal_device->Clear();
|
||||
|
||||
m_tv_device->Append(_("Disabled"));
|
||||
m_pad_device->Append(_("Disabled"));
|
||||
m_input_device->Append(_("Disabled"));
|
||||
m_portal_device->Append(_("Disabled"));
|
||||
|
||||
const auto audio_api = (IAudioAPI::AudioAPI)GetConfig().audio_api;
|
||||
const auto devices = IAudioAPI::GetDevices(audio_api);
|
||||
|
@ -1276,6 +1323,7 @@ void GeneralSettings2::UpdateAudioDeviceList()
|
|||
{
|
||||
m_tv_device->Append(device->GetName(), new wxDeviceDescription(device));
|
||||
m_pad_device->Append(device->GetName(), new wxDeviceDescription(device));
|
||||
m_portal_device->Append(device->GetName(), new wxDeviceDescription(device));
|
||||
}
|
||||
|
||||
const auto input_audio_api = IAudioInputAPI::Cubeb; //(IAudioAPI::AudioAPI)GetConfig().input_audio_api;
|
||||
|
@ -1295,6 +1343,8 @@ void GeneralSettings2::UpdateAudioDeviceList()
|
|||
|
||||
m_input_device->SetSelection(0);
|
||||
|
||||
m_portal_device->SetSelection(0);
|
||||
|
||||
// todo reset global instance of audio device
|
||||
}
|
||||
|
||||
|
@ -1817,6 +1867,22 @@ void GeneralSettings2::ApplyConfig()
|
|||
else
|
||||
m_input_device->SetSelection(0);
|
||||
|
||||
SendSliderEvent(m_portal_volume, config.portal_volume);
|
||||
if (!config.portal_device.empty() && m_portal_device->HasClientObjectData())
|
||||
{
|
||||
for (uint32 i = 0; i < m_portal_device->GetCount(); ++i)
|
||||
{
|
||||
const auto device_description = (wxDeviceDescription*)m_portal_device->GetClientObject(i);
|
||||
if (device_description && config.portal_device == device_description->GetDescription()->GetIdentifier())
|
||||
{
|
||||
m_portal_device->SetSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
m_portal_device->SetSelection(0);
|
||||
|
||||
// account
|
||||
UpdateOnlineAccounts();
|
||||
m_active_account->SetSelection(0);
|
||||
|
@ -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)
|
||||
|
|
|
@ -63,9 +63,9 @@ private:
|
|||
// Audio
|
||||
wxChoice* m_audio_api;
|
||||
wxSlider *m_audio_latency;
|
||||
wxSlider *m_tv_volume, *m_pad_volume, *m_input_volume;
|
||||
wxSlider *m_tv_volume, *m_pad_volume, *m_input_volume, *m_portal_volume;
|
||||
wxChoice *m_tv_channels, *m_pad_channels, *m_input_channels;
|
||||
wxChoice *m_tv_device, *m_pad_device, *m_input_device;
|
||||
wxChoice *m_tv_device, *m_pad_device, *m_input_device, *m_portal_device;
|
||||
|
||||
// Account
|
||||
wxButton* m_create_account, * m_delete_account;
|
||||
|
|
|
@ -96,6 +96,7 @@ enum
|
|||
MAINFRAME_MENU_ID_OPTIONS_GENERAL2,
|
||||
MAINFRAME_MENU_ID_OPTIONS_AUDIO,
|
||||
MAINFRAME_MENU_ID_OPTIONS_INPUT,
|
||||
MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS,
|
||||
// options -> account
|
||||
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
|
||||
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
|
||||
|
@ -145,6 +146,7 @@ enum
|
|||
MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE,
|
||||
|
||||
// debug->logging
|
||||
MAINFRAME_MENU_ID_DEBUG_LOGGING_MESSAGE = 21499,
|
||||
MAINFRAME_MENU_ID_DEBUG_LOGGING0 = 21500,
|
||||
MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO = 21599,
|
||||
// debug->dump
|
||||
|
@ -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_AUDIO, MainWindow::OnOptionsInput)
|
||||
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput)
|
||||
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS, MainWindow::OnOptionsInput)
|
||||
// tools menu
|
||||
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
|
||||
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
|
||||
|
@ -293,8 +296,13 @@ private:
|
|||
};
|
||||
|
||||
MainWindow::MainWindow()
|
||||
: wxFrame(nullptr, -1, GetInitialWindowTitle(), wxDefaultPosition, wxSize(1280, 720), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER)
|
||||
: wxFrame(nullptr, wxID_ANY, GetInitialWindowTitle(), wxDefaultPosition, wxSize(1280, 720), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER)
|
||||
{
|
||||
#ifdef __WXMAC__
|
||||
// Not necessary to set wxApp::s_macExitMenuItemId as automatically handled
|
||||
wxApp::s_macAboutMenuItemId = MAINFRAME_MENU_ID_HELP_ABOUT;
|
||||
wxApp::s_macPreferencesMenuItemId = MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS;
|
||||
#endif
|
||||
gui_initHandleContextFromWxWidgetsWindow(g_window_info.window_main, this);
|
||||
g_mainFrame = this;
|
||||
CafeSystem::SetImplementation(this);
|
||||
|
@ -918,6 +926,7 @@ void MainWindow::OnOptionsInput(wxCommandEvent& event)
|
|||
break;
|
||||
}
|
||||
|
||||
case MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS:
|
||||
case MAINFRAME_MENU_ID_OPTIONS_GENERAL2:
|
||||
{
|
||||
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");
|
||||
|
||||
sizer->Add(new wxStaticText(parent, wxID_ANY, versionString), wxSizerFlags().Border(wxALL, 3).Border(wxTOP, 10));
|
||||
sizer->Add(new wxHyperlinkCtrl(parent, -1, "https://cemu.info", "https://cemu.info"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 3));
|
||||
sizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://cemu.info", "https://cemu.info"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 3));
|
||||
|
||||
sizer->AddSpacer(3);
|
||||
sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4));
|
||||
|
@ -1898,95 +1907,105 @@ public:
|
|||
// zLib
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "zLib ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.zlib.net", "https://www.zlib.net"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "zLib ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.zlib.net", "https://www.zlib.net"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// wxWidgets
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "wxWidgets ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.wxwidgets.org/", "https://www.wxwidgets.org/"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "wxWidgets ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.wxwidgets.org/", "https://www.wxwidgets.org/"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// OpenSSL
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "OpenSSL ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.openssl.org/", "https://www.openssl.org/"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "OpenSSL ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.openssl.org/", "https://www.openssl.org/"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// libcurl
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "libcurl ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://curl.haxx.se/libcurl/", "https://curl.haxx.se/libcurl/"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "libcurl ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://curl.haxx.se/libcurl/", "https://curl.haxx.se/libcurl/"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// imgui
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "imgui ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/ocornut/imgui", "https://github.com/ocornut/imgui"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "imgui ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/ocornut/imgui", "https://github.com/ocornut/imgui"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// fontawesome
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "fontawesome ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/FortAwesome/Font-Awesome", "https://github.com/FortAwesome/Font-Awesome"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "fontawesome ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/FortAwesome/Font-Awesome", "https://github.com/FortAwesome/Font-Awesome"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// boost
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "boost ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.boost.org", "https://www.boost.org"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "boost ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.boost.org", "https://www.boost.org"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// libusb
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "libusb ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://libusb.info", "https://libusb.info"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "libusb ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://libusb.info", "https://libusb.info"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
#if BOOST_OS_MACOS
|
||||
// MoltenVK
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "MoltenVK ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/KhronosGroup/MoltenVK", "https://github.com/KhronosGroup/MoltenVK"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
#endif
|
||||
// icons
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "icons from "), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://icons8.com", "https://icons8.com"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "icons from "), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://icons8.com", "https://icons8.com"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// Lato font (are we still using it?)
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "\"Lato\" font by tyPoland Lukasz Dziedzic (OFL, V1.1)"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "\"Lato\" font by tyPoland Lukasz Dziedzic (OFL, V1.1)"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// SDL
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "SDL ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/libsdl-org/SDL", "https://github.com/libsdl-org/SDL"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "SDL ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/libsdl-org/SDL", "https://github.com/libsdl-org/SDL"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
// IH264
|
||||
{
|
||||
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, "Modified ih264 from Android project ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "Source", "https://cemu.info/oss/ih264d.zip"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, " "), 0);
|
||||
wxHyperlinkCtrl* noticeLink = new wxHyperlinkCtrl(parent, -1, "NOTICE", "");
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, "Modified ih264 from Android project ("), 0);
|
||||
lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "Source", "https://cemu.info/oss/ih264d.zip"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, " "), 0);
|
||||
wxHyperlinkCtrl* noticeLink = new wxHyperlinkCtrl(parent, wxID_ANY, "NOTICE", "");
|
||||
noticeLink->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& event)
|
||||
{
|
||||
fs::path tempPath = fs::temp_directory_path();
|
||||
|
@ -2020,7 +2039,7 @@ public:
|
|||
wxLaunchDefaultBrowser(wxHelper::FromUtf8(fmt::format("file:{}", _pathToUtf8(tempPath))));
|
||||
});
|
||||
lineSizer->Add(noticeLink, 0);
|
||||
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
|
||||
lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")"), 0);
|
||||
sizer->Add(lineSizer);
|
||||
}
|
||||
}
|
||||
|
@ -2054,7 +2073,7 @@ public:
|
|||
wxString& nameList = ((i % 2) == 0) ? nameListLeft : nameListRight;
|
||||
if (i >= 2)
|
||||
nameList.append("\n");
|
||||
nameList.append(name);
|
||||
nameList.append(wxString::FromUTF8(name));
|
||||
}
|
||||
|
||||
gridSizer->Add(new wxStaticText(parent, wxID_ANY, nameListLeft), wxSizerFlags());
|
||||
|
@ -2184,6 +2203,9 @@ void MainWindow::RecreateMenu()
|
|||
m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString);
|
||||
m_padViewMenuItem->Check(GetConfig().pad_open);
|
||||
optionsMenu->AppendSeparator();
|
||||
#if BOOST_OS_MACOS
|
||||
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS, _("&Settings..." "\tCtrl-,"));
|
||||
#endif
|
||||
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, _("&General settings"));
|
||||
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings"));
|
||||
|
||||
|
@ -2232,7 +2254,7 @@ void MainWindow::RecreateMenu()
|
|||
debugLoggingMenu->AppendSeparator();
|
||||
|
||||
wxMenu* logCosModulesMenu = new wxMenu();
|
||||
logCosModulesMenu->AppendCheckItem(0, _("&Options below are for experts. Leave off if unsure"), wxEmptyString)->Enable(false);
|
||||
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING_MESSAGE, _("&Options below are for experts. Leave off if unsure"), wxEmptyString)->Enable(false);
|
||||
logCosModulesMenu->AppendSeparator();
|
||||
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitFile), _("coreinit File-Access API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitFile));
|
||||
logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThreadSync), _("coreinit Thread-Synchronization API"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThreadSync));
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "gui/helpers/wxHelpers.h"
|
||||
|
@ -79,7 +80,7 @@ MemorySearcherTool::MemorySearcherTool(wxFrame* parent)
|
|||
m_gauge->Enable(false);
|
||||
|
||||
m_textEntryTable = new wxStaticText(this, wxID_ANY, _("Results"));
|
||||
m_listResults = new wxListCtrl(this, LIST_RESULTS, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SORT_ASCENDING);
|
||||
m_listResults = new wxListView(this, LIST_RESULTS, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SORT_ASCENDING);
|
||||
m_listResults->Bind(wxEVT_LEFT_DCLICK, &MemorySearcherTool::OnResultListClick, this);
|
||||
{
|
||||
wxListItem col0;
|
||||
|
@ -388,14 +389,8 @@ void MemorySearcherTool::OnEntryListRightClick(wxDataViewEvent& event)
|
|||
|
||||
void MemorySearcherTool::OnResultListClick(wxMouseEvent& event)
|
||||
{
|
||||
long selectedIndex = -1;
|
||||
|
||||
while (true)
|
||||
for (long selectedIndex = m_listResults->GetFirstSelected(); selectedIndex != wxNOT_FOUND; selectedIndex = m_listResults->GetNextSelected(selectedIndex))
|
||||
{
|
||||
selectedIndex = m_listResults->GetNextItem(selectedIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
if (selectedIndex == -1)
|
||||
break;
|
||||
|
||||
long address = m_listResults->GetItemData(selectedIndex);
|
||||
auto currValue = m_listResults->GetItemText(selectedIndex, 1);
|
||||
|
||||
|
@ -684,7 +679,7 @@ void MemorySearcherTool::OnPopupClick(wxCommandEvent& event)
|
|||
if (event.GetId() == LIST_ENTRY_REMOVE)
|
||||
{
|
||||
const int row = m_listEntryTable->GetSelectedRow();
|
||||
if (row == -1)
|
||||
if (row == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
m_listEntryTable->DeleteItem(row);
|
||||
|
@ -700,7 +695,7 @@ void MemorySearcherTool::OnItemEdited(wxDataViewEvent& event)
|
|||
else if (column == 3)
|
||||
{
|
||||
auto row = m_listEntryTable->GetSelectedRow();
|
||||
if (row == -1)
|
||||
if (row == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
auto addressText = std::string(m_listEntryTable->GetTextValue(row, 1).mbc_str());
|
||||
|
|
|
@ -173,7 +173,7 @@ wxDECLARE_EVENT_TABLE();
|
|||
wxComboBox* m_cbDataType;
|
||||
wxTextCtrl* m_textValue;
|
||||
wxButton *m_buttonStart, *m_buttonFilter;
|
||||
wxListCtrl* m_listResults;
|
||||
wxListView* m_listResults;
|
||||
wxDataViewListCtrl* m_listEntryTable;
|
||||
wxStaticText* m_textEntryTable;
|
||||
wxGauge* m_gauge;
|
||||
|
|
|
@ -356,7 +356,7 @@ void TitleManager::OnTitleSearchComplete(wxCommandEvent& event)
|
|||
|
||||
void TitleManager::OnSetStatusBarText(wxSetStatusBarTextEvent& event)
|
||||
{
|
||||
m_status_bar->SetStatusText(_(event.GetText()), event.GetNumber());
|
||||
m_status_bar->SetStatusText(event.GetText(), event.GetNumber());
|
||||
}
|
||||
|
||||
void TitleManager::OnFilterChanged(wxCommandEvent& event)
|
||||
|
|
|
@ -25,9 +25,8 @@
|
|||
|
||||
wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent);
|
||||
|
||||
|
||||
wxDownloadManagerList::wxDownloadManagerList(wxWindow* parent, wxWindowID id)
|
||||
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
{
|
||||
AddColumns();
|
||||
|
||||
|
@ -48,11 +47,13 @@ wxDownloadManagerList::wxDownloadManagerList(wxWindow* parent, wxWindowID id)
|
|||
Bind(wxEVT_REMOVE_ITEM, &wxDownloadManagerList::OnRemoveItem, this);
|
||||
Bind(wxEVT_REMOVE_ENTRY, &wxDownloadManagerList::OnRemoveEntry, this);
|
||||
Bind(wxEVT_CLOSE_WINDOW, &wxDownloadManagerList::OnClose, this);
|
||||
|
||||
ShowSortIndicator(ColumnName);
|
||||
}
|
||||
|
||||
boost::optional<const wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetSelectedTitleEntry() const
|
||||
{
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto tmp = GetTitleEntry(selection);
|
||||
|
@ -65,7 +66,7 @@ boost::optional<const wxDownloadManagerList::TitleEntry&> wxDownloadManagerList:
|
|||
|
||||
boost::optional<wxDownloadManagerList::TitleEntry&> wxDownloadManagerList::GetSelectedTitleEntry()
|
||||
{
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto tmp = GetTitleEntry(selection);
|
||||
|
@ -218,16 +219,7 @@ void wxDownloadManagerList::OnColumnClick(wxListEvent& event)
|
|||
{
|
||||
const int column = event.GetColumn();
|
||||
|
||||
if (column == m_sort_by_column)
|
||||
{
|
||||
m_sort_less = !m_sort_less;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sort_by_column = column;
|
||||
m_sort_less = true;
|
||||
}
|
||||
SortEntries();
|
||||
SortEntries(column);
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
@ -324,7 +316,7 @@ void wxDownloadManagerList::OnContextMenu(wxContextMenuEvent& event)
|
|||
wxMenu menu;
|
||||
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxDownloadManagerList::OnContextMenuSelected, this);
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
@ -379,8 +371,8 @@ void wxDownloadManagerList::OnContextMenuSelected(wxCommandEvent& event)
|
|||
// still doing work
|
||||
if (m_context_worker.valid() && !future_is_ready(m_context_worker))
|
||||
return;
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
@ -621,24 +613,31 @@ bool wxDownloadManagerList::SortFunc(std::span<int> sortColumnOrder, const Type_
|
|||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
void wxDownloadManagerList::SortEntries()
|
||||
void wxDownloadManagerList::SortEntries(int column)
|
||||
{
|
||||
boost::container::small_vector<int, 12> s_SortColumnOrder{ ColumnName, ColumnType, ColumnVersion, ColumnTitleId, ColumnProgress };
|
||||
|
||||
if (m_sort_by_column != -1)
|
||||
bool ascending;
|
||||
if (column == -1)
|
||||
{
|
||||
// prioritize column by moving it to first position in the column sort order list
|
||||
s_SortColumnOrder.erase(std::remove(s_SortColumnOrder.begin(), s_SortColumnOrder.end(), m_sort_by_column), s_SortColumnOrder.end());
|
||||
s_SortColumnOrder.insert(s_SortColumnOrder.begin(), m_sort_by_column);
|
||||
column = GetSortIndicator();
|
||||
if (column == -1)
|
||||
column = ColumnName;
|
||||
ascending = IsAscendingSortIndicator();
|
||||
}
|
||||
else
|
||||
ascending = GetUpdatedAscendingSortIndicator(column);
|
||||
|
||||
// prioritize column by moving it to first position in the column sort order list
|
||||
s_SortColumnOrder.erase(std::remove(s_SortColumnOrder.begin(), s_SortColumnOrder.end(), column), s_SortColumnOrder.end());
|
||||
s_SortColumnOrder.insert(s_SortColumnOrder.begin(), column);
|
||||
|
||||
std::sort(m_sorted_data.begin(), m_sorted_data.end(),
|
||||
[this, &s_SortColumnOrder](const Type_t& v1, const Type_t& v2) -> bool
|
||||
{
|
||||
const bool result = SortFunc({ s_SortColumnOrder.data(), s_SortColumnOrder.size() }, v1, v2);
|
||||
return m_sort_less ? result : !result;
|
||||
});
|
||||
|
||||
[this, &s_SortColumnOrder, ascending](const Type_t& v1, const Type_t& v2) -> bool {
|
||||
return ascending ? SortFunc(s_SortColumnOrder, v1, v2) : SortFunc(s_SortColumnOrder, v2, v1);
|
||||
});
|
||||
|
||||
ShowSortIndicator(column, ascending);
|
||||
RefreshPage();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class wxDownloadManagerList : public wxListCtrl
|
||||
class wxDownloadManagerList : public wxListView
|
||||
{
|
||||
friend class TitleManager;
|
||||
public:
|
||||
|
@ -49,7 +49,7 @@ public:
|
|||
// error state?
|
||||
};
|
||||
|
||||
void SortEntries();
|
||||
void SortEntries(int column = -1);
|
||||
void RefreshPage();
|
||||
void Filter(const wxString& filter);
|
||||
void Filter2(bool showTitles, bool showUpdates, bool showInstalled);
|
||||
|
@ -138,9 +138,6 @@ private:
|
|||
std::vector<ItemDataPtr> m_data;
|
||||
std::vector<std::reference_wrapper<ItemData>> m_sorted_data;
|
||||
|
||||
int m_sort_by_column = ItemColumn::ColumnName;
|
||||
bool m_sort_less = true;
|
||||
|
||||
bool m_filterShowTitles = true;
|
||||
bool m_filterShowUpdates = true;
|
||||
bool m_filterShowInstalled = true;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <numeric>
|
||||
|
||||
#include <wx/listctrl.h>
|
||||
#include <wx/wupdlock.h>
|
||||
#include <wx/menu.h>
|
||||
#include <wx/mstream.h>
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include <objidl.h>
|
||||
#include <shlguid.h>
|
||||
#include <shlobj.h>
|
||||
#include <wrl/client.h>
|
||||
#endif
|
||||
|
||||
// public events
|
||||
|
@ -85,8 +87,58 @@ std::list<fs::path> _getCachesPaths(const TitleId& titleId)
|
|||
return cachePaths;
|
||||
}
|
||||
|
||||
// Convert PNG to Apple icon image format
|
||||
bool writeICNS(const fs::path& pngPath, const fs::path& icnsPath) {
|
||||
// Read PNG file
|
||||
std::ifstream pngFile(pngPath, std::ios::binary);
|
||||
if (!pngFile)
|
||||
return false;
|
||||
|
||||
// Get PNG size
|
||||
pngFile.seekg(0, std::ios::end);
|
||||
uint32 pngSize = static_cast<uint32>(pngFile.tellg());
|
||||
pngFile.seekg(0, std::ios::beg);
|
||||
|
||||
// Calculate total file size (header + size + type + data)
|
||||
uint32 totalSize = 8 + 8 + pngSize;
|
||||
|
||||
// Create output file
|
||||
std::ofstream icnsFile(icnsPath, std::ios::binary);
|
||||
if (!icnsFile)
|
||||
return false;
|
||||
|
||||
// Write ICNS header
|
||||
icnsFile.put(0x69); // 'i'
|
||||
icnsFile.put(0x63); // 'c'
|
||||
icnsFile.put(0x6e); // 'n'
|
||||
icnsFile.put(0x73); // 's'
|
||||
|
||||
// Write total file size (big endian)
|
||||
icnsFile.put((totalSize >> 24) & 0xFF);
|
||||
icnsFile.put((totalSize >> 16) & 0xFF);
|
||||
icnsFile.put((totalSize >> 8) & 0xFF);
|
||||
icnsFile.put(totalSize & 0xFF);
|
||||
|
||||
// Write icon type (ic07 = 128x128 PNG)
|
||||
icnsFile.put(0x69); // 'i'
|
||||
icnsFile.put(0x63); // 'c'
|
||||
icnsFile.put(0x30); // '0'
|
||||
icnsFile.put(0x37); // '7'
|
||||
|
||||
// Write PNG size (big endian)
|
||||
icnsFile.put((pngSize >> 24) & 0xFF);
|
||||
icnsFile.put((pngSize >> 16) & 0xFF);
|
||||
icnsFile.put((pngSize >> 8) & 0xFF);
|
||||
icnsFile.put(pngSize & 0xFF);
|
||||
|
||||
// Copy PNG data
|
||||
icnsFile << pngFile.rdbuf();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
|
||||
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, GetStyleFlags(Style::kList)), m_style(Style::kList)
|
||||
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, GetStyleFlags(Style::kList)), m_style(Style::kList)
|
||||
{
|
||||
const auto& config = GetConfig();
|
||||
|
||||
|
@ -145,6 +197,8 @@ wxGameList::wxGameList(wxWindow* parent, wxWindowID id)
|
|||
// start async worker (for icon loading)
|
||||
m_async_worker_active = true;
|
||||
m_async_worker_thread = std::thread(&wxGameList::AsyncWorkerThread, this);
|
||||
|
||||
ShowSortIndicator(ColumnName);
|
||||
}
|
||||
|
||||
wxGameList::~wxGameList()
|
||||
|
@ -346,7 +400,7 @@ void wxGameList::SetStyle(Style style, bool save)
|
|||
SetWindowStyleFlag(GetStyleFlags(m_style));
|
||||
|
||||
uint64 selected_title_id = 0;
|
||||
auto selection = GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
selected_title_id = (uint64)GetItemData(selection);
|
||||
|
@ -369,8 +423,8 @@ void wxGameList::SetStyle(Style style, bool save)
|
|||
|
||||
if(selection != wxNOT_FOUND)
|
||||
{
|
||||
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
EnsureVisible(selection);
|
||||
Select(selection);
|
||||
Focus(selection);
|
||||
}
|
||||
|
||||
if(save)
|
||||
|
@ -437,44 +491,71 @@ static inline int order_to_int(const std::weak_ordering &wo)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData)
|
||||
std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData)
|
||||
{
|
||||
const auto isFavoriteA = GetConfig().IsGameListFavorite(titleId1);
|
||||
const auto isFavoriteB = GetConfig().IsGameListFavorite(titleId2);
|
||||
const auto& name1 = GetNameByTitleId(titleId1);
|
||||
const auto& name2 = GetNameByTitleId(titleId2);
|
||||
auto titleLastPlayed = [](uint64_t id)
|
||||
{
|
||||
iosu::pdm::GameListStat playTimeStat{};
|
||||
iosu::pdm::GetStatForGamelist(id, playTimeStat);
|
||||
return playTimeStat;
|
||||
};
|
||||
|
||||
if(sortData->dir > 0)
|
||||
return order_to_int(std::tie(isFavoriteB, name1) <=> std::tie(isFavoriteA, name2));
|
||||
else
|
||||
return order_to_int(std::tie(isFavoriteB, name2) <=> std::tie(isFavoriteA, name1));
|
||||
auto titlePlayMinutes = [](uint64_t id)
|
||||
{
|
||||
iosu::pdm::GameListStat playTimeStat;
|
||||
if (!iosu::pdm::GetStatForGamelist(id, playTimeStat))
|
||||
return 0u;
|
||||
return playTimeStat.numMinutesPlayed;
|
||||
};
|
||||
|
||||
auto titleRegion = [](uint64_t id)
|
||||
{
|
||||
return CafeTitleList::GetGameInfo(id).GetRegion();
|
||||
};
|
||||
|
||||
switch(sortData->column)
|
||||
{
|
||||
default:
|
||||
case ColumnName:
|
||||
{
|
||||
const auto isFavoriteA = GetConfig().IsGameListFavorite(titleId1);
|
||||
const auto isFavoriteB = GetConfig().IsGameListFavorite(titleId2);
|
||||
const auto nameA = GetNameByTitleId(titleId1);
|
||||
const auto nameB = GetNameByTitleId(titleId2);
|
||||
return std::tie(isFavoriteB, nameA) <=> std::tie(isFavoriteA, nameB);
|
||||
}
|
||||
case ColumnGameStarted:
|
||||
return titleLastPlayed(titleId1).last_played <=> titleLastPlayed(titleId2).last_played;
|
||||
case ColumnGameTime:
|
||||
return titlePlayMinutes(titleId1) <=> titlePlayMinutes(titleId2);
|
||||
case ColumnRegion:
|
||||
return titleRegion(titleId1) <=> titleRegion(titleId2);
|
||||
case ColumnTitleID:
|
||||
return titleId1 <=> titleId2;
|
||||
}
|
||||
// unreachable
|
||||
cemu_assert_debug(false);
|
||||
return std::weak_ordering::less;
|
||||
}
|
||||
|
||||
int wxGameList::SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
|
||||
{
|
||||
const auto sort_data = (SortData*)sortData;
|
||||
const int dir = sort_data->dir;
|
||||
|
||||
return sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data);
|
||||
return sort_data->dir * order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data));
|
||||
}
|
||||
|
||||
void wxGameList::SortEntries(int column)
|
||||
{
|
||||
bool ascending;
|
||||
if (column == -1)
|
||||
column = s_last_column;
|
||||
else
|
||||
{
|
||||
if (s_last_column == column)
|
||||
{
|
||||
s_last_column = 0;
|
||||
s_direction = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_last_column = column;
|
||||
s_direction = 1;
|
||||
}
|
||||
column = GetSortIndicator();
|
||||
if (column == -1)
|
||||
column = ColumnName;
|
||||
ascending = IsAscendingSortIndicator();
|
||||
}
|
||||
else
|
||||
ascending = GetUpdatedAscendingSortIndicator(column);
|
||||
|
||||
switch (column)
|
||||
{
|
||||
|
@ -482,9 +563,11 @@ void wxGameList::SortEntries(int column)
|
|||
case ColumnGameTime:
|
||||
case ColumnGameStarted:
|
||||
case ColumnRegion:
|
||||
case ColumnTitleID:
|
||||
{
|
||||
SortData data{ this, column, s_direction };
|
||||
SortData data{this, ItemColumns{column}, ascending ? 1 : -1};
|
||||
SortItems(SortFunction, (wxIntPtr)&data);
|
||||
ShowSortIndicator(column, ascending);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -496,21 +579,20 @@ void wxGameList::OnKeyDown(wxListEvent& event)
|
|||
if (m_style != Style::kList)
|
||||
return;
|
||||
|
||||
const auto keycode = std::tolower(event.m_code);
|
||||
const auto keycode = event.GetKeyCode();
|
||||
if (keycode == WXK_LEFT)
|
||||
{
|
||||
const auto item_count = GetItemCount();
|
||||
if (item_count > 0)
|
||||
{
|
||||
auto selection = (int)GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
auto selection = (int)GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
selection = 0;
|
||||
else
|
||||
selection = std::max(0, selection - GetCountPerPage());
|
||||
|
||||
SetItemState(wxNOT_FOUND, 0, wxLIST_STATE_SELECTED);
|
||||
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
EnsureVisible(selection);
|
||||
Select(selection);
|
||||
Focus(selection);
|
||||
}
|
||||
}
|
||||
else if (keycode == WXK_RIGHT)
|
||||
|
@ -518,15 +600,14 @@ void wxGameList::OnKeyDown(wxListEvent& event)
|
|||
const auto item_count = GetItemCount();
|
||||
if (item_count > 0)
|
||||
{
|
||||
auto selection = (int)GetNextItem(wxNOT_FOUND, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
auto selection = (int)GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
selection = 0;
|
||||
|
||||
selection = std::min(item_count - 1, selection + GetCountPerPage());
|
||||
|
||||
SetItemState(wxNOT_FOUND, 0, wxLIST_STATE_SELECTED);
|
||||
SetItemState(selection, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
|
||||
EnsureVisible(selection);
|
||||
Select(selection);
|
||||
Focus(selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -566,7 +647,7 @@ void wxGameList::OnContextMenu(wxContextMenuEvent& event)
|
|||
wxMenu menu;
|
||||
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxGameList::OnContextMenuSelected, this);
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto title_id = (uint64)GetItemData(selection);
|
||||
|
@ -599,9 +680,7 @@ void wxGameList::OnContextMenu(wxContextMenuEvent& event)
|
|||
menu.Append(kContextMenuEditGameProfile, _("&Edit game profile"));
|
||||
|
||||
menu.AppendSeparator();
|
||||
#if BOOST_OS_LINUX || BOOST_OS_WINDOWS
|
||||
menu.Append(kContextMenuCreateShortcut, _("&Create shortcut"));
|
||||
#endif
|
||||
menu.AppendSeparator();
|
||||
menu.Append(kContextMenuCopyTitleName, _("&Copy Title Name"));
|
||||
menu.Append(kContextMenuCopyTitleId, _("&Copy Title ID"));
|
||||
|
@ -727,9 +806,7 @@ void wxGameList::OnContextMenuSelected(wxCommandEvent& event)
|
|||
}
|
||||
case kContextMenuCreateShortcut:
|
||||
{
|
||||
#if BOOST_OS_LINUX || BOOST_OS_WINDOWS
|
||||
CreateShortcut(gameInfo);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case kContextMenuCopyTitleName:
|
||||
|
@ -1007,7 +1084,7 @@ void wxGameList::OnClose(wxCloseEvent& event)
|
|||
|
||||
int wxGameList::FindInsertPosition(TitleId titleId)
|
||||
{
|
||||
SortData data{ this, s_last_column, s_direction };
|
||||
SortData data{this, ItemColumns(GetSortIndicator()), IsAscendingSortIndicator()};
|
||||
const auto itemCount = GetItemCount();
|
||||
if (itemCount == 0)
|
||||
return 0;
|
||||
|
@ -1375,6 +1452,135 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
|
|||
}
|
||||
outputStream << desktopEntryString;
|
||||
}
|
||||
#elif BOOST_OS_MACOS
|
||||
void wxGameList::CreateShortcut(GameInfo2& gameInfo)
|
||||
{
|
||||
const auto titleId = gameInfo.GetBaseTitleId();
|
||||
const auto titleName = wxString::FromUTF8(gameInfo.GetTitleName());
|
||||
auto exePath = ActiveSettings::GetExecutablePath();
|
||||
|
||||
const wxString appName = wxString::Format("%s.app", titleName);
|
||||
wxFileDialog entryDialog(this, _("Choose shortcut location"), "~/Applications", appName,
|
||||
"Application (*.app)|*.app", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT);
|
||||
const auto result = entryDialog.ShowModal();
|
||||
if (result == wxID_CANCEL)
|
||||
return;
|
||||
const auto output_path = entryDialog.GetPath();
|
||||
// Create .app folder
|
||||
const fs::path appPath = output_path.utf8_string();
|
||||
if (!fs::create_directories(appPath))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to create app directory");
|
||||
return;
|
||||
}
|
||||
const fs::path infoPath = appPath / "Contents/Info.plist";
|
||||
const fs::path scriptPath = appPath / "Contents/MacOS/run.sh";
|
||||
const fs::path icnsPath = appPath / "Contents/Resources/shortcut.icns";
|
||||
if (!(fs::create_directories(scriptPath.parent_path()) && fs::create_directories(icnsPath.parent_path())))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to create app shortcut directories");
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<fs::path> iconPath;
|
||||
// Obtain and convert icon
|
||||
[&]()
|
||||
{
|
||||
int iconIndex, smallIconIndex;
|
||||
|
||||
if (!QueryIconForTitle(titleId, iconIndex, smallIconIndex))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Icon hasn't loaded");
|
||||
return;
|
||||
}
|
||||
const fs::path outIconDir = fs::temp_directory_path();
|
||||
|
||||
if (!fs::exists(outIconDir) && !fs::create_directories(outIconDir))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to create icon directory");
|
||||
return;
|
||||
}
|
||||
|
||||
iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId());
|
||||
wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value()));
|
||||
|
||||
auto image = m_image_list->GetIcon(iconIndex).ConvertToImage();
|
||||
wxPNGHandler pngHandler;
|
||||
if (!pngHandler.SaveFile(&image, pngFileStream, false))
|
||||
{
|
||||
iconPath = std::nullopt;
|
||||
cemuLog_log(LogType::Force, "Icon failed to save");
|
||||
}
|
||||
}();
|
||||
|
||||
std::string runCommand = fmt::format("#!/bin/zsh\n\n{0:?} --title-id {1:016x}", _pathToUtf8(exePath), titleId);
|
||||
const std::string infoPlist = fmt::format(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
|
||||
"<plist version=\"1.0\">\n"
|
||||
"<dict>\n"
|
||||
" <key>CFBundleDisplayName</key>\n"
|
||||
" <string>{0}</string>\n"
|
||||
" <key>CFBundleExecutable</key>\n"
|
||||
" <string>run.sh</string>\n"
|
||||
" <key>CFBundleIconFile</key>\n"
|
||||
" <string>shortcut.icns</string>\n"
|
||||
" <key>CFBundleName</key>\n"
|
||||
" <string>{0}</string>\n"
|
||||
" <key>CFBundlePackageType</key>\n"
|
||||
" <string>APPL</string>\n"
|
||||
" <key>CFBundleSignature</key>\n"
|
||||
" <string>\?\?\?\?</string>\n"
|
||||
" <key>LSApplicationCategoryType</key>\n"
|
||||
" <string>public.app-category.games</string>\n"
|
||||
" <key>CFBundleShortVersionString</key>\n"
|
||||
" <string>{1}</string>\n"
|
||||
" <key>CFBundleVersion</key>\n"
|
||||
" <string>{1}</string>\n"
|
||||
"</dict>\n"
|
||||
"</plist>\n",
|
||||
gameInfo.GetTitleName(),
|
||||
std::to_string(gameInfo.GetVersion())
|
||||
);
|
||||
// write Info.plist to infoPath
|
||||
std::ofstream infoStream(infoPath);
|
||||
std::ofstream scriptStream(scriptPath);
|
||||
if (!infoStream.good() || !scriptStream.good())
|
||||
{
|
||||
auto errorMsg = formatWxString(_("Failed to save app shortcut to {}"), output_path.utf8_string());
|
||||
wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
|
||||
return;
|
||||
}
|
||||
infoStream << infoPlist;
|
||||
scriptStream << runCommand;
|
||||
scriptStream.close();
|
||||
|
||||
// Set execute permissions for script
|
||||
fs::permissions(
|
||||
scriptPath,
|
||||
fs::perms::owner_exec | fs::perms::group_exec | fs::perms::others_exec,
|
||||
fs::perm_options::add
|
||||
);
|
||||
|
||||
// Return if iconPath is empty
|
||||
if (!iconPath)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Icon not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert icon to icns, only works for 128x128 PNG
|
||||
// Alternatively, can run the command "sips -s format icns {iconPath} --out '{icnsPath}'"
|
||||
// using std::system() to handle images of any size
|
||||
if (!writeICNS(*iconPath, icnsPath))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to convert icon to icns");
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove temp file
|
||||
fs::remove(*iconPath);
|
||||
}
|
||||
#elif BOOST_OS_WINDOWS
|
||||
void wxGameList::CreateShortcut(GameInfo2& gameInfo)
|
||||
{
|
||||
|
@ -1428,8 +1634,8 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
|
|||
}
|
||||
}
|
||||
|
||||
IShellLinkW* shellLink;
|
||||
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast<LPVOID*>(&shellLink));
|
||||
Microsoft::WRL::ComPtr<IShellLinkW> shellLink;
|
||||
HRESULT hres = CoCreateInstance(__uuidof(ShellLink), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
const auto description = wxString::Format("Play %s on Cemu", titleName);
|
||||
|
@ -1445,15 +1651,13 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo)
|
|||
else
|
||||
shellLink->SetIconLocation(exePath.wstring().c_str(), 0);
|
||||
|
||||
IPersistFile* shellLinkFile;
|
||||
Microsoft::WRL::ComPtr<IPersistFile> shellLinkFile;
|
||||
// save the shortcut
|
||||
hres = shellLink->QueryInterface(IID_IPersistFile, reinterpret_cast<LPVOID*>(&shellLinkFile));
|
||||
hres = shellLink.As(&shellLinkFile);
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
hres = shellLinkFile->Save(outputPath.wc_str(), TRUE);
|
||||
shellLinkFile->Release();
|
||||
}
|
||||
shellLink->Release();
|
||||
}
|
||||
if (!SUCCEEDED(hres)) {
|
||||
auto errorMsg = formatWxString(_("Failed to save shortcut to {}"), outputPath);
|
||||
|
|
|
@ -30,7 +30,7 @@ wxDECLARE_EVENT(wxEVT_OPEN_GRAPHIC_PACK, wxTitleIdEvent);
|
|||
wxDECLARE_EVENT(wxEVT_GAMELIST_BEGIN_UPDATE, wxCommandEvent);
|
||||
wxDECLARE_EVENT(wxEVT_GAMELIST_END_UPDATE, wxCommandEvent);
|
||||
|
||||
class wxGameList : public wxListCtrl
|
||||
class wxGameList : public wxListView
|
||||
{
|
||||
friend class MainWindow;
|
||||
public:
|
||||
|
@ -53,9 +53,7 @@ public:
|
|||
void ReloadGameEntries(bool cached = false);
|
||||
void DeleteCachedStrings();
|
||||
|
||||
#if BOOST_OS_LINUX || BOOST_OS_WINDOWS
|
||||
void CreateShortcut(GameInfo2& gameInfo);
|
||||
#endif
|
||||
|
||||
long FindListItemByTitleId(uint64 title_id) const;
|
||||
void OnClose(wxCloseEvent& event);
|
||||
|
@ -70,7 +68,7 @@ private:
|
|||
inline static const wxColour kSecondColor{ 0xFDF9F2 };
|
||||
void UpdateItemColors(sint32 startIndex = 0);
|
||||
|
||||
enum ItemColumns
|
||||
enum ItemColumns : int
|
||||
{
|
||||
ColumnHiddenName = 0,
|
||||
ColumnIcon,
|
||||
|
@ -85,18 +83,16 @@ private:
|
|||
ColumnCounts,
|
||||
};
|
||||
|
||||
int s_last_column = ColumnName;
|
||||
int s_direction = 1;
|
||||
void SortEntries(int column = -1);
|
||||
struct SortData
|
||||
{
|
||||
wxGameList* thisptr;
|
||||
int column;
|
||||
ItemColumns column;
|
||||
int dir;
|
||||
};
|
||||
|
||||
int FindInsertPosition(TitleId titleId);
|
||||
int SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData);
|
||||
std::weak_ordering SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData);
|
||||
static int SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData);
|
||||
|
||||
wxTimer* m_tooltip_timer;
|
||||
|
|
|
@ -38,7 +38,7 @@ wxDEFINE_EVENT(wxEVT_TITLE_REMOVED, wxCommandEvent);
|
|||
wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent);
|
||||
|
||||
wxTitleManagerList::wxTitleManagerList(wxWindow* parent, wxWindowID id)
|
||||
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
: wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
{
|
||||
AddColumns();
|
||||
|
||||
|
@ -64,6 +64,8 @@ wxTitleManagerList::wxTitleManagerList(wxWindow* parent, wxWindowID id)
|
|||
|
||||
m_callbackIdTitleList = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((wxTitleManagerList*)ctx)->HandleTitleListCallback(evt); }, this);
|
||||
m_callbackIdSaveList = CafeSaveList::RegisterCallback([](CafeSaveListCallbackEvent* evt, void* ctx) { ((wxTitleManagerList*)ctx)->HandleSaveListCallback(evt); }, this);
|
||||
|
||||
ShowSortIndicator(ColumnTitleId);
|
||||
}
|
||||
|
||||
wxTitleManagerList::~wxTitleManagerList()
|
||||
|
@ -74,7 +76,7 @@ wxTitleManagerList::~wxTitleManagerList()
|
|||
|
||||
boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry() const
|
||||
{
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto tmp = GetTitleEntry(selection);
|
||||
|
@ -87,7 +89,7 @@ boost::optional<const wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSe
|
|||
|
||||
boost::optional<wxTitleManagerList::TitleEntry&> wxTitleManagerList::GetSelectedTitleEntry()
|
||||
{
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto tmp = GetTitleEntry(selection);
|
||||
|
@ -574,7 +576,7 @@ void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId, uint64 righ
|
|||
}
|
||||
else
|
||||
{
|
||||
progressDialog.Update(0, _("Collecting list of files..." + fmt::format(" ({})", writerContext.totalFileCount.load())));
|
||||
progressDialog.Update(0, _("Collecting list of files...") + fmt::format(" ({})", writerContext.totalFileCount.load()));
|
||||
}
|
||||
if (progressDialog.WasCancelled())
|
||||
writerContext.cancelled.store(true);
|
||||
|
@ -757,7 +759,7 @@ void wxTitleManagerList::OnContextMenu(wxContextMenuEvent& event)
|
|||
wxMenu menu;
|
||||
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxTitleManagerList::OnContextMenuSelected, this);
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
@ -855,8 +857,8 @@ void wxTitleManagerList::OnContextMenuSelected(wxCommandEvent& event)
|
|||
// still doing work
|
||||
if (m_context_worker.valid() && !future_is_ready(m_context_worker))
|
||||
return;
|
||||
|
||||
const auto selection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
|
||||
const auto selection = GetFirstSelected();
|
||||
if (selection == wxNOT_FOUND)
|
||||
return;
|
||||
|
||||
|
@ -1173,54 +1175,48 @@ bool wxTitleManagerList::SortFunc(int column, const Type_t& v1, const Type_t& v2
|
|||
{
|
||||
if(entry1.version == entry2.version)
|
||||
return SortFunc(ColumnTitleId, v1, v2);
|
||||
|
||||
return std::underlying_type_t<EntryType>(entry1.version) < std::underlying_type_t<EntryType>(entry2.version);
|
||||
|
||||
return entry1.version < entry2.version;
|
||||
}
|
||||
else if (column == ColumnRegion)
|
||||
{
|
||||
if(entry1.region == entry2.region)
|
||||
return SortFunc(ColumnTitleId, v1, v2);
|
||||
|
||||
return std::underlying_type_t<EntryType>(entry1.region) < std::underlying_type_t<EntryType>(entry2.region);
|
||||
|
||||
return std::underlying_type_t<CafeConsoleRegion>(entry1.region) < std::underlying_type_t<CafeConsoleRegion>(entry2.region);
|
||||
}
|
||||
else if (column == ColumnFormat)
|
||||
{
|
||||
if(entry1.format == entry2.format)
|
||||
return SortFunc(ColumnType, v1, v2);
|
||||
|
||||
return std::underlying_type_t<EntryType>(entry1.format) < std::underlying_type_t<EntryType>(entry2.format);
|
||||
return std::underlying_type_t<EntryFormat>(entry1.format) < std::underlying_type_t<EntryFormat>(entry2.format);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void wxTitleManagerList::SortEntries(int column)
|
||||
{
|
||||
if(column == -1)
|
||||
bool ascending;
|
||||
if (column == -1)
|
||||
{
|
||||
column = m_last_column_sorted;
|
||||
m_last_column_sorted = -1;
|
||||
column = GetSortIndicator();
|
||||
if (column == -1)
|
||||
column = ColumnTitleId;
|
||||
ascending = IsAscendingSortIndicator();
|
||||
}
|
||||
|
||||
else
|
||||
ascending = GetUpdatedAscendingSortIndicator(column);
|
||||
|
||||
if (column != ColumnTitleId && column != ColumnName && column != ColumnType && column != ColumnVersion && column != ColumnRegion && column != ColumnFormat)
|
||||
return;
|
||||
|
||||
if (m_last_column_sorted != column)
|
||||
{
|
||||
m_last_column_sorted = column;
|
||||
m_sort_less = true;
|
||||
}
|
||||
else
|
||||
m_sort_less = !m_sort_less;
|
||||
|
||||
std::sort(m_sorted_data.begin(), m_sorted_data.end(),
|
||||
[this, column](const Type_t& v1, const Type_t& v2) -> bool
|
||||
{
|
||||
const bool result = SortFunc(column, v1, v2);
|
||||
return m_sort_less ? result : !result;
|
||||
});
|
||||
|
||||
[this, column, ascending](const Type_t& v1, const Type_t& v2) -> bool {
|
||||
return ascending ? SortFunc(column, v1, v2) : SortFunc(column, v2, v1);
|
||||
});
|
||||
|
||||
ShowSortIndicator(column, ascending);
|
||||
RefreshPage();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class wxTitleManagerList : public wxListCtrl
|
||||
class wxTitleManagerList : public wxListView
|
||||
{
|
||||
friend class TitleManager;
|
||||
public:
|
||||
|
@ -127,8 +127,6 @@ private:
|
|||
std::vector<ItemDataPtr> m_data;
|
||||
std::vector<std::reference_wrapper<ItemData>> m_sorted_data;
|
||||
|
||||
int m_last_column_sorted = -1;
|
||||
bool m_sort_less = true;
|
||||
using Type_t = std::reference_wrapper<const ItemData>;
|
||||
bool SortFunc(int column, const Type_t& v1, const Type_t& v2);
|
||||
|
||||
|
|
|
@ -230,8 +230,8 @@ void BreakpointWindow::OnRightDown(wxMouseEvent& event)
|
|||
}
|
||||
else
|
||||
{
|
||||
m_breakpoints->SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
|
||||
m_breakpoints->SetItemState(index, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
||||
m_breakpoints->Focus(index);
|
||||
m_breakpoints->Select(index);
|
||||
|
||||
wxMenu menu;
|
||||
menu.Append(MENU_ID_DELETE_BP, _("Delete breakpoint"));
|
||||
|
@ -245,8 +245,8 @@ void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt)
|
|||
{
|
||||
if (evt.GetId() == MENU_ID_DELETE_BP)
|
||||
{
|
||||
long sel = m_breakpoints->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
if (sel != -1)
|
||||
long sel = m_breakpoints->GetFirstSelected();
|
||||
if (sel != wxNOT_FOUND)
|
||||
{
|
||||
if (sel >= debuggerState.breakpoints.size())
|
||||
return;
|
||||
|
|
|
@ -615,7 +615,7 @@ void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
|
|||
{
|
||||
// double-clicked on disassembly (operation and operand data)
|
||||
wxString currentInstruction = wxEmptyString;
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new instruction."), _(wxString::Format("Overwrite instruction at address %08x", virtualAddress)), currentInstruction);
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new instruction."), wxString::Format(_("Overwrite instruction at address %08x"), virtualAddress), currentInstruction);
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
PPCAssemblerInOut ctx = { 0 };
|
||||
|
@ -637,7 +637,7 @@ void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
|
|||
if (comment && comment->type == RplDebugSymbolComment)
|
||||
old_comment = comment->comment;
|
||||
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), _(wxString::Format("Create comment at address %08x", virtualAddress)), old_comment);
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), wxString::Format(_("Create comment at address %08x"), virtualAddress), old_comment);
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
rplDebugSymbol_createComment(virtualAddress, set_value_dialog.GetValue().wc_str());
|
||||
|
@ -850,4 +850,4 @@ void DisasmCtrl::GoToAddressDialog()
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ void DumpCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
|
|||
const uint32 offset = LineToOffset(line) + byte_index;
|
||||
const uint8 value = memory_readU8(offset);
|
||||
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set byte at address %08x", offset)), wxString::Format("%02x", value));
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set byte at address %08x"), offset), wxString::Format("%02x", value));
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
const uint8 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
|
||||
|
@ -303,4 +303,4 @@ void DumpCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position)
|
|||
wxSize DumpCtrl::DoGetBestSize() const
|
||||
{
|
||||
return TextList::DoGetBestSize();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ void ModuleWindow::OnGameLoaded()
|
|||
void ModuleWindow::OnLeftDClick(wxMouseEvent& event)
|
||||
{
|
||||
long selected = m_modules->GetFirstSelected();
|
||||
if (selected == -1)
|
||||
if (selected == wxNOT_FOUND)
|
||||
return;
|
||||
const auto text = m_modules->GetItemText(selected, ColumnAddress);
|
||||
const auto address = std::stoul(text.ToStdString(), nullptr, 16);
|
||||
|
|
|
@ -201,7 +201,7 @@ void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
|
|||
if (position.x <= OFFSET_REGISTER + OFFSET_REGISTER_LABEL)
|
||||
{
|
||||
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index];
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set R%d value", register_index)), wxString::Format("%08x", register_value));
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set R%d value"), register_index), wxString::Format("%08x", register_value));
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
|
||||
|
@ -220,7 +220,7 @@ void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
|
|||
if (position.x <= OFFSET_REGISTER + OFFSET_FPR)
|
||||
{
|
||||
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0;
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP0_%d value", register_index)), wxString::Format("%lf", register_value));
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP0_%d value"), register_index), wxString::Format("%lf", register_value));
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
|
||||
|
@ -234,7 +234,7 @@ void RegisterCtrl::OnMouseDClick(const wxPoint& position, uint32 line)
|
|||
else
|
||||
{
|
||||
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1;
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP1_%d value", register_index)), wxString::Format("%lf", register_value));
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP1_%d value"), register_index), wxString::Format("%lf", register_value));
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
|
||||
|
|
|
@ -339,7 +339,7 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event)
|
|||
{
|
||||
const uint32 register_index = id - kRegisterValueR0;
|
||||
const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index];
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set R%d value", register_index)), wxString::Format("%08x", register_value));
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set R%d value"), register_index), wxString::Format("%08x", register_value));
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16);
|
||||
|
@ -355,7 +355,7 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event)
|
|||
{
|
||||
const uint32 register_index = id - kRegisterValueFPR0_0;
|
||||
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0;
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP0_%d value", register_index)), wxString::Format("%lf", register_value));
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP0_%d value"), register_index), wxString::Format("%lf", register_value));
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
|
||||
|
@ -371,7 +371,7 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event)
|
|||
{
|
||||
const uint32 register_index = id - kRegisterValueFPR1_0;
|
||||
const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1;
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), _(wxString::Format("Set FP1_%d value", register_index)), wxString::Format("%lf", register_value));
|
||||
wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP1_%d value"), register_index), wxString::Format("%lf", register_value));
|
||||
if (set_value_dialog.ShowModal() == wxID_OK)
|
||||
{
|
||||
const double new_value = std::stod(set_value_dialog.GetValue().ToStdString());
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "gui/guiWrapper.h"
|
||||
#include "Cafe/OS/RPL/rpl_symbol_storage.h"
|
||||
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
enum ItemColumns
|
||||
{
|
||||
|
@ -10,8 +11,7 @@ enum ItemColumns
|
|||
ColumnModule,
|
||||
};
|
||||
|
||||
SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size) :
|
||||
wxListCtrl(parent, id, pos, size, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size) : wxListView(parent, id, pos, size, wxLC_REPORT | wxLC_VIRTUAL)
|
||||
{
|
||||
wxListItem col0;
|
||||
col0.SetId(ColumnName);
|
||||
|
@ -106,8 +106,8 @@ wxString SymbolListCtrl::OnGetItemText(long item, long column) const
|
|||
|
||||
void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
|
||||
{
|
||||
long selected = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
if (selected == -1)
|
||||
long selected = GetFirstSelected();
|
||||
if (selected == wxNOT_FOUND)
|
||||
return;
|
||||
const auto text = GetItemText(selected, ColumnAddress);
|
||||
const auto address = std::stoul(text.ToStdString(), nullptr, 16);
|
||||
|
@ -119,8 +119,8 @@ void SymbolListCtrl::OnLeftDClick(wxListEvent& event)
|
|||
|
||||
void SymbolListCtrl::OnRightClick(wxListEvent& event)
|
||||
{
|
||||
long selected = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
if (selected == -1)
|
||||
long selected = GetFirstSelected();
|
||||
if (selected == wxNOT_FOUND)
|
||||
return;
|
||||
auto text = GetItemText(selected, ColumnAddress);
|
||||
text = "0x" + text;
|
||||
|
@ -162,4 +162,4 @@ void SymbolListCtrl::ChangeListFilter(std::string filter)
|
|||
SetItemCount(visible_entries);
|
||||
if (visible_entries > 0)
|
||||
RefreshItems(GetTopItem(), std::min<long>(visible_entries - 1, GetTopItem() + GetCountPerPage() + 1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
class SymbolListCtrl : public wxListCtrl
|
||||
class SymbolListCtrl : public wxListView
|
||||
{
|
||||
public:
|
||||
SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size);
|
||||
|
|
|
@ -12,7 +12,7 @@ public:
|
|||
: wxCommandEvent(EVT_LOG), m_filter(filter), m_message(message) { }
|
||||
|
||||
wxLogEvent(const wxLogEvent& event)
|
||||
: wxCommandEvent(event), m_filter(event.m_filter), m_message(event.m_message) { }
|
||||
: wxCommandEvent(event), m_filter(event.GetFilter()), m_message(event.GetMessage()) { }
|
||||
|
||||
wxEvent* Clone() const { return new wxLogEvent(*this); }
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ using wxControllerData = wxCustomData<ControllerPtr>;
|
|||
|
||||
InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position,
|
||||
const std::vector<ControllerPtr>& controllers)
|
||||
: wxDialog(parent, wxID_ANY, "Add input API", position, wxDefaultSize, 0), m_controllers(controllers)
|
||||
: wxDialog(parent, wxID_ANY, "Add input API", position, wxDefaultSize, wxCAPTION), m_controllers(controllers)
|
||||
{
|
||||
this->SetSizeHints(wxDefaultSize, wxDefaultSize);
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ void PairingDialog::WorkerThread()
|
|||
BluetoothFindDeviceClose(deviceFind);
|
||||
}
|
||||
}
|
||||
#elif BOOST_OS_LINUX
|
||||
#elif defined(HAS_BLUEZ)
|
||||
void PairingDialog::WorkerThread()
|
||||
{
|
||||
constexpr static uint8_t LIAC_LAP[] = {0x00, 0x8b, 0x9e};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <cinttypes>
|
||||
#include <helpers/wxHelpers.h>
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -42,7 +43,7 @@ DebugPPCThreadsWindow::DebugPPCThreadsWindow(wxFrame& parent)
|
|||
wxFrame::SetBackgroundColour(*wxWHITE);
|
||||
|
||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_thread_list = new wxListCtrl(this, GPLIST_ID, wxPoint(0, 0), wxSize(930, 240), wxLC_REPORT);
|
||||
m_thread_list = new wxListView(this, GPLIST_ID, wxPoint(0, 0), wxSize(930, 240), wxLC_REPORT);
|
||||
|
||||
m_thread_list->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Courier New")); //wxSystemSettings::GetFont(wxSYS_OEM_FIXED_FONT));
|
||||
|
||||
|
@ -169,7 +170,7 @@ void DebugPPCThreadsWindow::RefreshThreadList()
|
|||
wxWindowUpdateLocker lock(m_thread_list);
|
||||
|
||||
long selected_thread = 0;
|
||||
const int selection = m_thread_list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
||||
const int selection = m_thread_list->GetFirstSelected();
|
||||
if (selection != wxNOT_FOUND)
|
||||
selected_thread = m_thread_list->GetItemData(selection);
|
||||
|
||||
|
@ -267,12 +268,15 @@ void DebugPPCThreadsWindow::RefreshThreadList()
|
|||
|
||||
m_thread_list->SetItem(i, 12, tempStr);
|
||||
|
||||
if(selected_thread != 0 && selected_thread == (long)threadItrMPTR)
|
||||
m_thread_list->SetItemState(i, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED, wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
|
||||
}
|
||||
srwlock_activeThreadList.UnlockWrite();
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
if (selected_thread != 0 && selected_thread == (long)threadItrMPTR)
|
||||
{
|
||||
m_thread_list->Select(i);
|
||||
m_thread_list->Focus(i);
|
||||
}
|
||||
}
|
||||
srwlock_activeThreadList.UnlockWrite();
|
||||
__OSUnlockScheduler();
|
||||
}
|
||||
|
||||
m_thread_list->SetScrollPos(0, scrollPos, true);
|
||||
}
|
||||
|
@ -436,12 +440,11 @@ void DebugPPCThreadsWindow::OnThreadListRightClick(wxMouseEvent& event)
|
|||
if (itemIndex == wxNOT_FOUND)
|
||||
return;
|
||||
// select item
|
||||
m_thread_list->SetItemState(itemIndex, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
|
||||
long sel = m_thread_list->GetNextItem(-1, wxLIST_NEXT_ALL,
|
||||
wxLIST_STATE_SELECTED);
|
||||
if (sel != -1)
|
||||
m_thread_list->SetItemState(sel, 0, wxLIST_STATE_SELECTED);
|
||||
m_thread_list->SetItemState(itemIndex, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
||||
m_thread_list->Focus(itemIndex);
|
||||
long sel = m_thread_list->GetFirstSelected();
|
||||
if (sel != wxNOT_FOUND)
|
||||
m_thread_list->Select(sel, false);
|
||||
m_thread_list->Select(itemIndex);
|
||||
// check if thread is still on the list of active threads
|
||||
MPTR threadMPTR = (MPTR)m_thread_list->GetItemData(itemIndex);
|
||||
__OSLockScheduler();
|
||||
|
|
|
@ -23,7 +23,7 @@ private:
|
|||
void PresentProfileResults(OSThread_t* thread, const std::unordered_map<VAddr, uint32>& samples);
|
||||
void DumpStackTrace(struct OSThread_t* thread);
|
||||
|
||||
wxListCtrl* m_thread_list;
|
||||
wxListView* m_thread_list;
|
||||
wxCheckBox* m_auto_refresh;
|
||||
wxTimer* m_timer;
|
||||
|
||||
|
@ -32,4 +32,4 @@ private:
|
|||
wxDECLARE_EVENT_TABLE();
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
|
|
|
@ -72,7 +72,7 @@ DECLARE_EXPORTED_EVENT_TYPE(WXEXPORT, wxEVT_COMMAND_LIST_ITEM_UNCHECKED, -1);
|
|||
|
||||
//! This is the class which performs all transactions with the server.
|
||||
//! It uses the wxSocket facilities.
|
||||
class wxCheckedListCtrl : public wxListCtrl
|
||||
class wxCheckedListCtrl : public wxListView
|
||||
{
|
||||
protected:
|
||||
|
||||
|
@ -85,18 +85,18 @@ protected:
|
|||
|
||||
public:
|
||||
wxCheckedListCtrl()
|
||||
: wxListCtrl(), m_imageList(16, 16, TRUE) {}
|
||||
: wxListView(), m_imageList(16, 16, TRUE) {}
|
||||
|
||||
wxCheckedListCtrl(wxWindow *parent, wxWindowID id = -1,
|
||||
wxCheckedListCtrl(wxWindow *parent, wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pt = wxDefaultPosition,
|
||||
const wxSize& sz = wxDefaultSize,
|
||||
long style = wxCLC_CHECK_WHEN_SELECTING,
|
||||
const wxValidator& validator = wxDefaultValidator,
|
||||
const wxString& name = wxListCtrlNameStr)
|
||||
: wxListCtrl(), m_imageList(16, 16, TRUE)
|
||||
: wxListView(), m_imageList(16, 16, TRUE)
|
||||
{ Create(parent, id, pt, sz, style, validator, name); }
|
||||
|
||||
bool Create(wxWindow *parent, wxWindowID id = -1,
|
||||
bool Create(wxWindow *parent, wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pt = wxDefaultPosition,
|
||||
const wxSize& sz = wxDefaultSize,
|
||||
long style = wxCLC_CHECK_WHEN_SELECTING,
|
||||
|
|
|
@ -61,7 +61,7 @@ if(WIN32)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WIIMOTE)
|
||||
if (SUPPORTS_WIIMOTE)
|
||||
target_compile_definitions(CemuInput PUBLIC SUPPORTS_WIIMOTE)
|
||||
target_sources(CemuInput PRIVATE
|
||||
api/Wiimote/WiimoteControllerProvider.h
|
||||
|
@ -70,13 +70,17 @@ if (ENABLE_WIIMOTE)
|
|||
api/Wiimote/NativeWiimoteController.h
|
||||
api/Wiimote/NativeWiimoteController.cpp
|
||||
api/Wiimote/WiimoteDevice.h
|
||||
api/Wiimote/hidapi/HidapiWiimote.cpp
|
||||
api/Wiimote/hidapi/HidapiWiimote.h
|
||||
)
|
||||
if (UNIX AND NOT APPLE)
|
||||
if (ENABLE_HIDAPI)
|
||||
target_sources(CemuInput PRIVATE
|
||||
api/Wiimote/l2cap/L2CapWiimote.cpp
|
||||
api/Wiimote/l2cap/L2CapWiimote.h)
|
||||
api/Wiimote/hidapi/HidapiWiimote.cpp
|
||||
api/Wiimote/hidapi/HidapiWiimote.h)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_BLUEZ)
|
||||
target_sources(CemuInput PRIVATE
|
||||
api/Wiimote/l2cap/L2CapWiimote.cpp
|
||||
api/Wiimote/l2cap/L2CapWiimote.h)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
|
@ -95,6 +99,7 @@ target_link_libraries(CemuInput PRIVATE
|
|||
pugixml::pugixml
|
||||
SDL2::SDL2
|
||||
)
|
||||
|
||||
if (ENABLE_HIDAPI)
|
||||
target_link_libraries(CemuInput PRIVATE hidapi::hidapi)
|
||||
endif()
|
||||
|
@ -103,7 +108,6 @@ if (ENABLE_WXWIDGETS)
|
|||
target_link_libraries(CemuInput PRIVATE wx::base wx::core)
|
||||
endif()
|
||||
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
if (ENABLE_BLUEZ)
|
||||
target_link_libraries(CemuInput PRIVATE bluez::bluez)
|
||||
endif ()
|
|
@ -15,9 +15,6 @@ DirectInputController::DirectInputController(const GUID& guid, std::string_view
|
|||
|
||||
DirectInputController::~DirectInputController()
|
||||
{
|
||||
if (m_effect)
|
||||
m_effect->Release();
|
||||
|
||||
if (m_device)
|
||||
{
|
||||
m_device->Unacquire();
|
||||
|
@ -39,8 +36,8 @@ DirectInputController::~DirectInputController()
|
|||
if (kGameCubeController == m_product_guid)
|
||||
should_release_device = false;
|
||||
|
||||
if (should_release_device)
|
||||
m_device->Release();
|
||||
if (!should_release_device)
|
||||
m_device.Detach();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +101,6 @@ bool DirectInputController::connect()
|
|||
// set data format
|
||||
if (FAILED(m_device->SetDataFormat(m_provider->get_data_format())))
|
||||
{
|
||||
SAFE_RELEASE(m_device);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -115,7 +111,6 @@ bool DirectInputController::connect()
|
|||
{
|
||||
if (FAILED(m_device->SetCooperativeLevel(hwndMainWindow, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
|
||||
{
|
||||
SAFE_RELEASE(m_device);
|
||||
return false;
|
||||
}
|
||||
// rumble can only be used with exclusive access
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "input/api/DirectInput/DirectInputControllerProvider.h"
|
||||
#include "input/api/Controller.h"
|
||||
#include <wrl/client.h>
|
||||
|
||||
class DirectInputController : public Controller<DirectInputControllerProvider>
|
||||
{
|
||||
|
@ -41,9 +42,9 @@ private:
|
|||
GUID m_product_guid{};
|
||||
|
||||
std::shared_mutex m_mutex;
|
||||
LPDIRECTINPUTDEVICE8 m_device = nullptr;
|
||||
LPDIRECTINPUTEFFECT m_effect = nullptr;
|
||||
Microsoft::WRL::ComPtr<IDirectInputDevice8> m_device;
|
||||
Microsoft::WRL::ComPtr<IDirectInputEffect> m_effect;
|
||||
|
||||
std::array<LONG, 6> m_min_axis{};
|
||||
std::array<LONG, 6> m_max_axis{};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ DirectInputControllerProvider::DirectInputControllerProvider()
|
|||
|
||||
|
||||
const auto r = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_dinput8, nullptr);
|
||||
if (FAILED(r) || !m_dinput8)
|
||||
if (FAILED(r))
|
||||
{
|
||||
const auto error = GetLastError();
|
||||
//FreeLibrary(m_module);
|
||||
|
@ -29,9 +29,6 @@ DirectInputControllerProvider::DirectInputControllerProvider()
|
|||
|
||||
DirectInputControllerProvider::~DirectInputControllerProvider()
|
||||
{
|
||||
if (m_dinput8)
|
||||
m_dinput8->Release();
|
||||
|
||||
/*if (m_module)
|
||||
FreeLibrary(m_module);
|
||||
*/
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#define DIRECTINPUT_VERSION 0x0800
|
||||
|
||||
#include <dinput.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "input/api/ControllerProvider.h"
|
||||
|
||||
|
@ -22,7 +23,7 @@ public:
|
|||
|
||||
std::vector<std::shared_ptr<ControllerBase>> get_controllers() override;
|
||||
|
||||
IDirectInput8* get_dinput() const { return m_dinput8; }
|
||||
IDirectInput8* get_dinput() const { return m_dinput8.Get(); }
|
||||
LPCDIDATAFORMAT get_data_format() const;
|
||||
|
||||
private:
|
||||
|
@ -31,7 +32,7 @@ private:
|
|||
decltype(&DirectInput8Create) m_DirectInput8Create;
|
||||
decltype(&GetdfDIJoystick) m_GetdfDIJoystick = nullptr;
|
||||
|
||||
IDirectInput8* m_dinput8 = nullptr;
|
||||
Microsoft::WRL::ComPtr<IDirectInput8> m_dinput8;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <dxgi1_4.h>
|
||||
//#include <atlbase.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
class DXGIWrapper
|
||||
{
|
||||
|
@ -23,34 +23,28 @@ public:
|
|||
throw std::runtime_error("can't find CreateDXGIFactory1 in dxgi module");
|
||||
}
|
||||
|
||||
IDXGIFactory1* dxgiFactory = nullptr;
|
||||
Microsoft::WRL::ComPtr<IDXGIFactory1> dxgiFactory;
|
||||
pCreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory));
|
||||
|
||||
IDXGIAdapter1* tmpDxgiAdapter = nullptr;
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> dxgiAdapter;
|
||||
UINT adapterIndex = 0;
|
||||
while (dxgiFactory->EnumAdapters1(adapterIndex, &tmpDxgiAdapter) != DXGI_ERROR_NOT_FOUND)
|
||||
while (dxgiFactory->EnumAdapters1(adapterIndex, &dxgiAdapter) != DXGI_ERROR_NOT_FOUND)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc;
|
||||
tmpDxgiAdapter->GetDesc1(&desc);
|
||||
dxgiAdapter->GetDesc1(&desc);
|
||||
|
||||
if (deviceLUID == nullptr || memcmp(&desc.AdapterLuid, deviceLUID, sizeof(LUID)) == 0)
|
||||
{
|
||||
tmpDxgiAdapter->QueryInterface(IID_PPV_ARGS(&m_dxgiAdapter));
|
||||
tmpDxgiAdapter->Release();
|
||||
if (FAILED(dxgiAdapter.As(&m_dxgiAdapter)))
|
||||
{
|
||||
Cleanup();
|
||||
throw std::runtime_error("can't create dxgi adapter");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
tmpDxgiAdapter->Release();
|
||||
++adapterIndex;
|
||||
}
|
||||
|
||||
dxgiFactory->Release();
|
||||
|
||||
if (!m_dxgiAdapter)
|
||||
{
|
||||
Cleanup();
|
||||
throw std::runtime_error("can't create dxgi adapter");
|
||||
}
|
||||
}
|
||||
|
||||
~DXGIWrapper()
|
||||
|
@ -65,15 +59,11 @@ public:
|
|||
|
||||
private:
|
||||
HMODULE m_moduleHandle = nullptr;
|
||||
IDXGIAdapter3* m_dxgiAdapter = nullptr;
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter3> m_dxgiAdapter;
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
if (m_dxgiAdapter)
|
||||
{
|
||||
m_dxgiAdapter->Release();
|
||||
m_dxgiAdapter = nullptr;
|
||||
}
|
||||
m_dxgiAdapter.Reset();
|
||||
|
||||
if (m_moduleHandle)
|
||||
{
|
||||
|
@ -81,4 +71,4 @@ private:
|
|||
m_moduleHandle = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -15,7 +15,12 @@ Fiber::Fiber(void(*FiberEntryPoint)(void* userParam), void* userParam, void* pri
|
|||
ctx->uc_stack.ss_sp = m_stackPtr;
|
||||
ctx->uc_stack.ss_size = stackSize;
|
||||
ctx->uc_link = &ctx[0];
|
||||
#ifdef __arm64__
|
||||
// https://www.man7.org/linux/man-pages/man3/makecontext.3.html#NOTES
|
||||
makecontext(ctx, (void(*)())FiberEntryPoint, 2, (uint64) userParam >> 32, userParam);
|
||||
#else
|
||||
makecontext(ctx, (void(*)())FiberEntryPoint, 1, userParam);
|
||||
#endif
|
||||
this->m_implData = (void*)ctx;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,11 @@ namespace MemMapper
|
|||
void* r;
|
||||
if(fromReservation)
|
||||
{
|
||||
if( mprotect(baseAddr, size, GetProt(permissionFlags)) == 0 )
|
||||
uint64 page_size = sysconf(_SC_PAGESIZE);
|
||||
void* page = baseAddr;
|
||||
if ( (uint64) baseAddr % page_size != 0 )
|
||||
page = (void*) ((uint64)baseAddr & ~(page_size - 1));
|
||||
if( mprotect(page, size, GetProt(permissionFlags)) == 0 )
|
||||
r = baseAddr;
|
||||
else
|
||||
r = nullptr;
|
||||
|
|
|
@ -111,9 +111,9 @@ namespace StringHelpers
|
|||
}
|
||||
|
||||
// convert utf8 string to Wii U big-endian wchar_t string
|
||||
static std::basic_string<uint16be> FromUtf8(std::string_view str)
|
||||
static std::vector<uint16be> FromUtf8(std::string_view str)
|
||||
{
|
||||
std::basic_string<uint16be> tmpStr;
|
||||
std::vector<uint16be> tmpStr;
|
||||
std::wstring w = boost::nowide::widen(str.data(), str.size());
|
||||
for (auto& c : w)
|
||||
tmpStr.push_back((uint16)c);
|
||||
|
|
|
@ -27,6 +27,8 @@ uint64 HighResolutionTimer::m_freq = []() -> uint64 {
|
|||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
return (uint64)(freq.QuadPart);
|
||||
#elif BOOST_OS_MACOS
|
||||
return 1000000000;
|
||||
#else
|
||||
timespec pc;
|
||||
clock_getres(CLOCK_MONOTONIC_RAW, &pc);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue