WinArm64-MSVC

This commit is contained in:
Live session user 2026-05-11 09:24:13 -07:00
parent 8e3e961b8e
commit d0c2212c5f
24 changed files with 640 additions and 476 deletions

View File

@ -128,7 +128,16 @@ jobs:
path: artifacts
build-windows:
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
include:
- os: windows-2022
arch: x64
- os: windows-11-arm
arch: arm64
name: build-windows-${{ matrix.arch }}
runs-on: ${{ matrix.os }}
steps:
- name: "Checkout repo"
uses: actions/checkout@v6
@ -190,19 +199,19 @@ jobs:
shell: cmd
run: |
cd src\resource
makensis /DPRODUCT_VERSION=${{ inputs.next_version_major }}.${{ inputs.next_version_minor }} installer.nsi
makensis /DPRODUCT_VERSION=${{ inputs.next_version_major }}.${{ inputs.next_version_minor }} /DARCH=${{ matrix.arch }} installer.nsi
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: cemu-bin-windows-x64
name: cemu-bin-windows-${{ matrix.arch }}
path: ./bin/Cemu.exe
- name: Upload NSIS Installer
uses: actions/upload-artifact@v6
with:
name: cemu-installer-windows-x64
path: ./src/resource/cemu-${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}-windows-x64-installer.exe
name: cemu-installer-windows-${{ matrix.arch }}
path: ./src/resource/cemu-${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}-windows-${{ matrix.arch }}-installer.exe
build-macos:
runs-on: macos-14

View File

@ -51,8 +51,16 @@ if (ENABLE_VCPKG)
# CONFIG option
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
if (WIN32)
set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "")
endif()
message(STATUS "CPU Arch is: $ENV{PROCESSOR_ARCHITECTURE}")
if($ENV{PROCESSOR_ARCHITECTURE} MATCHES "x86_64|amd64|AMD64")
set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "" FORCE)
set(VCPKG_HOST_TRIPLET "x64-windows-static" CACHE STRING "" FORCE)
else()
set(VCPKG_TARGET_TRIPLET "arm64-windows-static" CACHE STRING "" FORCE)
set(VCPKG_HOST_TRIPLET "arm64-windows-static" CACHE STRING "" FORCE)
endif()
message(STATUS "Using VCPKG triplet: ${VCPKG_TARGET_TRIPLET}")
endif()
endif()
project(Cemu VERSION 2.0.0)
@ -79,6 +87,7 @@ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
if (MSVC)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT CemuBin)
# floating point model: precise, fiber safe optimizations
add_compile_definitions(NOMINMAX)
add_compile_options(/EHsc /fp:precise)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Speeds up static linking (especially helpful in incremental compilation)
@ -173,6 +182,19 @@ if (NOT TARGET glslang::SPIRV AND TARGET SPIRV)
add_library(glslang::SPIRV ALIAS SPIRV)
endif()
if (UNIX AND NOT APPLE)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(ARCH_TRIPLET "aarch64-linux-gnu")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(ARCH_TRIPLET "x86_64-linux-gnu")
else()
message(WARNING "Unknown architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif()
if(ARCH_TRIPLET)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/share/pkgconfig:/usr/lib/${ARCH_TRIPLET}/pkgconfig:/usr/local/share/pkgconfig")
endif()
endif()
if (UNIX AND NOT APPLE)
find_package(X11 REQUIRED)
if (ENABLE_WAYLAND)
@ -253,6 +275,9 @@ if (ENABLE_CUBEB)
endif()
add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL)
if(MSVC)
target_compile_options(ih264d PRIVATE /wd4005)
endif()
if (CMAKE_OSX_ARCHITECTURES)
set(CEMU_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
@ -261,6 +286,9 @@ else()
endif()
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
add_subdirectory("dependencies/xbyak_aarch64" EXCLUDE_FROM_ALL)
if(MSVC)
set_target_properties(xbyak_aarch64 PROPERTIES COMPILE_FLAGS "/wd4245 /wd4458")
endif()
endif()
find_package(ZArchive QUIET)

View File

@ -1,4 +1,4 @@
{
{
"configurations": [
{
"name": "RelWithDebInfo",
@ -16,6 +16,14 @@
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}"
},
{
"name": "ARM64-Release",
"configurationType": "Release",
"generator": "Ninja",
"inheritEnvironments": [ "msvc_arm64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}"
},
{
"name": "Debug",
"configurationType": "Debug",
@ -25,4 +33,4 @@
"installRoot": "${projectDir}\\out\\install\\${name}"
}
]
}
}

View File

@ -1,204 +1,148 @@
cmake_minimum_required (VERSION 3.8)
cmake_minimum_required(VERSION 3.10)
project ("ih264d")
project(ih264d LANGUAGES C)
add_library (ih264d
"common/ih264_buf_mgr.c"
"common/ih264_buf_mgr.h"
"common/ih264_cabac_tables.c"
"common/ih264_cabac_tables.h"
"common/ih264_cavlc_tables.c"
"common/ih264_cavlc_tables.h"
"common/ih264_chroma_intra_pred_filters.c"
"common/ih264_common_tables.c"
"common/ih264_common_tables.h"
"common/ih264_deblk_edge_filters.c"
"common/ih264_deblk_edge_filters.h"
"common/ih264_deblk_tables.c"
"common/ih264_deblk_tables.h"
"common/ih264_debug.h"
"common/ih264_defs.h"
"common/ih264_disp_mgr.c"
"common/ih264_disp_mgr.h"
"common/ih264_dpb_mgr.c"
"common/ih264_dpb_mgr.h"
"common/ih264_error.h"
"common/ih264_ihadamard_scaling.c"
"common/ih264_inter_pred_filters.c"
"common/ih264_inter_pred_filters.h"
"common/ih264_intra_pred_filters.h"
"common/ih264_iquant_itrans_recon.c"
"common/ih264_list.c"
"common/ih264_list.h"
"common/ih264_luma_intra_pred_filters.c"
"common/ih264_macros.h"
"common/ih264_mem_fns.c"
"common/ih264_mem_fns.h"
"common/ih264_padding.c"
"common/ih264_padding.h"
"common/ih264_resi_trans.h"
"common/ih264_resi_trans_quant.c"
"common/ih264_size_defs.h"
"common/ih264_structs.h"
"common/ih264_trans_data.c"
"common/ih264_trans_data.h"
"common/ih264_trans_macros.h"
"common/ih264_trans_quant_itrans_iquant.h"
"common/ih264_typedefs.h"
"common/ih264_weighted_pred.c"
"common/ih264_weighted_pred.h"
"common/ithread.c"
"common/ithread.h"
"decoder/ih264d.h"
"decoder/ih264d_api.c"
"decoder/ih264d_bitstrm.c"
"decoder/ih264d_bitstrm.h"
"decoder/ih264d_cabac.c"
"decoder/ih264d_cabac.h"
"decoder/ih264d_cabac_init_tables.c"
"decoder/ih264d_compute_bs.c"
"decoder/ih264d_deblocking.c"
"decoder/ih264d_deblocking.h"
"decoder/ih264d_debug.h"
"decoder/ih264d_defs.h"
"decoder/ih264d_dpb_manager.h"
"decoder/ih264d_dpb_mgr.c"
"decoder/ih264d_error_handler.h"
"decoder/ih264d_format_conv.c"
"decoder/ih264d_format_conv.h"
"decoder/ih264d_function_selector.h"
"decoder/ih264d_function_selector_generic.c"
"decoder/ih264d_inter_pred.c"
"decoder/ih264d_inter_pred.h"
"decoder/ih264d_mb_utils.c"
"decoder/ih264d_mb_utils.h"
"decoder/ih264d_mem_request.h"
"decoder/ih264d_mvpred.c"
"decoder/ih264d_mvpred.h"
"decoder/ih264d_nal.c"
"decoder/ih264d_nal.h"
"decoder/ih264d_parse_bslice.c"
"decoder/ih264d_parse_cabac.c"
"decoder/ih264d_parse_cabac.h"
"decoder/ih264d_parse_cavlc.c"
"decoder/ih264d_parse_cavlc.h"
"decoder/ih264d_parse_headers.c"
"decoder/ih264d_parse_headers.h"
"decoder/ih264d_parse_islice.c"
"decoder/ih264d_parse_islice.h"
"decoder/ih264d_parse_mb_header.c"
"decoder/ih264d_parse_mb_header.h"
"decoder/ih264d_parse_pslice.c"
"decoder/ih264d_parse_slice.c"
"decoder/ih264d_parse_slice.h"
"decoder/ih264d_process_bslice.c"
"decoder/ih264d_process_bslice.h"
"decoder/ih264d_process_intra_mb.c"
"decoder/ih264d_process_intra_mb.h"
"decoder/ih264d_process_pslice.c"
"decoder/ih264d_process_pslice.h"
"decoder/ih264d_quant_scaling.c"
"decoder/ih264d_quant_scaling.h"
"decoder/ih264d_sei.c"
"decoder/ih264d_sei.h"
"decoder/ih264d_structs.h"
"decoder/ih264d_tables.c"
"decoder/ih264d_tables.h"
"decoder/ih264d_thread_compute_bs.c"
"decoder/ih264d_thread_compute_bs.h"
"decoder/ih264d_thread_parse_decode.c"
"decoder/ih264d_thread_parse_decode.h"
"decoder/ih264d_transfer_address.h"
"decoder/ih264d_utils.c"
"decoder/ih264d_utils.h"
"decoder/ih264d_vui.c"
"decoder/ih264d_vui.h"
"decoder/iv.h"
"decoder/ivd.h"
add_library(ih264d STATIC
"common/ih264_buf_mgr.c"
"common/ih264_cabac_tables.c"
"common/ih264_cavlc_tables.c"
"common/ih264_chroma_intra_pred_filters.c"
"common/ih264_common_tables.c"
"common/ih264_deblk_edge_filters.c"
"common/ih264_deblk_tables.c"
"common/ih264_disp_mgr.c"
"common/ih264_dpb_mgr.c"
"common/ih264_ihadamard_scaling.c"
"common/ih264_inter_pred_filters.c"
"common/ih264_iquant_itrans_recon.c"
"common/ih264_list.c"
"common/ih264_luma_intra_pred_filters.c"
"common/ih264_mem_fns.c"
"common/ih264_padding.c"
"common/ih264_resi_trans_quant.c"
"common/ih264_trans_data.c"
"common/ih264_weighted_pred.c"
"common/ithread.c"
"decoder/ih264d_api.c"
"decoder/ih264d_bitstrm.c"
"decoder/ih264d_cabac.c"
"decoder/ih264d_cabac_init_tables.c"
"decoder/ih264d_compute_bs.c"
"decoder/ih264d_deblocking.c"
"decoder/ih264d_dpb_mgr.c"
"decoder/ih264d_format_conv.c"
"decoder/ih264d_function_selector_generic.c"
"decoder/ih264d_inter_pred.c"
"decoder/ih264d_mb_utils.c"
"decoder/ih264d_mvpred.c"
"decoder/ih264d_nal.c"
"decoder/ih264d_parse_bslice.c"
"decoder/ih264d_parse_cabac.c"
"decoder/ih264d_parse_cavlc.c"
"decoder/ih264d_parse_headers.c"
"decoder/ih264d_parse_islice.c"
"decoder/ih264d_parse_mb_header.c"
"decoder/ih264d_parse_pslice.c"
"decoder/ih264d_parse_slice.c"
"decoder/ih264d_process_bslice.c"
"decoder/ih264d_process_intra_mb.c"
"decoder/ih264d_process_pslice.c"
"decoder/ih264d_quant_scaling.c"
"decoder/ih264d_sei.c"
"decoder/ih264d_tables.c"
"decoder/ih264d_thread_compute_bs.c"
"decoder/ih264d_thread_parse_decode.c"
"decoder/ih264d_utils.c"
"decoder/ih264d_vui.c"
)
if (CMAKE_OSX_ARCHITECTURES)
set(IH264D_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
if(CMAKE_OSX_ARCHITECTURES)
set(IH264D_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES})
else()
set(IH264D_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
set(IH264D_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
if (IH264D_ARCHITECTURE STREQUAL "x86_64" OR IH264D_ARCHITECTURE STREQUAL "amd64" OR IH264D_ARCHITECTURE STREQUAL "AMD64")
set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86")
include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES})
target_sources(ih264d PRIVATE
"common/x86/ih264_chroma_intra_pred_filters_ssse3.c"
"common/x86/ih264_deblk_chroma_ssse3.c"
"common/x86/ih264_deblk_luma_ssse3.c"
"common/x86/ih264_ihadamard_scaling_sse42.c"
"common/x86/ih264_ihadamard_scaling_ssse3.c"
"common/x86/ih264_inter_pred_filters_ssse3.c"
"common/x86/ih264_iquant_itrans_recon_dc_ssse3.c"
"common/x86/ih264_iquant_itrans_recon_sse42.c"
"common/x86/ih264_iquant_itrans_recon_ssse3.c"
"common/x86/ih264_luma_intra_pred_filters_ssse3.c"
"common/x86/ih264_mem_fns_ssse3.c"
"common/x86/ih264_padding_ssse3.c"
"common/x86/ih264_platform_macros.h"
"common/x86/ih264_resi_trans_quant_sse42.c"
"common/x86/ih264_weighted_pred_sse42.c"
"decoder/x86/ih264d_function_selector.c"
"decoder/x86/ih264d_function_selector_sse42.c"
"decoder/x86/ih264d_function_selector_ssse3.c"
)
elseif(IH264D_ARCHITECTURE STREQUAL "aarch64" OR IH264D_ARCHITECTURE STREQUAL "arm64")
enable_language( C CXX ASM )
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
target_sources(ih264d PRIVATE
"common/armv8/ih264_deblk_chroma_av8.s"
"common/armv8/ih264_deblk_luma_av8.s"
"common/armv8/ih264_default_weighted_pred_av8.s"
"common/armv8/ih264_ihadamard_scaling_av8.s"
"common/armv8/ih264_inter_pred_chroma_av8.s"
"common/armv8/ih264_inter_pred_filters_luma_horz_av8.s"
"common/armv8/ih264_inter_pred_filters_luma_vert_av8.s"
"common/armv8/ih264_inter_pred_luma_copy_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_hpel_vert_hpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_hpel_vert_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_vert_hpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_vert_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_vert_qpel_av8.s"
"common/armv8/ih264_intra_pred_chroma_av8.s"
"common/armv8/ih264_intra_pred_luma_16x16_av8.s"
"common/armv8/ih264_intra_pred_luma_4x4_av8.s"
"common/armv8/ih264_intra_pred_luma_8x8_av8.s"
"common/armv8/ih264_iquant_itrans_recon_av8.s"
"common/armv8/ih264_iquant_itrans_recon_dc_av8.s"
"common/armv8/ih264_mem_fns_neon_av8.s"
"common/armv8/ih264_neon_macros.s"
"common/armv8/ih264_padding_neon_av8.s"
"common/armv8/ih264_platform_macros.h"
"common/armv8/ih264_resi_trans_quant_av8.s"
"common/armv8/ih264_weighted_bi_pred_av8.s"
"common/armv8/ih264_weighted_pred_av8.s"
"decoder/arm/ih264d_function_selector_a9q.c"
"decoder/arm/ih264d_function_selector_av8.c"
"decoder/arm/ih264d_function_selector.c"
)
target_compile_options(ih264d PRIVATE -DARMV8 $<$<COMPILE_LANGUAGE:ASM,Clang>:-Wno-unused-command-line-argument>)
if(NOT MSVC)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
endif()
if(APPLE)
target_sources(ih264d PRIVATE "common/armv8/macos_arm_symbol_aliases.s")
endif()
message(STATUS "DEBUG: ih264d detected architecture is: ${IH264D_ARCHITECTURE}")
if(IH264D_ARCHITECTURE MATCHES "x86_64|amd64|AMD64|i386|i686")
set(LIBAVCDEC_X86_INCLUDES "common/x86" "decoder/x86")
include_directories("common/" "decoder/" ${LIBAVCDEC_X86_INCLUDES})
target_sources(ih264d PRIVATE
"common/x86/ih264_chroma_intra_pred_filters_ssse3.c"
"common/x86/ih264_deblk_chroma_ssse3.c"
"common/x86/ih264_deblk_luma_ssse3.c"
"common/x86/ih264_ihadamard_scaling_sse42.c"
"common/x86/ih264_ihadamard_scaling_ssse3.c"
"common/x86/ih264_inter_pred_filters_ssse3.c"
"common/x86/ih264_iquant_itrans_recon_dc_ssse3.c"
"common/x86/ih264_iquant_itrans_recon_sse42.c"
"common/x86/ih264_iquant_itrans_recon_ssse3.c"
"common/x86/ih264_luma_intra_pred_filters_ssse3.c"
"common/x86/ih264_mem_fns_ssse3.c"
"common/x86/ih264_padding_ssse3.c"
"common/x86/ih264_resi_trans_quant_sse42.c"
"common/x86/ih264_weighted_pred_sse42.c"
"decoder/x86/ih264d_function_selector.c"
"decoder/x86/ih264d_function_selector_sse42.c"
"decoder/x86/ih264d_function_selector_ssse3.c"
)
elseif(IH264D_ARCHITECTURE MATCHES "aarch64|arm64|ARM64")
set(LIBAVCDEC_ARM_INCLUDES "common/armv8" "decoder/arm")
include_directories("common/" "decoder/" ${LIBAVCDEC_ARM_INCLUDES})
if(MSVC)
message(STATUS "MSVC ARM64 detected: Forcing portable C implementation.")
target_sources(ih264d PRIVATE
"decoder/arm/ih264d_function_selector.c"
"decoder/arm/ih264d_function_selector_av8.c"
)
target_compile_definitions(ih264d PRIVATE PORTABLE_C ARCH_GENERIC DISABLE_NEON)
else()
enable_language(ASM)
message(STATUS "ARM64 Clang/GCC detected: Enabling assembly optimization.")
target_sources(ih264d PRIVATE
"common/armv8/ih264_deblk_chroma_av8.s"
"common/armv8/ih264_deblk_luma_av8.s"
"common/armv8/ih264_default_weighted_pred_av8.s"
"common/armv8/ih264_ihadamard_scaling_av8.s"
"common/armv8/ih264_inter_pred_chroma_av8.s"
"common/armv8/ih264_inter_pred_filters_luma_horz_av8.s"
"common/armv8/ih264_inter_pred_filters_luma_vert_av8.s"
"common/armv8/ih264_inter_pred_luma_copy_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_hpel_vert_hpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_hpel_vert_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_vert_hpel_av8.s"
"common/armv8/ih264_inter_pred_luma_horz_qpel_vert_qpel_av8.s"
"common/armv8/ih264_inter_pred_luma_vert_qpel_av8.s"
"common/armv8/ih264_intra_pred_chroma_av8.s"
"common/armv8/ih264_intra_pred_luma_16x16_av8.s"
"common/armv8/ih264_intra_pred_luma_4x4_av8.s"
"common/armv8/ih264_intra_pred_luma_8x8_av8.s"
"common/armv8/ih264_iquant_itrans_recon_av8.s"
"common/armv8/ih264_iquant_itrans_recon_dc_av8.s"
"common/armv8/ih264_mem_fns_neon_av8.s"
"common/armv8/ih264_padding_neon_av8.s"
"common/armv8/ih264_resi_trans_quant_av8.s"
"common/armv8/ih264_weighted_bi_pred_av8.s"
"common/armv8/ih264_weighted_pred_av8.s"
"decoder/arm/ih264d_function_selector_av8.c"
"decoder/arm/ih264d_function_selector.c"
)
target_compile_definitions(ih264d PRIVATE ARMV8)
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -x assembler-with-cpp")
endif()
if(APPLE)
target_sources(ih264d PRIVATE "common/armv8/macos_arm_symbol_aliases.s")
endif()
else()
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
message(FATAL_ERROR "ih264d unknown architecture: ${IH264D_ARCHITECTURE}")
endif()
if(MSVC)
set_property(TARGET ih264d PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# tune settings for slightly better performance
target_compile_options(ih264d PRIVATE $<$<CONFIG:Release,RelWithDebInfo>:/Oi>) # enable intrinsic functions
target_compile_options(ih264d PRIVATE $<$<CONFIG:Release,RelWithDebInfo>:/Ot>) # favor speed
target_compile_options(ih264d PRIVATE "/GS-") # disable runtime checks
set_property(TARGET ih264d PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_compile_options(ih264d PRIVATE $<$<CONFIG:Release,RelWithDebInfo>:/Oi> $<$<CONFIG:Release,RelWithDebInfo>:/Ot> "/GS-")
endif()

View File

@ -21,6 +21,17 @@
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ]
},
{
"name": "arm64-Release",
"generator": "Ninja",
"configurationType": "Release",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_arm64_x64" ]
}
]
}
}

View File

@ -38,144 +38,95 @@
#include <stdint.h>
#ifndef ARMV8
static __inline WORD32 CLIP_U8(WORD32 x)
{
asm("usat %0, #8, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_S8(WORD32 x)
{
asm("ssat %0, #8, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_U10(WORD32 x)
{
asm("usat %0, #10, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_S10(WORD32 x)
{
asm("ssat %0, #10, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_U11(WORD32 x)
{
asm("usat %0, #11, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_S11(WORD32 x)
{
asm("ssat %0, #11, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_U12(WORD32 x)
{
asm("usat %0, #12, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_S12(WORD32 x)
{
asm("ssat %0, #12, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_U16(WORD32 x)
{
asm("usat %0, #16, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline WORD32 CLIP_S16(WORD32 x)
{
asm("ssat %0, #16, %1" : "=r"(x) : "r"(x));
return x;
}
static __inline UWORD32 ITT_BIG_ENDIAN(UWORD32 x)
{
asm("rev %0, %1" : "=r"(x) : "r"(x));
return x;
}
#define NOP(nop_cnt) {UWORD32 nop_i; for (nop_i = 0; nop_i < nop_cnt; nop_i++) asm("nop");}
#else
#define CLIP_U8(x) CLIP3(0, UINT8_MAX, (x))
#define CLIP_S8(x) CLIP3(INT8_MIN, INT8_MAX, (x))
#define CLIP_U10(x) CLIP3(0, 1023, (x))
#define CLIP_S10(x) CLIP3(-512, 511, (x))
#define CLIP_U11(x) CLIP3(0, 2047, (x))
#define CLIP_S11(x) CLIP3(-1024, 1023, (x))
#define CLIP_U12(x) CLIP3(0, 4095, (x))
#define CLIP_S12(x) CLIP3(-2048, 2047, (x))
#define CLIP_U16(x) CLIP3(0, UINT16_MAX, (x))
#define CLIP_S16(x) CLIP3(INT16_MIN, INT16_MAX, (x))
#define ITT_BIG_ENDIAN(x) __asm__("rev %0, %1" : "=r"(x) : "r"(x));
#define NOP(nop_cnt) \
{ \
UWORD32 nop_i; \
for (nop_i = 0; nop_i < nop_cnt; nop_i++) \
__asm__ __volatile__("mov x0, x0"); \
}
#ifndef WORD32
typedef int32_t WORD32;
#endif
#ifndef UWORD32
typedef uint32_t UWORD32;
#endif
/*saturating instructions are not available for WORD64 in ARMv7, hence we cannot
* use inline assembly like other clips*/
#define CLIP_U32(x) CLIP3(0, UINT32_MAX, (x))
#define CLIP_S32(x) CLIP3(INT32_MIN, INT32_MAX, (x))
#include <arm_neon.h>
#define DATA_SYNC() __sync_synchronize()
#ifdef _MSC_VER
#include <intrin.h>
#if defined(_M_ARM64) && !defined(__arm64_intrinsics_h__)
#ifndef _ARM64_BARRIER_ISH
#define _ARM64_BARRIER_ISH 0xB
#endif
#endif
#define SHL(x,y) (((y) < 32) ? ((x) << (y)) : 0)
#define SHR(x,y) (((y) < 32) ? ((x) >> (y)) : 0)
#define INLINE __inline
#define MEM_ALIGN8 __declspec(align(8))
#define MEM_ALIGN16 __declspec(align(16))
#define MEM_ALIGN32 __declspec(align(32))
#define DATA_SYNC() __dmb(_ARM64_BARRIER_ISH)
#define NOP(nop_cnt) { for (int i = 0; i < (int)nop_cnt; i++) __nop(); }
#define ITT_BIG_ENDIAN(x) _byteswap_ulong(x)
#else
#define INLINE inline
#define MEM_ALIGN8 __attribute__ ((aligned (8)))
#define MEM_ALIGN16 __attribute__ ((aligned (16)))
#define MEM_ALIGN32 __attribute__ ((aligned (32)))
#define DATA_SYNC() __sync_synchronize()
#define NOP(nop_cnt) \
{ \
uint32_t nop_i; \
for (nop_i = 0; nop_i < nop_cnt; nop_i++) \
__asm__ __volatile__("mov x0, x0"); \
}
static INLINE uint32_t ITT_BIG_ENDIAN(uint32_t x) {
return __builtin_bswap32(x);
}
#endif
#ifndef CLIP3
#define CLIP3(min_val, max_val, x) (((x) < (min_val)) ? (min_val) : (((x) > (max_val)) ? (max_val) : (x)))
#endif
#define CLIP_U8(x) CLIP3(0, 255, (x))
#define CLIP_S8(x) CLIP3(-128, 127, (x))
#define CLIP_U10(x) CLIP3(0, 1023, (x))
#define CLIP_S10(x) CLIP3(-512, 511, (x))
#define CLIP_U11(x) CLIP3(0, 2047, (x))
#define CLIP_S11(x) CLIP3(-1024, 1023, (x))
#define CLIP_U12(x) CLIP3(0, 4095, (x))
#define CLIP_S12(x) CLIP3(-2048, 2047, (x))
#define CLIP_U16(x) CLIP3(0, 65535, (x))
#define CLIP_S16(x) CLIP3(-32768, 32767, (x))
#define CLIP_U32(x) CLIP3(0, 0xFFFFFFFF, (x))
#define CLIP_S32(x) CLIP3(-2147483647-1, 2147483647, (x))
static INLINE uint32_t CLZ(uint32_t u4_word) {
if (u4_word == 0) return 31;
#ifdef _MSC_VER
return (uint32_t)_CountLeadingZeros(u4_word);
#else
return (uint32_t)__builtin_clz(u4_word);
#endif
}
static INLINE uint32_t CTZ(uint32_t u4_word) {
if (u4_word == 0) return 31;
#ifdef _MSC_VER
unsigned long index;
_BitScanForward(&index, u4_word);
return (uint32_t)index;
#else
return (uint32_t)__builtin_ctz(u4_word);
#endif
}
#define SHL(x,y) (((y) < 32) ? ((uint32_t)(x) << (y)) : 0)
#define SHR(x,y) (((y) < 32) ? ((uint32_t)(x) >> (y)) : 0)
#define SHR_NEG(val,shift) ((shift>0)?(val>>shift):(val<<(-shift)))
#define SHL_NEG(val,shift) ((shift<0)?(val>>(-shift)):(val<<shift))
#define INLINE inline
/* In normal cases, 0 will not be passed as an argument to CLZ and CTZ.
As CLZ and CTZ outputs are used as a shift value in few places, these return
31 for u4_word == 0 case, just to handle error cases gracefully without any
undefined behaviour */
static INLINE UWORD32 CLZ(UWORD32 u4_word)
{
if(u4_word)
return (__builtin_clz(u4_word));
else
return 31;
}
static INLINE UWORD32 CTZ(UWORD32 u4_word)
{
if(0 == u4_word)
return 31;
else
{
unsigned int index;
index = __builtin_ctz(u4_word);
return (UWORD32)index;
}
}
#define MEM_ALIGN8 __attribute__ ((aligned (8)))
#define MEM_ALIGN16 __attribute__ ((aligned (16)))
#define MEM_ALIGN32 __attribute__ ((aligned (32)))
#endif /* _IH264_PLATFORM_MACROS_H_ */

View File

@ -1,16 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
<!-- Changed processorArchitecture to "*" for ARM64/x64 compatibility -->
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel></requestedPrivileges></security>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- Modern DPI awareness setting (covers Windows 7 through 11) -->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>
</assembly>

View File

@ -70,10 +70,15 @@ add_executable(CemuBin
cemu_use_precompiled_header(CemuBin)
if(MSVC AND MSVC_VERSION EQUAL 1940)
# workaround for an msvc issue on VS 17.10 where generated ILK files are too large
# see https://developercommunity.visualstudio.com/t/After-updating-to-VS-1710-the-size-of-/10665511
set_target_properties(CemuBin PROPERTIES LINK_FLAGS "/INCREMENTAL:NO")
if(MSVC)
set(CEMU_LINKER_FLAGS "/NODEFAULTLIB:MSVCRT")
# Check for the specific VS 17.10 version
if(MSVC_VERSION EQUAL 1940)
string(APPEND CEMU_LINKER_FLAGS " /INCREMENTAL:NO")
endif()
set_target_properties(CemuBin PROPERTIES LINK_FLAGS "${CEMU_LINKER_FLAGS}")
endif()
if(WIN32)

View File

@ -83,13 +83,6 @@ add_library(CemuCafe
HW/Espresso/Recompiler/PPCRecompilerImlGenFPU.cpp
HW/Espresso/Recompiler/PPCRecompilerIml.h
HW/Espresso/Recompiler/PPCRecompilerIntermediate.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64.h
HW/Espresso/Recompiler/BackendX64/X64Emit.hpp
HW/Espresso/Recompiler/BackendX64/x86Emitter.h
HW/Latte/Common/RegisterSerializer.cpp
@ -618,12 +611,23 @@ if(ENABLE_METAL)
)
endif()
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
target_sources(CemuCafe PRIVATE
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.h
)
target_link_libraries(CemuCafe PRIVATE xbyak_aarch64)
if(CEMU_ARCHITECTURE MATCHES "(x86_64|amd64|AMD64)")
target_sources(CemuCafe PRIVATE
HW/Espresso/Recompiler/BackendX64/BackendX64.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64.h
HW/Espresso/Recompiler/BackendX64/BackendX64AVX.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64BMI.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64FPU.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64Gen.cpp
HW/Espresso/Recompiler/BackendX64/BackendX64GenFPU.cpp
)
else()
target_sources(CemuCafe PRIVATE
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.h
)
target_link_libraries(CemuCafe PRIVATE xbyak_aarch64)
target_compile_definitions(ih264d PRIVATE ARCH_GENERIC DISABLE_NEON)
endif()
set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
@ -646,7 +650,7 @@ else()
endif()
target_link_libraries(CemuCafe PRIVATE
CemuCommon
CemuCommon
CemuGui
ZArchive::zarchive
imguiImpl
@ -660,6 +664,10 @@ target_link_libraries(CemuCafe PRIVATE
OpenSSL::SSL
)
if(MSVC AND CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
target_link_libraries(CemuCafe PRIVATE ntdll.lib)
endif()
if (ENABLE_WAYLAND)
# PUBLIC because wayland-client.h is included in VulkanAPI.h
target_link_libraries(CemuCafe PUBLIC Wayland::Client)
@ -668,7 +676,9 @@ endif()
if (ENABLE_LIBUSB)
if (ENABLE_VCPKG)
if(WIN32)
set(PKG_CONFIG_EXECUTABLE "${VCPKG_INSTALLED_DIR}/x64-windows/tools/pkgconf/pkgconf.exe")
if(DEFINED VCPKG_TARGET_TRIPLET)
set(PKG_CONFIG_EXECUTABLE "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/tools/pkgconf/pkgconf.exe")
endif()
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)

View File

@ -199,7 +199,7 @@ void debugger_updateMemoryBreakpoint(DebuggerBreakpoint* bp)
{
std::vector<std::thread::native_handle_type> schedulerThreadHandles = coreinit::OSGetSchedulerThreads();
#if BOOST_OS_WINDOWS
#if BOOST_OS_WINDOWS && defined(_M_X64)
s_debuggerState.activeMemoryBreakpoint = bp;
for (auto& hThreadNH : schedulerThreadHandles)
{
@ -235,9 +235,51 @@ void debugger_updateMemoryBreakpoint(DebuggerBreakpoint* bp)
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);
}
#else
#elif defined(_M_X64)
s_debuggerState.activeMemoryBreakpoint = bp;
for (auto& hThreadNH : schedulerThreadHandles)
{
HANDLE hThread = (HANDLE)hThreadNH;
CONTEXT ctx{};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SuspendThread(hThread);
if (GetThreadContext(hThread, &ctx))
{
if (s_debuggerState.activeMemoryBreakpoint)
{
// ARM64 uses Bvr/Bcr pairs. We'll use slot 0.
ctx.Bvr[0] = (DWORD64)memory_getPointerFromVirtualOffset(bp->address);
// Construct the Control Register (BCR)
DWORD64 bcr = 0;
bcr |= 0x1; // Bit 0: Global Enable
bcr |= (0x3 << 1); // Bits 1-2: User and Privileged access
bcr |= (0xF << 5); // Bits 5-8: Byte address select (watch all 4 bytes)
// Map Cemu BP types to ARM64 Watchpoint types
if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ)
bcr |= (0x1 << 21); // Bit 21-22: 1 = Load
else if (bp->bpType == DEBUGGER_BP_T_MEMORY_WRITE)
bcr |= (0x2 << 21); // Bit 21-22: 2 = Store
else
bcr |= (0x3 << 21); // Bit 21-22: 3 = Either
ctx.Bcr[0] = bcr;
}
else
{
// Disable breakpoint slot 0
ctx.Bvr[0] = 0;
ctx.Bcr[0] = 0;
}
SetThreadContext(hThread, &ctx);
}
ResumeThread(hThread);
}
#else
cemuLog_log(LogType::Force, "Debugger breakpoints are not supported");
#endif
#endif
}
void debugger_handleSingleStepException(uint64 dr6)

View File

@ -3,14 +3,11 @@
#include "util/helpers/fspinlock.h"
#include "util/highresolutiontimer/HighResolutionTimer.h"
#include "Common/cpu_features.h"
#if defined(ARCH_X86_64)
#include <immintrin.h>
#pragma intrinsic(__rdtsc)
#endif
#include "Common/Intrinsics.h"
uint64 _rdtscLastMeasure = 0;
uint64 _rdtscFrequency = 0;
uint64 _tickSummary = 0;
struct uint128_t
{
@ -33,39 +30,32 @@ uint64 muldiv64(uint64 a, uint64 b, uint64 d)
uint64 PPCTimer_estimateRDTSCFrequency()
{
#if defined(ARCH_X86_64)
#if defined(ARCH_X86_64)
if (!g_CPUFeatures.x86.invariant_tsc)
cemuLog_log(LogType::Force, "Invariant TSC not supported");
#endif
#endif
_mm_mfence();
uint64 tscStart = __rdtsc();
BARRIER_FENCE();
uint64 tscStart = READ_TSC();
unsigned int startTime = GetTickCount();
HRTick startTick = HighResolutionTimer::now().getTick();
// wait roughly 3 seconds
while (true)
{
if ((GetTickCount() - startTime) >= 3000)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
_mm_mfence();
BARRIER_FENCE();
HRTick stopTick = HighResolutionTimer::now().getTick();
uint64 tscEnd = __rdtsc();
// derive frequency approximation from measured time difference
uint64 tscEnd = READ_TSC();
uint64 tsc_diff = tscEnd - tscStart;
uint64 hrtFreq = 0;
uint64 hrtDiff = HighResolutionTimer::getTimeDiffEx(startTick, stopTick, hrtFreq);
uint64 tsc_freq = muldiv64(tsc_diff, hrtFreq, hrtDiff);
// uint64 freqMultiplier = tsc_freq / hrtFreq;
//cemuLog_log(LogType::Force, "RDTSC measurement test:");
//cemuLog_log(LogType::Force, "TSC-diff: 0x{:016x}", tsc_diff);
//cemuLog_log(LogType::Force, "TSC-freq: 0x{:016x}", tsc_freq);
//cemuLog_log(LogType::Force, "HPC-diff: 0x{:016x}", qpc_diff);
//cemuLog_log(LogType::Force, "HPC-freq: 0x{:016x}", (uint64)qpc_freq.QuadPart);
//cemuLog_log(LogType::Force, "Multiplier: 0x{:016x}", freqMultiplier);
return tsc_freq;
}
@ -79,20 +69,18 @@ void PPCTimer_init()
{
std::thread t(PPCTimer_initThread);
t.detach();
_rdtscLastMeasure = __rdtsc();
_rdtscLastMeasure = READ_TSC();
}
uint64 _tickSummary = 0;
void PPCTimer_start()
{
_rdtscLastMeasure = __rdtsc();
_rdtscLastMeasure = READ_TSC();
_tickSummary = 0;
}
uint64 PPCTimer_getRawTsc()
{
return __rdtsc();
return READ_TSC();
}
uint64 PPCTimer_microsecondsToTsc(uint64 us)
@ -103,10 +91,12 @@ uint64 PPCTimer_microsecondsToTsc(uint64 us)
uint64 PPCTimer_tscToMicroseconds(uint64 us)
{
uint128_t r{};
r.low = _umul128(us, 1000000ULL, &r.high);
// Changed: Using Multiply64to128 wrapper
r.low = Multiply64to128(us, 1000000ULL, &r.high);
uint64 remainder;
const uint64 microseconds = _udiv128(r.high, r.low, _rdtscFrequency, &remainder);
// Changed: Using Divide128by64 wrapper
const uint64 microseconds = Divide128by64(r.high, r.low, _rdtscFrequency, &remainder);
return microseconds;
}
@ -123,40 +113,37 @@ void PPCTimer_waitForInit()
FSpinlock sTimerSpinlock;
// thread safe
uint64 PPCTimer_getFromRDTSC()
{
sTimerSpinlock.lock();
_mm_mfence();
uint64 rdtscCurrentMeasure = __rdtsc();
BARRIER_FENCE();
uint64 rdtscCurrentMeasure = READ_TSC();
uint64 rdtscDif = rdtscCurrentMeasure - _rdtscLastMeasure;
// optimized max(rdtscDif, 0) without conditionals
rdtscDif = rdtscDif & ~(uint64)((sint64)rdtscDif >> 63);
uint128_t diff{};
diff.low = _umul128(rdtscDif, Espresso::CORE_CLOCK, &diff.high);
diff.low = Multiply64to128(rdtscDif, Espresso::CORE_CLOCK, &diff.high);
if(rdtscCurrentMeasure > _rdtscLastMeasure)
_rdtscLastMeasure = rdtscCurrentMeasure; // only travel forward in time
_rdtscLastMeasure = rdtscCurrentMeasure;
uint8 c = 0;
#if BOOST_OS_WINDOWS
#if defined(_MSC_VER)
c = _addcarry_u64(c, _rdtscAcc.low, diff.low, &_rdtscAcc.low);
_addcarry_u64(c, _rdtscAcc.high, diff.high, &_rdtscAcc.high);
#else
// requires casting because of long / long long nonesense
c = _addcarry_u64(c, _rdtscAcc.low, diff.low, (unsigned long long*)&_rdtscAcc.low);
_addcarry_u64(c, _rdtscAcc.high, diff.high, (unsigned long long*)&_rdtscAcc.high);
#endif
uint64 remainder;
uint64 elapsedTick = _udiv128(_rdtscAcc.high, _rdtscAcc.low, _rdtscFrequency, &remainder);
uint64 elapsedTick = Divide128by64(_rdtscAcc.high, _rdtscAcc.low, _rdtscFrequency, &remainder);
_rdtscAcc.low = remainder;
_rdtscAcc.high = 0;
// timer scaling
elapsedTick <<= 3ull; // *8
elapsedTick <<= 3ull;
uint8 timerShiftFactor = ActiveSettings::GetTimerShiftFactor();
elapsedTick >>= timerShiftFactor;

View File

@ -758,7 +758,7 @@ struct IMLInstruction
void make_fpr_compare(IMLReg regA, IMLReg regB, IMLReg regR, IMLCondition cond)
{
this->type = PPCREC_IML_TYPE_FPR_COMPARE;
this->operation = -999;
this->operation = 255;
this->op_fpr_compare.regR = regR;
this->op_fpr_compare.regA = regA;
this->op_fpr_compare.regB = regB;
@ -807,7 +807,7 @@ struct IMLInstruction
void make_x86_eflags_jcc(IMLCondition cond, bool invertedCondition)
{
this->type = PPCREC_IML_TYPE_X86_EFLAGS_JCC;
this->operation = -999;
this->operation = 255;
this->op_x86_eflags_jcc.cond = cond;
this->op_x86_eflags_jcc.invertedCondition = invertedCondition;
}
@ -823,4 +823,4 @@ namespace IMLArchX86
{
static constexpr int PHYSREG_GPR_BASE = 0;
static constexpr int PHYSREG_FPR_BASE = 16;
};
};

View File

@ -146,7 +146,7 @@ uint32 LatteTexture_CalculateTextureDataHash(LatteTexture* hostTexture)
bool isCompressedFormat = hostTexture->IsCompressedFormat();
if( isCompressedFormat == false )
{
#if BOOST_OS_WINDOWS
#if BOOST_OS_WINDOWS && defined(_M_X64)
if (g_CPUFeatures.x86.avx2)
{
__m256i h256 = { 0 };
@ -430,4 +430,4 @@ void LatteTC_UnloadAllTextures()
LatteTexture_Delete(itr);
}
LatteRenderTarget_unloadAll();
}
}

View File

@ -1,3 +1,4 @@
#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/HW/Latte/Renderer/Renderer.h"
#include "Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.h"
#include "config/ActiveSettings.h"
@ -707,7 +708,7 @@ void optimizedLinearReadbackWriteLoop(LatteTextureLoaderCtx* textureLoader, uint
copyType* blockData = (copyType*)LatteTextureLoader_getInputLinearOptimized_(textureLoader, 0, y, 1, 1, sizeof(copyType) * 8, 0, 1, 0, textureLoader->pitch, textureLoader->height);
if constexpr (sizeof(copyType) == 4)
{
memcpy_dwords(blockData, rowPixelData, textureLoader->width);
SAFE_MOVSD(blockData, rowPixelData, textureLoader->width);
}
else
{

View File

@ -113,4 +113,41 @@ static void* _ppc_va_arg(ppc_va_list* vargs, ppc_va_type argType)
vargs->overflow_arg_area += 8;
return r;
}
}
}
#ifdef _WIN32
#include <intrin.h>
#else
#include <string.h>
#if defined(__x86_64__) || defined(__i386__)
#include <x86intrin.h>
#endif
#endif
#ifdef _WIN32
static inline void safe_memcpy_dwords(void* dest, const void* src, size_t count) {
#if defined(_M_ARM64) || defined(_M_ARM)
memcpy(dest, src, count * 4);
#else
__movsd((unsigned long*)dest, (const unsigned long*)src, (unsigned long)count);
#endif
}
static inline void safe_memcpy_qwords(void* dest, const void* src, size_t count) {
#if defined(_M_ARM64) || defined(_M_ARM)
memcpy(dest, src, count * 8);
#else
__movsq((unsigned __int64*)dest, (const unsigned __int64*)src, count);
#endif
}
#define SAFE_MOVSD(d, s, c) safe_memcpy_dwords((d), (s), (c))
#define SAFE_MOVSQ(d, s, c) safe_memcpy_qwords((d), (s), (c))
#else
static inline void safe_memcpy_dwords(void* dest, const void* src, size_t count) {
memcpy(dest, src, count * 4);
}
static inline void safe_memcpy_qwords(void* dest, const void* src, size_t count) {
memcpy(dest, src, count * 8);
}
#define SAFE_MOVSD(d, s, c) safe_memcpy_dwords((d), (s), (c))
#define SAFE_MOVSQ(d, s, c) safe_memcpy_qwords((d), (s), (c))
#endif

View File

@ -256,7 +256,7 @@ namespace coreinit
uint8* destPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[3]);
uint8* srcPtr = memory_getPointerFromVirtualOffset(hCPU->gpr[4]);
// copy right away, we don't emulate the DMAQueue currently
memcpy_qwords(destPtr, srcPtr, numBlocks * (32 / sizeof(uint64)));
SAFE_MOVSQ(destPtr, srcPtr, (size_t)numBlocks * 4);
LatteBufferCache_notifyDCFlush(hCPU->gpr[3], numBlocks * 32);

View File

@ -42,7 +42,7 @@ void gx2WriteGather_submitU32AsLEArray(uint32* v, uint32 numValues)
uint32 coreIndex = PPCInterpreter_getCoreIndex(PPCInterpreter_getCurrentInstance());
if (GX2::s_perCoreCBState[coreIndex].currentWritePtr == nullptr)
return;
memcpy_dwords(GX2::s_perCoreCBState[coreIndex].currentWritePtr, v, numValues);
SAFE_MOVSD(GX2::s_perCoreCBState[coreIndex].currentWritePtr, v, numValues);
GX2::s_perCoreCBState[coreIndex].currentWritePtr += numValues;
cemu_assert_debug(GX2::s_perCoreCBState[coreIndex].currentWritePtr <= (GX2::s_perCoreCBState[coreIndex].bufferPtr + GX2::s_perCoreCBState[coreIndex].bufferSizeInU32s));
}

View File

@ -209,6 +209,20 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context)
// register info
sprintf(dumpLine, "\n");
cemuLog_writePlainToLog(dumpLine);
#if defined(_M_ARM64)
// ARM64 Register Dump
sprintf(dumpLine, "X0 =%016I64x X1 =%016I64x X2 =%016I64x X3 =%016I64x\n", context->X0, context->X1, context->X2, context->X3);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "X4 =%016I64x X5 =%016I64x X6 =%016I64x X7 =%016I64x\n", context->X4, context->X5, context->X6, context->X7);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "X8 =%016I64x X9 =%016I64x X10=%016I64x X11=%016I64x\n", context->X8, context->X9, context->X10, context->X11);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "X12=%016I64x X13=%016I64x X14=%016I64x X15=%016I64x\n", context->X12, context->X13, context->X14, context->X15);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "SP =%016I64x PC =%016I64x\n", context->Sp, context->Pc);
cemuLog_writePlainToLog(dumpLine);
#else
// x86_64 Register Dump
sprintf(dumpLine, "RAX=%016I64x RBX=%016I64x RCX=%016I64x RDX=%016I64x\n", context->Rax, context->Rbx, context->Rcx, context->Rdx);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "RSP=%016I64x RBP=%016I64x RDI=%016I64x RSI=%016I64x\n", context->Rsp, context->Rbp, context->Rdi, context->Rsi);
@ -217,6 +231,7 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context)
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x\n", context->R12, context->R13, context->R14, context->R15);
cemuLog_writePlainToLog(dumpLine);
#endif
CrashLog_SetOutputChannels(false, true);
ExceptionHandler_LogGeneralInfo();
@ -264,10 +279,13 @@ LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
if (r != EXCEPTION_CONTINUE_SEARCH)
return r;
#if !defined(_M_ARM64)
// Hardware breakpoints (Dr6) are x86-specific in the Windows CONTEXT struct
if (GetBits(pExceptionInfo->ContextRecord->Dr6, 0, 1) || GetBits(pExceptionInfo->ContextRecord->Dr6, 1, 1))
debugger_handleSingleStepException(pExceptionInfo->ContextRecord->Dr6);
else if (GetBits(pExceptionInfo->ContextRecord->Dr6, 2, 1) || GetBits(pExceptionInfo->ContextRecord->Dr6, 3, 1))
g_gdbstub->HandleAccessException(pExceptionInfo->ContextRecord->Dr6);
#endif
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;

88
src/Common/Intrinsics.h Normal file
View File

@ -0,0 +1,88 @@
#pragma once
#include <cstdint>
#if defined(_MSC_VER)
#include <intrin.h>
#if defined(_M_ARM64)
#include <arm64intr.h>
#define BARRIER_FENCE() __dmb(_ARM64_BARRIER_ISH)
#define READ_TSC() _ReadStatusReg(ARM64_CNTVCT_EL0)
#else
#include <immintrin.h>
#define BARRIER_FENCE() _mm_mfence()
#define READ_TSC() __rdtsc()
#endif
#else
#if defined(__aarch64__)
#define BARRIER_FENCE() __asm__ __volatile__ ("dmb ish" : : : "memory")
static inline uint64_t READ_TSC() {
uint64_t val;
__asm__ __volatile__("mrs %0, cntvct_el0" : "=r" (val));
return val;
}
#else
#include <x86intrin.h>
#define BARRIER_FENCE() __asm__ __volatile__ ("mfence" : : : "memory")
#define READ_TSC() __rdtsc()
#endif
#endif
static inline uint64_t Multiply64to128(uint64_t a, uint64_t b, uint64_t* high) {
#if defined(__SIZEOF_INT128__)
unsigned __int128 res = (unsigned __int128)a * b;
*high = (uint64_t)(res >> 64);
return (uint64_t)res;
#elif defined(_MSC_VER) && defined(_M_X64)
return _umul128(a, b, high);
#elif defined(_MSC_VER) && defined(_M_ARM64)
*high = __umulh(a, b);
return a * b;
#else
// Generic fallback for other compilers/archs
uint64_t a_lo = (uint32_t)a, a_hi = a >> 32;
uint64_t b_lo = (uint32_t)b, b_hi = b >> 32;
uint64_t p0 = a_lo * b_lo;
uint64_t p1 = a_lo * b_hi;
uint64_t p2 = a_hi * b_lo;
uint64_t p3 = a_hi * b_hi;
uint64_t cy = (uint32_t)(p0 >> 32) + (uint32_t)p1 + (uint32_t)p2;
*high = p3 + (p1 >> 32) + (p2 >> 32) + (cy >> 32);
return (p0 & 0xFFFFFFFF) | (cy << 32);
#endif
}
static inline uint64_t Divide128by64(uint64_t high, uint64_t low, uint64_t divisor, uint64_t* remainder) {
#if defined(__SIZEOF_INT128__)
unsigned __int128 dividend = ((unsigned __int128)high << 64) | low;
*remainder = (uint64_t)(dividend % divisor);
return (uint64_t)(dividend / divisor);
#elif defined(_MSC_VER) && defined(_M_X64)
return _udiv128(high, low, divisor, remainder);
#else
// Software fallback for MSVC ARM64 and others
// This implements long division for 128-bit / 64-bit
if (high < divisor) {
// Common case for many emulators: quotient fits in 64 bits
uint64_t q, r;
// Schoolbook division: split 128-bit into 4x32-bit if necessary,
// but here we use a binary long division for simplicity and correctness.
uint64_t rem = high;
uint64_t quot = 0;
for (int i = 63; i >= 0; i--) {
rem = (rem << 1) | ((low >> i) & 1);
if (rem >= divisor) {
rem -= divisor;
quot |= (1ULL << i);
}
}
*remainder = rem;
return quot;
} else {
// Quotient overflow: high >= divisor means result > 64 bits.
// If your code expects a 64-bit return, this is technically an error state.
// Return max value as a sentinel or handle as needed.
*remainder = 0;
return 0xFFFFFFFFFFFFFFFFULL;
}
#endif
}

View File

@ -341,44 +341,62 @@ inline uint64 _udiv128(uint64 highDividend, uint64 lowDividend, uint64 divisor,
#define FORCE_INLINE inline
#endif
FORCE_INLINE int BSF(uint32 v) // returns index of first bit set, counting from LSB. If v is 0 then result is undefined
FORCE_INLINE int BSF(uint32 v) // returns index of first bit set, counting from LSB. If v is 0 then result is 32
{
#if defined(_MSC_VER)
return _tzcnt_u32(v); // TZCNT requires BMI1. But if not supported it will execute as BSF
#if defined(_M_ARM64) || defined(_M_ARM)
unsigned long index;
if (_BitScanForward(&index, (unsigned long)v))
return (int)index;
return 32;
#else
// This is the x86/x64 specific intrinsic that was causing ARM64 build failures
return (v == 0) ? 32 : (int)_tzcnt_u32(v);
#endif
#elif defined(__GNUC__) || defined(__clang__)
return __builtin_ctz(v);
return v == 0 ? 32 : __builtin_ctz(v);
#else
return std::countr_zero(v);
return (int)std::countr_zero(v);
#endif
}
// On aarch64 we handle some of the x86 intrinsics by implementing them as wrappers
#if defined(__aarch64__)
#if defined(__aarch64__) || defined(_M_ARM64)
inline void _mm_pause()
{
#if defined(_MSC_VER)
__yield();
#else
asm volatile("yield");
#endif
}
inline uint64 __rdtsc()
{
#if defined(_MSC_VER)
return (uint64)_ReadStatusReg(ARM64_CNTVCT_EL0);
#else
uint64 t;
asm volatile("mrs %0, cntvct_el0" : "=r" (t));
return t;
#endif
}
inline void _mm_mfence()
{
asm volatile("" ::: "memory");
std::atomic_thread_fence(std::memory_order_seq_cst);
#if defined(_MSC_VER)
__dmb(_ARM64_BARRIER_ISH); // Inner Shareable Data Memory Barrier
#else
asm volatile("" ::: "memory");
std::atomic_thread_fence(std::memory_order_seq_cst);
#endif
}
inline unsigned char _addcarry_u64(unsigned char carry, unsigned long long a, unsigned long long b, unsigned long long *result)
{
*result = a + b + (unsigned long long)carry;
if (*result < a)
return 1;
return 0;
unsigned __int128 res = (unsigned __int128)a + b + carry;
*result = (unsigned long long)res;
return (res >> 64) ? 1 : 0;
}
#endif

View File

@ -191,6 +191,7 @@ endif()
if(WIN32)
target_link_libraries(CemuWxGui PRIVATE bthprops)
target_compile_options(CemuWxGui PRIVATE /wd4312)
endif()
if(ALLOW_PORTABLE)

View File

@ -13,38 +13,25 @@
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Neutral (Default) (unknown sub-lang: 0x8) resources
// Neutral (Default) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ZZZ)
LANGUAGE LANG_NEUTRAL, 0x8
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
M_WND_ICON128 ICON "resource\\logo_icon.ico"
M_WND_ICON16 ICON "resource\\logo_icon16.ico"
X_INPUT_CONTROLLER ICON "resource\\input\\icons8-game-controller-24.ico"
X_BOX ICON "resource\\icons8_cardboard_box_filled.ico"
X_SETTINGS ICON "resource\\icons8_automatic_26_xQK_icon.ico"
X_GAME_PROFILE ICON "resource\\icons8-compose-filled-50.ico"
X_HOTKEY_SETTINGS ICON "resource\\icons8_hotkeys.ico"
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
@ -61,10 +48,8 @@ BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
@ -73,16 +58,25 @@ END
#define xstr(s) str(s)
#define str(s) #s
#if defined(_M_ARM64)
#define ARCH_DESC " (ARM64)"
#elif defined(_M_X64) || defined(_M_AMD64)
#define ARCH_DESC " (x64)"
#else
#define ARCH_DESC ""
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0
PRODUCTVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0
FILEVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0
PRODUCTVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x0L
// VOS_NT_WINDOWS32 is used for both x64 and ARM64 NT-based systems
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
@ -90,12 +84,12 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "Cemu Wii U emulator"
VALUE "FileDescription", "Cemu Wii U emulator" ARCH_DESC
VALUE "InternalName", "Cemu"
VALUE "LegalCopyright", "Team Cemu"
VALUE "OriginalFilename", "Cemu.exe"
VALUE "ProductName", "Cemu"
VALUE "ProductVersion", xstr(EMULATOR_VERSION_MAJOR) "." xstr(EMULATOR_VERSION_MINOR) "." xstr(EMULATOR_VERSION_PATCH) EMULATOR_VERSION_SUFFIX "\0"
VALUE "ProductVersion", xstr(EMULATOR_VERSION_MAJOR) "." xstr(EMULATOR_VERSION_MINOR) "." xstr(EMULATOR_VERSION_PATCH) EMULATOR_VERSION_SUFFIX "\0"
END
END
BLOCK "VarFileInfo"
@ -104,7 +98,6 @@ BEGIN
END
END
/////////////////////////////////////////////////////////////////////////////
//
// RCDATA
@ -112,10 +105,7 @@ END
IDR_FONTAWESOME RCDATA "resource\\fontawesome-webfont.ttf"
#endif // Neutral (Default) (unknown sub-lang: 0x8) resources
/////////////////////////////////////////////////////////////////////////////
#endif // Neutral resources
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
@ -126,4 +116,3 @@ IDR_FONTAWESOME RCDATA "resource\\fontawesome-webfont.t
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -2,12 +2,16 @@
; Licensed under MPL 2.0 with permission from authors
; Usage:
; get the latest nsis: https://nsis.sourceforge.io/Download
; probably also want vscode extension: https://marketplace.visualstudio.com/items?itemName=idleberg.nsis
; makensis.exe /DPRODUCT_VERSION=2.0 /DARCH=arm64 installer.nsi
; makensis.exe /DPRODUCT_VERSION=2.0 /DARCH=x64 installer.nsi
; Require /DPRODUCT_VERSION for makensis.
!ifndef PRODUCT_VERSION
!error "PRODUCT_VERSION must be defined"
!error "PRODUCT_VERSION must be defined (e.g. /DPRODUCT_VERSION=2.0)"
!endif
; Default to x64 if ARCH is not defined
!ifndef ARCH
!define ARCH "x64"
!endif
ManifestDPIAware true
@ -19,38 +23,33 @@ ManifestDPIAware true
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define BINARY_SOURCE_DIR "..\..\bin"
OutFile "cemu-${PRODUCT_VERSION}-windows-${ARCH}-installer.exe"
Name "${PRODUCT_NAME}"
OutFile "cemu-${PRODUCT_VERSION}-windows-x64-installer.exe"
Name "${PRODUCT_NAME} (${ARCH})"
SetCompressor /SOLID lzma
InstallDir "$LOCALAPPDATA\Cemu"
ShowInstDetails show
ShowUnInstDetails show
!include "MUI2.nsh"
; Custom page plugin
!include "nsDialogs.nsh"
!include "x64.nsh"
; MUI Settings
!define MUI_ICON "logo_icon.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
; License page
!insertmacro MUI_PAGE_LICENSE "..\..\LICENSE.txt"
; Desktop Shortcut page
Page custom desktopShortcutPageCreate desktopShortcutPageLeave
; Directory page
!insertmacro MUI_PAGE_DIRECTORY
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\Cemu.exe"
!insertmacro MUI_PAGE_FINISH
; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES
; Variables
Var DesktopShortcutPageDialog
Var DesktopShortcutCheckbox
Var DesktopShortcut
@ -81,8 +80,19 @@ Var DesktopShortcut
; MUI end ------
Function .onInit
StrCpy $DesktopShortcut 1
; Block installation if trying to install x64 on ARM without emulation,
; or ensure the user is aware.
${If} "${ARCH}" == "arm64"
${Unless} ${IsNativeARM64}
MessageBox MB_OK|MB_ICONEXCLAMATION "This installer is for ARM64 systems. Your CPU does not appear to support it natively."
${EndUnless}
${Else}
${If} ${IsNativeARM64}
DetailPrint "Running x64 installer on ARM64 system via Prism/Emulation."
${EndIf}
${EndIf}
StrCpy $DesktopShortcut 1
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd
@ -97,7 +107,6 @@ Function desktopShortcutPageCreate
${NSD_CreateCheckbox} 0u 0u 100% 12u "Create a desktop shortcut"
Pop $DesktopShortcutCheckbox
${NSD_SetState} $DesktopShortcutCheckbox $DesktopShortcut
nsDialogs::Show
FunctionEnd
@ -106,16 +115,16 @@ Function desktopShortcutPageLeave
FunctionEnd
Section "Base"
; Prevent running the uninstaller of a different arch in the same folder
; without cleaning up first
ExecWait '"$INSTDIR\uninst.exe" /S _?=$INSTDIR'
SectionIn RO
SetOutPath "$INSTDIR"
; The binplaced build output will be included verbatim.
; This will pull files from the directory defined at the top
File /r "${BINARY_SOURCE_DIR}\*"
; Create start menu and desktop shortcuts
CreateShortCut "$SMPROGRAMS\$(^Name).lnk" "$INSTDIR\Cemu.exe"
${If} $DesktopShortcut == 1
CreateShortCut "$DESKTOP\$(^Name).lnk" "$INSTDIR\Cemu.exe"
@ -126,20 +135,19 @@ SectionEnd
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"
WriteRegStr HKCU "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\Cemu.exe"
; Write metadata for add/remove programs applet
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\Cemu.exe"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKCU "${PRODUCT_UNINST_KEY}" "EstimatedSize" "$0"
; File Associations
WriteRegStr HKCU "Software\Classes\.wud" "" "$(^Name)"
WriteRegStr HKCU "Software\Classes\.wux" "" "$(^Name)"
WriteRegStr HKCU "Software\Classes\.wua" "" "$(^Name)"
@ -150,8 +158,6 @@ SectionEnd
Section Uninstall
Delete "$DESKTOP\$(^Name).lnk"
Delete "$SMPROGRAMS\$(^Name).lnk"
; Be a bit careful to not delete files a user may have put into the install directory
Delete "$INSTDIR\Cemu.exe"
Delete "$INSTDIR\uninst.exe"
RMDir /r "$INSTDIR\gameProfiles"
@ -162,9 +168,6 @@ Section Uninstall
DeleteRegKey HKCU "Software\Classes\.wux"
DeleteRegKey HKCU "Software\Classes\.wua"
DeleteRegKey HKCU "Software\Classes\$(^Name)"
DeleteRegKey HKCU "Software\Classes\discord-460807638964371468"
DeleteRegKey HKCU "${PRODUCT_UNINST_KEY}"
DeleteRegKey HKCU "${PRODUCT_DIR_REGKEY}"

View File

@ -67,7 +67,8 @@
"zip"
]
},
"libusb"
"libusb",
"pkgconf"
],
"overrides": [
{