diff options
39 files changed, 1385 insertions, 42 deletions
diff --git a/.gitmodules b/.gitmodules index 9ba8fe207..79028bbb5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,9 @@  [submodule "xbyak"]      path = externals/xbyak      url = https://github.com/herumi/xbyak.git +[submodule "externals/libusb"] +	path = externals/libusb +	url = https://github.com/ameerj/libusb +[submodule "opus"] +	path = externals/opus/opus +	url = https://github.com/xiph/opus.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 73405ce4b..27383bce8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,8 +156,6 @@ macro(yuzu_find_packages)          #"libzip            1.5         libzip/1.5.2@bincrafters/stable"          "lz4               1.8         lz4/1.9.2"          "nlohmann_json     3.7         nlohmann_json/3.7.3" -    # we need to be careful as the version check might be broken https://github.com/xiph/opus/issues/110 -        "opus              1.3         opus/1.3.1"          "ZLIB              1.2         zlib/1.2.11"          "zstd              1.4         zstd/1.4.4"      ) @@ -331,6 +329,12 @@ elseif(SDL2_FOUND)      target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")  endif() +# Ensure libusb is properly configured (based on dolphin libusb include) +find_package(LibUSB) +add_subdirectory(externals/libusb) +set(LIBUSB_LIBRARIES usb) + +  # Prefer the -pthread flag on Linux.  set(THREADS_PREFER_PTHREAD_FLAG ON)  find_package(Threads REQUIRED) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index b80b27605..d1dcc403b 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -91,3 +91,6 @@ if (ENABLE_WEB_SERVICE)      target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)      target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})  endif() + +# Opus +add_subdirectory(opus) diff --git a/externals/libusb b/externals/libusb new file mode 160000 +Subproject 3406d72cda879f8792a88bf5f6bd0b7a65636f7 diff --git a/externals/opus/CMakeLists.txt b/externals/opus/CMakeLists.txt new file mode 100644 index 000000000..94a86551f --- /dev/null +++ b/externals/opus/CMakeLists.txt @@ -0,0 +1,254 @@ +cmake_minimum_required(VERSION 3.8) + +project(opus) + +option(OPUS_STACK_PROTECTOR "Use stack protection" OFF) +option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF) +option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF) +option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF) +option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON) + +include(opus/opus_functions.cmake) + +if(OPUS_STACK_PROTECTOR) +    if(NOT MSVC) # GC on by default on MSVC +        check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong) +    endif() +else() +    if(MSVC) +        check_and_set_flag(BUFFER_SECURITY_CHECK /GS-) +    endif() +endif() + +add_library(opus STATIC +    # CELT sources +    opus/celt/bands.c +    opus/celt/celt.c +    opus/celt/celt_decoder.c +    opus/celt/celt_encoder.c +    opus/celt/celt_lpc.c +    opus/celt/cwrs.c +    opus/celt/entcode.c +    opus/celt/entdec.c +    opus/celt/entenc.c +    opus/celt/kiss_fft.c +    opus/celt/laplace.c +    opus/celt/mathops.c +    opus/celt/mdct.c +    opus/celt/modes.c +    opus/celt/pitch.c +    opus/celt/quant_bands.c +    opus/celt/rate.c +    opus/celt/vq.c + +    # SILK sources +    opus/silk/A2NLSF.c +    opus/silk/CNG.c +    opus/silk/HP_variable_cutoff.c +    opus/silk/LPC_analysis_filter.c +    opus/silk/LPC_fit.c +    opus/silk/LPC_inv_pred_gain.c +    opus/silk/LP_variable_cutoff.c +    opus/silk/NLSF2A.c +    opus/silk/NLSF_VQ.c +    opus/silk/NLSF_VQ_weights_laroia.c +    opus/silk/NLSF_decode.c +    opus/silk/NLSF_del_dec_quant.c +    opus/silk/NLSF_encode.c +    opus/silk/NLSF_stabilize.c +    opus/silk/NLSF_unpack.c +    opus/silk/NSQ.c +    opus/silk/NSQ_del_dec.c +    opus/silk/PLC.c +    opus/silk/VAD.c +    opus/silk/VQ_WMat_EC.c +    opus/silk/ana_filt_bank_1.c +    opus/silk/biquad_alt.c +    opus/silk/bwexpander.c +    opus/silk/bwexpander_32.c +    opus/silk/check_control_input.c +    opus/silk/code_signs.c +    opus/silk/control_SNR.c +    opus/silk/control_audio_bandwidth.c +    opus/silk/control_codec.c +    opus/silk/dec_API.c +    opus/silk/decode_core.c +    opus/silk/decode_frame.c +    opus/silk/decode_indices.c +    opus/silk/decode_parameters.c +    opus/silk/decode_pitch.c +    opus/silk/decode_pulses.c +    opus/silk/decoder_set_fs.c +    opus/silk/enc_API.c +    opus/silk/encode_indices.c +    opus/silk/encode_pulses.c +    opus/silk/gain_quant.c +    opus/silk/init_decoder.c +    opus/silk/init_encoder.c +    opus/silk/inner_prod_aligned.c +    opus/silk/interpolate.c +    opus/silk/lin2log.c +    opus/silk/log2lin.c +    opus/silk/pitch_est_tables.c +    opus/silk/process_NLSFs.c +    opus/silk/quant_LTP_gains.c +    opus/silk/resampler.c +    opus/silk/resampler_down2.c +    opus/silk/resampler_down2_3.c +    opus/silk/resampler_private_AR2.c +    opus/silk/resampler_private_IIR_FIR.c +    opus/silk/resampler_private_down_FIR.c +    opus/silk/resampler_private_up2_HQ.c +    opus/silk/resampler_rom.c +    opus/silk/shell_coder.c +    opus/silk/sigm_Q15.c +    opus/silk/sort.c +    opus/silk/stereo_LR_to_MS.c +    opus/silk/stereo_MS_to_LR.c +    opus/silk/stereo_decode_pred.c +    opus/silk/stereo_encode_pred.c +    opus/silk/stereo_find_predictor.c +    opus/silk/stereo_quant_pred.c +    opus/silk/sum_sqr_shift.c +    opus/silk/table_LSF_cos.c +    opus/silk/tables_LTP.c +    opus/silk/tables_NLSF_CB_NB_MB.c +    opus/silk/tables_NLSF_CB_WB.c +    opus/silk/tables_gain.c +    opus/silk/tables_other.c +    opus/silk/tables_pitch_lag.c +    opus/silk/tables_pulses_per_block.c + +    # Opus sources +    opus/src/analysis.c +    opus/src/mapping_matrix.c +    opus/src/mlp.c +    opus/src/mlp_data.c +    opus/src/opus.c +    opus/src/opus_decoder.c +    opus/src/opus_encoder.c +    opus/src/opus_multistream.c +    opus/src/opus_multistream_decoder.c +    opus/src/opus_multistream_encoder.c +    opus/src/opus_projection_decoder.c +    opus/src/opus_projection_encoder.c +    opus/src/repacketizer.c +) + +if (DEBUG) +    target_sources(opus PRIVATE opus/silk/debug.c) +endif() + +if (OPUS_FIXED_POINT) +    target_sources(opus PRIVATE +        opus/silk/fixed/LTP_analysis_filter_FIX.c +        opus/silk/fixed/LTP_scale_ctrl_FIX.c +        opus/silk/fixed/apply_sine_window_FIX.c +        opus/silk/fixed/autocorr_FIX.c +        opus/silk/fixed/burg_modified_FIX.c +        opus/silk/fixed/corrMatrix_FIX.c +        opus/silk/fixed/encode_frame_FIX.c +        opus/silk/fixed/find_LPC_FIX.c +        opus/silk/fixed/find_LTP_FIX.c +        opus/silk/fixed/find_pitch_lags_FIX.c +        opus/silk/fixed/find_pred_coefs_FIX.c +        opus/silk/fixed/k2a_FIX.c +        opus/silk/fixed/k2a_Q16_FIX.c +        opus/silk/fixed/noise_shape_analysis_FIX.c +        opus/silk/fixed/pitch_analysis_core_FIX.c +        opus/silk/fixed/prefilter_FIX.c +        opus/silk/fixed/process_gains_FIX.c +        opus/silk/fixed/regularize_correlations_FIX.c +        opus/silk/fixed/residual_energy16_FIX.c +        opus/silk/fixed/residual_energy_FIX.c +        opus/silk/fixed/schur64_FIX.c +        opus/silk/fixed/schur_FIX.c +        opus/silk/fixed/solve_LS_FIX.c +        opus/silk/fixed/vector_ops_FIX.c +        opus/silk/fixed/warped_autocorrelation_FIX.c +    ) +else() +    target_sources(opus PRIVATE +        opus/silk/float/LPC_analysis_filter_FLP.c +        opus/silk/float/LPC_inv_pred_gain_FLP.c +        opus/silk/float/LTP_analysis_filter_FLP.c +        opus/silk/float/LTP_scale_ctrl_FLP.c +        opus/silk/float/apply_sine_window_FLP.c +        opus/silk/float/autocorrelation_FLP.c +        opus/silk/float/burg_modified_FLP.c +        opus/silk/float/bwexpander_FLP.c +        opus/silk/float/corrMatrix_FLP.c +        opus/silk/float/encode_frame_FLP.c +        opus/silk/float/energy_FLP.c +        opus/silk/float/find_LPC_FLP.c +        opus/silk/float/find_LTP_FLP.c +        opus/silk/float/find_pitch_lags_FLP.c +        opus/silk/float/find_pred_coefs_FLP.c +        opus/silk/float/inner_product_FLP.c +        opus/silk/float/k2a_FLP.c +        opus/silk/float/noise_shape_analysis_FLP.c +        opus/silk/float/pitch_analysis_core_FLP.c +        opus/silk/float/process_gains_FLP.c +        opus/silk/float/regularize_correlations_FLP.c +        opus/silk/float/residual_energy_FLP.c +        opus/silk/float/scale_copy_vector_FLP.c +        opus/silk/float/scale_vector_FLP.c +        opus/silk/float/schur_FLP.c +        opus/silk/float/sort_FLP.c +        opus/silk/float/warped_autocorrelation_FLP.c +        opus/silk/float/wrappers_FLP.c +    ) +endif() + +target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING) + +if(NOT MSVC) +    if(MINGW) +        target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0) +    else() +        target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2) +    endif() +endif() + +# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99 +# variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack +# allocation If none is defined, then the fallback is a non-threadsafe global +# array +if(OPUS_USE_ALLOCA OR MSVC) +    target_compile_definitions(opus PRIVATE USE_ALLOCA) +else() +    target_compile_definitions(opus PRIVATE VAR_ARRAYS) +endif() + +if(OPUS_CUSTOM_MODES) +    target_compile_definitions(opus PRIVATE CUSTOM_MODES) +endif() + +if(NOT OPUS_ENABLE_FLOAT_API) +    target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API) +endif() + +target_compile_definitions(opus +PUBLIC +    -DOPUS_VERSION="\\"1.3.1\\"" + +PRIVATE +    # Use C99 intrinsics to speed up float-to-int conversion +    HAVE_LRINTF +) + +if (FIXED_POINT) +    target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API) +endif() + +target_include_directories(opus +PUBLIC +    opus/include + +PRIVATE +    opus/celt +    opus/silk +    opus/silk/fixed +    opus/silk/float +    opus/src +) diff --git a/externals/opus/opus b/externals/opus/opus new file mode 160000 +Subproject ad8fe90db79b7d2a135e3dfd2ed6631b0c5662a diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f87d67db5..d1f173f42 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -614,7 +614,7 @@ endif()  create_target_directory_groups(core)  target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus unicorn zip) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip)  if (YUZU_ENABLE_BOXCAT)      target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT) diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index bf3434e1c..9269a73f2 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -223,13 +223,16 @@ bool operator<(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {  class KeyManager {  public: -    static KeyManager& instance() { +    static KeyManager& Instance() {          static KeyManager instance;          return instance;      } -    KeyManager(KeyManager const&) = delete; -    void operator=(KeyManager const&) = delete; +    KeyManager(const KeyManager&) = delete; +    KeyManager& operator=(const KeyManager&) = delete; + +    KeyManager(KeyManager&&) = delete; +    KeyManager& operator=(KeyManager&&) = delete;      bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;      bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 464ca6503..8935a62c3 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -79,7 +79,7 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {  }  VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    auto& keys = Core::Crypto::KeyManager::Instance();      Core::Crypto::PartitionDataManager pdm{          Core::System::GetInstance().GetFilesystem()->OpenDirectory(              FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index a09d504ae..e1b136426 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -140,6 +140,6 @@ private:      u64 update_normal_partition_end; -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();  };  } // namespace FileSys diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index e6c887b32..d25cbcf91 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -158,7 +158,7 @@ private:      bool encrypted = false;      bool is_update = false; -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();  };  } // namespace FileSys diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 4b2fb08cb..f339cd17b 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -88,7 +88,7 @@ public:  protected:      // A single instance of KeyManager to be used by GetEntry() -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();  };  class PlaceholderCache { diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index c35a0d10b..175a8266a 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -21,7 +21,7 @@  namespace FileSys {  namespace {  void SetTicketKeys(const std::vector<VirtualFile>& files) { -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    auto& keys = Core::Crypto::KeyManager::Instance();      for (const auto& ticket_file : files) {          if (ticket_file == nullptr) { diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index bd577f6e5..cf89de6a9 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -73,7 +73,7 @@ private:      std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;      std::vector<VirtualFile> ticket_files; -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();      VirtualFile romfs;      VirtualDir exefs; diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 95da907bc..563531bb6 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -62,6 +62,6 @@ private:      VirtualFile dec_file; -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();  };  } // namespace FileSys diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 94d8c1fc6..8ac856ec3 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -776,6 +776,15 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {      rb.Push(RESULT_SUCCESS);  } +void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { +    LOG_WARNING(Service_ACC, "(STUBBED) called"); + +    // TODO(ogniK): Handle open contexts +    ctx.WriteBuffer(profile_manager->GetOpenUsers()); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(RESULT_SUCCESS); +} +  void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service_ACC, "called");      // A u8 is passed into this function which we can safely ignore. It's to determine if we have diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 74ca39d6e..d4c6395c6 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -34,6 +34,7 @@ public:          void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);          void GetProfileEditor(Kernel::HLERequestContext& ctx);          void ListQualifiedUsers(Kernel::HLERequestContext& ctx); +        void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);      private:          ResultCode InitializeApplicationInfoBase(); diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 85620bde3..d2bb8c2c8 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -20,7 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p          {6, nullptr, "GetProfileDigest"}, // 3.0.0+          {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},          {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, -        {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 +        {60, &ACC_SU::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0          {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+          {100, nullptr, "GetUserRegistrationNotifier"},          {101, nullptr, "GetUserStateChangeNotifier"}, diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 49f6e20f1..cb44e06b7 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -20,7 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p          {6, nullptr, "GetProfileDigest"}, // 3.0.0+          {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},          {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, -        {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 +        {60, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0          {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+          {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},          {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, @@ -30,7 +30,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p          {111, nullptr, "ClearSaveDataThumbnail"},          {120, nullptr, "CreateGuestLoginRequest"},          {130, nullptr, "LoadOpenContext"}, // 5.0.0+ -        {131, nullptr, "ListOpenContextStoredUsers"}, // 6.0.0+ +        {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+          {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+          {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+          {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, // 6.0.0+ diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index f47004f84..a4aa5316a 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -20,7 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p          {6, nullptr, "GetProfileDigest"}, // 3.0.0+          {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},          {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, -        {60, nullptr, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0 +        {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 5.0.0 - 5.1.0          {99, nullptr, "DebugActivateOpenContextRetention"}, // 6.0.0+          {100, nullptr, "GetUserRegistrationNotifier"},          {101, nullptr, "GetUserStateChangeNotifier"}, diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 20f366635..1bb544dd8 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -841,7 +841,7 @@ public:              {110, nullptr, "NeedsToExitProcess"},              {120, nullptr, "GetLibraryAppletInfo"},              {150, nullptr, "RequestForAppletToGetForeground"}, -            {160, nullptr, "GetIndirectLayerConsumerHandle"}, +            {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},          };          // clang-format on @@ -960,6 +960,18 @@ private:          rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());      } +    void GetIndirectLayerConsumerHandle(Kernel::HLERequestContext& ctx) { +        LOG_WARNING(Service_AM, "(STUBBED) called"); + +        // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is +        // actually used anywhere +        constexpr u64 handle = 0xdeadbeef; + +        IPC::ResponseBuilder rb{ctx, 4}; +        rb.Push(RESULT_SUCCESS); +        rb.Push(handle); +    } +      std::shared_ptr<Applets::Applet> applet;  }; diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index da6b74a22..a41c73c48 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -302,7 +302,7 @@ private:          rb.Push<u64>(write_size);      } -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();  };  void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index f3b4b286c..e5cfd2101 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -3,6 +3,7 @@  // Refer to the license.txt file included.  #include <algorithm> +#include <array>  #include <chrono>  #include "common/logging/log.h"  #include "core/hle/ipc_helpers.h" @@ -31,6 +32,44 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{      LanguageCode::ZH_HANT,  }}; +enum class KeyboardLayout : u64 { +    Japanese = 0, +    EnglishUs = 1, +    EnglishUsInternational = 2, +    EnglishUk = 3, +    French = 4, +    FrenchCa = 5, +    Spanish = 6, +    SpanishLatin = 7, +    German = 8, +    Italian = 9, +    Portuguese = 10, +    Russian = 11, +    Korean = 12, +    ChineseSimplified = 13, +    ChineseTraditional = 14, +}; + +constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 17> language_to_layout{{ +    {LanguageCode::JA, KeyboardLayout::Japanese}, +    {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, +    {LanguageCode::FR, KeyboardLayout::French}, +    {LanguageCode::DE, KeyboardLayout::German}, +    {LanguageCode::IT, KeyboardLayout::Italian}, +    {LanguageCode::ES, KeyboardLayout::Spanish}, +    {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified}, +    {LanguageCode::KO, KeyboardLayout::Korean}, +    {LanguageCode::NL, KeyboardLayout::EnglishUsInternational}, +    {LanguageCode::PT, KeyboardLayout::Portuguese}, +    {LanguageCode::RU, KeyboardLayout::Russian}, +    {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional}, +    {LanguageCode::EN_GB, KeyboardLayout::EnglishUk}, +    {LanguageCode::FR_CA, KeyboardLayout::FrenchCa}, +    {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, +    {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, +    {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, +}}; +  constexpr std::size_t pre4_0_0_max_entries = 15;  constexpr std::size_t post4_0_0_max_entries = 17; @@ -50,6 +89,25 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m      ctx.WriteBuffer(available_language_codes.data(), copy_size);      PushResponseLanguageCode(ctx, copy_amount);  } + +void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { +    const auto language_code = available_language_codes[Settings::values.language_index]; +    const auto key_code = +        std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), +                     [=](const auto& element) { return element.first == language_code; }); +    KeyboardLayout layout = KeyboardLayout::EnglishUs; +    if (key_code == language_to_layout.cend()) { +        LOG_ERROR(Service_SET, +                  "Could not find keyboard layout for language index {}, defaulting to English us", +                  Settings::values.language_index); +    } else { +        layout = key_code->second; +    } + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(RESULT_SUCCESS); +    ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); +}  } // Anonymous namespace  LanguageCode GetLanguageCodeFromIndex(std::size_t index) { @@ -120,6 +178,16 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {      rb.Push(Settings::values.region_index);  } +void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) { +    LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); +    GetKeyCodeMapImpl(ctx); +} + +void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) { +    LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); +    GetKeyCodeMapImpl(ctx); +} +  SET::SET() : ServiceFramework("set") {      // clang-format off      static const FunctionInfo functions[] = { @@ -130,9 +198,9 @@ SET::SET() : ServiceFramework("set") {          {4, &SET::GetRegionCode, "GetRegionCode"},          {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},          {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, -        {7, nullptr, "GetKeyCodeMap"}, +        {7, &SET::GetKeyCodeMap, "GetKeyCodeMap"},          {8, &SET::GetQuestFlag, "GetQuestFlag"}, -        {9, nullptr, "GetKeyCodeMap2"}, +        {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},          {10, nullptr, "GetFirmwareVersionForDebug"},      };      // clang-format on diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 6084b345d..8ac9c169d 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -44,6 +44,8 @@ private:      void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);      void GetQuestFlag(Kernel::HLERequestContext& ctx);      void GetRegionCode(Kernel::HLERequestContext& ctx); +    void GetKeyCodeMap(Kernel::HLERequestContext& ctx); +    void GetKeyCodeMap2(Kernel::HLERequestContext& ctx);  };  } // namespace Service::Set diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index a9c2392b1..3bd76dd23 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -7,6 +7,10 @@ add_library(input_common STATIC      main.h      motion_emu.cpp      motion_emu.h +    gcadapter/gc_adapter.cpp +    gcadapter/gc_adapter.h +    gcadapter/gc_poller.cpp +    gcadapter/gc_poller.h      sdl/sdl.cpp      sdl/sdl.h      udp/client.cpp @@ -26,5 +30,7 @@ if(SDL2_FOUND)      target_compile_definitions(input_common PRIVATE HAVE_SDL2)  endif() +target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES}) +  create_target_directory_groups(input_common)  target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp new file mode 100644 index 000000000..b39d2a3fb --- /dev/null +++ b/src/input_common/gcadapter/gc_adapter.cpp @@ -0,0 +1,379 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <chrono> +#include <thread> +#include "common/logging/log.h" +#include "input_common/gcadapter/gc_adapter.h" + +namespace GCAdapter { + +/// Used to loop through and assign button in poller +constexpr std::array<PadButton, 12> PadButtonArray{ +    PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN, +    PadButton::PAD_BUTTON_UP,   PadButton::PAD_TRIGGER_Z,    PadButton::PAD_TRIGGER_R, +    PadButton::PAD_TRIGGER_L,   PadButton::PAD_BUTTON_A,     PadButton::PAD_BUTTON_B, +    PadButton::PAD_BUTTON_X,    PadButton::PAD_BUTTON_Y,     PadButton::PAD_BUTTON_START, +}; + +Adapter::Adapter() { +    if (usb_adapter_handle != nullptr) { +        return; +    } +    LOG_INFO(Input, "GC Adapter Initialization started"); + +    current_status = NO_ADAPTER_DETECTED; +    libusb_init(&libusb_ctx); + +    StartScanThread(); +} + +GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) { +    GCPadStatus pad = {}; +    bool get_origin = false; + +    ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4); +    if (type != ControllerTypes::None) { +        get_origin = true; +    } + +    adapter_controllers_status[port] = type; + +    static constexpr std::array<PadButton, 8> b1_buttons{ +        PadButton::PAD_BUTTON_A,    PadButton::PAD_BUTTON_B,    PadButton::PAD_BUTTON_X, +        PadButton::PAD_BUTTON_Y,    PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, +        PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP, +    }; + +    static constexpr std::array<PadButton, 4> b2_buttons{ +        PadButton::PAD_BUTTON_START, +        PadButton::PAD_TRIGGER_Z, +        PadButton::PAD_TRIGGER_R, +        PadButton::PAD_TRIGGER_L, +    }; + +    if (adapter_controllers_status[port] != ControllerTypes::None) { +        const u8 b1 = adapter_payload[1 + (9 * port) + 1]; +        const u8 b2 = adapter_payload[1 + (9 * port) + 2]; + +        for (std::size_t i = 0; i < b1_buttons.size(); ++i) { +            if ((b1 & (1U << i)) != 0) { +                pad.button |= static_cast<u16>(b1_buttons[i]); +            } +        } + +        for (std::size_t j = 0; j < b2_buttons.size(); ++j) { +            if ((b2 & (1U << j)) != 0) { +                pad.button |= static_cast<u16>(b2_buttons[j]); +            } +        } + +        if (get_origin) { +            pad.button |= PAD_GET_ORIGIN; +        } + +        pad.stick_x = adapter_payload[1 + (9 * port) + 3]; +        pad.stick_y = adapter_payload[1 + (9 * port) + 4]; +        pad.substick_x = adapter_payload[1 + (9 * port) + 5]; +        pad.substick_y = adapter_payload[1 + (9 * port) + 6]; +        pad.trigger_left = adapter_payload[1 + (9 * port) + 7]; +        pad.trigger_right = adapter_payload[1 + (9 * port) + 8]; +    } +    return pad; +} + +void Adapter::PadToState(const GCPadStatus& pad, GCState& state) { +    for (const auto& button : PadButtonArray) { +        const u16 button_value = static_cast<u16>(button); +        state.buttons.insert_or_assign(button_value, pad.button & button_value); +    } + +    state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x); +    state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y); +    state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x); +    state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y); +    state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left); +    state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right); +} + +void Adapter::Read() { +    LOG_DEBUG(Input, "GC Adapter Read() thread started"); + +    int payload_size_in, payload_size_copy; +    std::array<u8, 37> adapter_payload; +    std::array<u8, 37> adapter_payload_copy; +    std::array<GCPadStatus, 4> pads; + +    while (adapter_thread_running) { +        libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(), +                                  sizeof(adapter_payload), &payload_size_in, 16); +        payload_size_copy = 0; +        // this mutex might be redundant? +        { +            std::lock_guard<std::mutex> lk(s_mutex); +            std::copy(std::begin(adapter_payload), std::end(adapter_payload), +                      std::begin(adapter_payload_copy)); +            payload_size_copy = payload_size_in; +        } + +        if (payload_size_copy != sizeof(adapter_payload_copy) || +            adapter_payload_copy[0] != LIBUSB_DT_HID) { +            LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy, +                      adapter_payload_copy[0]); +            adapter_thread_running = false; // error reading from adapter, stop reading. +            break; +        } +        for (std::size_t port = 0; port < pads.size(); ++port) { +            pads[port] = GetPadStatus(port, adapter_payload_copy); +            if (DeviceConnected(port) && configuring) { +                if (pads[port].button != PAD_GET_ORIGIN) { +                    pad_queue[port].Push(pads[port]); +                } + +                // Accounting for a threshold here because of some controller variance +                if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD || +                    pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) { +                    pads[port].axis = GCAdapter::PadAxes::StickX; +                    pads[port].axis_value = pads[port].stick_x; +                    pad_queue[port].Push(pads[port]); +                } +                if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD || +                    pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) { +                    pads[port].axis = GCAdapter::PadAxes::StickY; +                    pads[port].axis_value = pads[port].stick_y; +                    pad_queue[port].Push(pads[port]); +                } +                if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD || +                    pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) { +                    pads[port].axis = GCAdapter::PadAxes::SubstickX; +                    pads[port].axis_value = pads[port].substick_x; +                    pad_queue[port].Push(pads[port]); +                } +                if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD || +                    pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) { +                    pads[port].axis = GCAdapter::PadAxes::SubstickY; +                    pads[port].axis_value = pads[port].substick_y; +                    pad_queue[port].Push(pads[port]); +                } +                if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) { +                    pads[port].axis = GCAdapter::PadAxes::TriggerLeft; +                    pads[port].axis_value = pads[port].trigger_left; +                    pad_queue[port].Push(pads[port]); +                } +                if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) { +                    pads[port].axis = GCAdapter::PadAxes::TriggerRight; +                    pads[port].axis_value = pads[port].trigger_right; +                    pad_queue[port].Push(pads[port]); +                } +            } +            PadToState(pads[port], state[port]); +        } +        std::this_thread::yield(); +    } +} + +void Adapter::ScanThreadFunc() { +    LOG_INFO(Input, "GC Adapter scanning thread started"); + +    while (detect_thread_running) { +        if (usb_adapter_handle == nullptr) { +            std::lock_guard<std::mutex> lk(initialization_mutex); +            Setup(); +        } +        std::this_thread::sleep_for(std::chrono::milliseconds(500)); +    } +} + +void Adapter::StartScanThread() { +    if (detect_thread_running) { +        return; +    } +    if (!libusb_ctx) { +        return; +    } + +    detect_thread_running = true; +    detect_thread = std::thread([=] { ScanThreadFunc(); }); +} + +void Adapter::StopScanThread() { +    detect_thread_running = false; +    detect_thread.join(); +} + +void Adapter::Setup() { +    // Reset the error status in case the adapter gets unplugged +    if (current_status < 0) { +        current_status = NO_ADAPTER_DETECTED; +    } + +    adapter_controllers_status.fill(ControllerTypes::None); + +    // pointer to list of connected usb devices +    libusb_device** devices; + +    // populate the list of devices, get the count +    const std::size_t device_count = libusb_get_device_list(libusb_ctx, &devices); + +    for (std::size_t index = 0; index < device_count; ++index) { +        if (CheckDeviceAccess(devices[index])) { +            // GC Adapter found and accessible, registering it +            GetGCEndpoint(devices[index]); +            break; +        } +    } +} + +bool Adapter::CheckDeviceAccess(libusb_device* device) { +    libusb_device_descriptor desc; +    const int get_descriptor_error = libusb_get_device_descriptor(device, &desc); +    if (get_descriptor_error) { +        // could not acquire the descriptor, no point in trying to use it. +        LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}", +                  get_descriptor_error); +        return false; +    } + +    if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) { +        // This isn't the device we are looking for. +        return false; +    } +    const int open_error = libusb_open(device, &usb_adapter_handle); + +    if (open_error == LIBUSB_ERROR_ACCESS) { +        LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.", +                  desc.idVendor, desc.idProduct); +        return false; +    } +    if (open_error) { +        LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error); +        return false; +    } + +    int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0); +    if (kernel_driver_error == 1) { +        kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0); +        if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { +            LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}", +                      kernel_driver_error); +        } +    } + +    if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) { +        libusb_close(usb_adapter_handle); +        usb_adapter_handle = nullptr; +        return false; +    } + +    const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0); +    if (interface_claim_error) { +        LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error); +        libusb_close(usb_adapter_handle); +        usb_adapter_handle = nullptr; +        return false; +    } + +    return true; +} + +void Adapter::GetGCEndpoint(libusb_device* device) { +    libusb_config_descriptor* config = nullptr; +    libusb_get_config_descriptor(device, 0, &config); +    for (u8 ic = 0; ic < config->bNumInterfaces; ic++) { +        const libusb_interface* interfaceContainer = &config->interface[ic]; +        for (int i = 0; i < interfaceContainer->num_altsetting; i++) { +            const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i]; +            for (u8 e = 0; e < interface->bNumEndpoints; e++) { +                const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e]; +                if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) { +                    input_endpoint = endpoint->bEndpointAddress; +                } else { +                    output_endpoint = endpoint->bEndpointAddress; +                } +            } +        } +    } +    // This transfer seems to be responsible for clearing the state of the adapter +    // Used to clear the "busy" state of when the device is unexpectedly unplugged +    unsigned char clear_payload = 0x13; +    libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload, +                              sizeof(clear_payload), nullptr, 16); + +    adapter_thread_running = true; +    current_status = ADAPTER_DETECTED; +    adapter_input_thread = std::thread([=] { Read(); }); // Read input +} + +Adapter::~Adapter() { +    StopScanThread(); +    Reset(); +} + +void Adapter::Reset() { +    std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock); +    if (!lock.try_lock()) { +        return; +    } +    if (current_status != ADAPTER_DETECTED) { +        return; +    } + +    if (adapter_thread_running) { +        adapter_thread_running = false; +    } +    adapter_input_thread.join(); + +    adapter_controllers_status.fill(ControllerTypes::None); +    current_status = NO_ADAPTER_DETECTED; + +    if (usb_adapter_handle) { +        libusb_release_interface(usb_adapter_handle, 1); +        libusb_close(usb_adapter_handle); +        usb_adapter_handle = nullptr; +    } + +    if (libusb_ctx) { +        libusb_exit(libusb_ctx); +    } +} + +bool Adapter::DeviceConnected(int port) { +    return adapter_controllers_status[port] != ControllerTypes::None; +} + +void Adapter::ResetDeviceType(int port) { +    adapter_controllers_status[port] = ControllerTypes::None; +} + +void Adapter::BeginConfiguration() { +    for (auto& pq : pad_queue) { +        pq.Clear(); +    } +    configuring = true; +} + +void Adapter::EndConfiguration() { +    for (auto& pq : pad_queue) { +        pq.Clear(); +    } +    configuring = false; +} + +std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() { +    return pad_queue; +} + +const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const { +    return pad_queue; +} + +std::array<GCState, 4>& Adapter::GetPadState() { +    return state; +} + +const std::array<GCState, 4>& Adapter::GetPadState() const { +    return state; +} + +} // namespace GCAdapter diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h new file mode 100644 index 000000000..0ea6263eb --- /dev/null +++ b/src/input_common/gcadapter/gc_adapter.h @@ -0,0 +1,160 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once +#include <algorithm> +#include <functional> +#include <mutex> +#include <thread> +#include <libusb.h> +#include "common/common_types.h" +#include "common/threadsafe_queue.h" + +namespace GCAdapter { + +enum { +    PAD_USE_ORIGIN = 0x0080, +    PAD_GET_ORIGIN = 0x2000, +    PAD_ERR_STATUS = 0x8000, +}; + +enum class PadButton { +    PAD_BUTTON_LEFT = 0x0001, +    PAD_BUTTON_RIGHT = 0x0002, +    PAD_BUTTON_DOWN = 0x0004, +    PAD_BUTTON_UP = 0x0008, +    PAD_TRIGGER_Z = 0x0010, +    PAD_TRIGGER_R = 0x0020, +    PAD_TRIGGER_L = 0x0040, +    PAD_BUTTON_A = 0x0100, +    PAD_BUTTON_B = 0x0200, +    PAD_BUTTON_X = 0x0400, +    PAD_BUTTON_Y = 0x0800, +    PAD_BUTTON_START = 0x1000, +    // Below is for compatibility with "AxisButton" type +    PAD_STICK = 0x2000, +}; + +extern const std::array<PadButton, 12> PadButtonArray; + +enum class PadAxes : u8 { +    StickX, +    StickY, +    SubstickX, +    SubstickY, +    TriggerLeft, +    TriggerRight, +    Undefined, +}; + +struct GCPadStatus { +    u16 button{};       // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits +    u8 stick_x{};       // 0 <= stick_x       <= 255 +    u8 stick_y{};       // 0 <= stick_y       <= 255 +    u8 substick_x{};    // 0 <= substick_x    <= 255 +    u8 substick_y{};    // 0 <= substick_y    <= 255 +    u8 trigger_left{};  // 0 <= trigger_left  <= 255 +    u8 trigger_right{}; // 0 <= trigger_right <= 255 + +    static constexpr u8 MAIN_STICK_CENTER_X = 0x80; +    static constexpr u8 MAIN_STICK_CENTER_Y = 0x80; +    static constexpr u8 MAIN_STICK_RADIUS = 0x7f; +    static constexpr u8 C_STICK_CENTER_X = 0x80; +    static constexpr u8 C_STICK_CENTER_Y = 0x80; +    static constexpr u8 C_STICK_RADIUS = 0x7f; +    static constexpr u8 THRESHOLD = 10; + +    // 256/4, at least a quarter press to count as a press. For polling mostly +    static constexpr u8 TRIGGER_THRESHOLD = 64; + +    u8 port{}; +    PadAxes axis{PadAxes::Undefined}; +    u8 axis_value{255}; +}; + +struct GCState { +    std::unordered_map<int, bool> buttons; +    std::unordered_map<int, u16> axes; +}; + +enum class ControllerTypes { None, Wired, Wireless }; + +enum { +    NO_ADAPTER_DETECTED = 0, +    ADAPTER_DETECTED = 1, +}; + +class Adapter { +public: +    /// Initialize the GC Adapter capture and read sequence +    Adapter(); + +    /// Close the adapter read thread and release the adapter +    ~Adapter(); +    /// Used for polling +    void BeginConfiguration(); +    void EndConfiguration(); + +    std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); +    const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; + +    std::array<GCState, 4>& GetPadState(); +    const std::array<GCState, 4>& GetPadState() const; + +private: +    GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload); + +    void PadToState(const GCPadStatus& pad, GCState& state); + +    void Read(); +    void ScanThreadFunc(); +    /// Begin scanning for the GC Adapter. +    void StartScanThread(); + +    /// Stop scanning for the adapter +    void StopScanThread(); + +    /// Returns true if there is a device connected to port +    bool DeviceConnected(int port); + +    /// Resets status of device connected to port +    void ResetDeviceType(int port); + +    /// Returns true if we successfully gain access to GC Adapter +    bool CheckDeviceAccess(libusb_device* device); + +    /// Captures GC Adapter endpoint address, +    void GetGCEndpoint(libusb_device* device); + +    /// For shutting down, clear all data, join all threads, release usb +    void Reset(); + +    /// For use in initialization, querying devices to find the adapter +    void Setup(); + +    int current_status = NO_ADAPTER_DETECTED; +    libusb_device_handle* usb_adapter_handle = nullptr; +    std::array<ControllerTypes, 4> adapter_controllers_status{}; + +    std::mutex s_mutex; + +    std::thread adapter_input_thread; +    bool adapter_thread_running; + +    std::mutex initialization_mutex; +    std::thread detect_thread; +    bool detect_thread_running = false; + +    libusb_context* libusb_ctx; + +    u8 input_endpoint = 0; +    u8 output_endpoint = 0; + +    bool configuring = false; + +    std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue; +    std::array<GCState, 4> state; +}; + +} // namespace GCAdapter diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp new file mode 100644 index 000000000..385ce8430 --- /dev/null +++ b/src/input_common/gcadapter/gc_poller.cpp @@ -0,0 +1,272 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <atomic> +#include <list> +#include <mutex> +#include <utility> +#include "common/threadsafe_queue.h" +#include "input_common/gcadapter/gc_adapter.h" +#include "input_common/gcadapter/gc_poller.h" + +namespace InputCommon { + +class GCButton final : public Input::ButtonDevice { +public: +    explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter) +        : port(port_), button(button_), gcadapter(adapter) {} + +    ~GCButton() override; + +    bool GetStatus() const override { +        return gcadapter->GetPadState()[port].buttons.at(button); +    } + +private: +    const int port; +    const int button; +    GCAdapter::Adapter* gcadapter; +}; + +class GCAxisButton final : public Input::ButtonDevice { +public: +    explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_, +                          GCAdapter::Adapter* adapter) +        : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), +          gcadapter(adapter) { +        // L/R triggers range is only in positive direction beginning near 0 +        // 0.0 threshold equates to near half trigger press, but threshold accounts for variability. +        if (axis > 3) { +            threshold *= -0.5; +        } +    } + +    bool GetStatus() const override { +        const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f; +        if (trigger_if_greater) { +            // TODO: Might be worthwile to set a slider for the trigger threshold. It is currently +            // always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick +            return axis_value > threshold; +        } +        return axis_value < -threshold; +    } + +private: +    const int port; +    const int axis; +    float threshold; +    bool trigger_if_greater; +    GCAdapter::Adapter* gcadapter; +}; + +GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) +    : adapter(std::move(adapter_)) {} + +GCButton::~GCButton() = default; + +std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) { +    const int button_id = params.Get("button", 0); +    const int port = params.Get("port", 0); + +    constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK); + +    // button is not an axis/stick button +    if (button_id != PAD_STICK_ID) { +        auto button = std::make_unique<GCButton>(port, button_id, adapter.get()); +        return std::move(button); +    } + +    // For Axis buttons, used by the binary sticks. +    if (button_id == PAD_STICK_ID) { +        const int axis = params.Get("axis", 0); +        const float threshold = params.Get("threshold", 0.25f); +        const std::string direction_name = params.Get("direction", ""); +        bool trigger_if_greater; +        if (direction_name == "+") { +            trigger_if_greater = true; +        } else if (direction_name == "-") { +            trigger_if_greater = false; +        } else { +            trigger_if_greater = true; +            LOG_ERROR(Input, "Unknown direction {}", direction_name); +        } +        return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater, +                                              adapter.get()); +    } +} + +Common::ParamPackage GCButtonFactory::GetNextInput() { +    Common::ParamPackage params; +    GCAdapter::GCPadStatus pad; +    auto& queue = adapter->GetPadQueue(); +    for (std::size_t port = 0; port < queue.size(); ++port) { +        while (queue[port].Pop(pad)) { +            // This while loop will break on the earliest detected button +            params.Set("engine", "gcpad"); +            params.Set("port", static_cast<int>(port)); +            for (const auto& button : GCAdapter::PadButtonArray) { +                const u16 button_value = static_cast<u16>(button); +                if (pad.button & button_value) { +                    params.Set("button", button_value); +                    break; +                } +            } + +            // For Axis button implementation +            if (pad.axis != GCAdapter::PadAxes::Undefined) { +                params.Set("axis", static_cast<u8>(pad.axis)); +                params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK)); +                if (pad.axis_value > 128) { +                    params.Set("direction", "+"); +                    params.Set("threshold", "0.25"); +                } else { +                    params.Set("direction", "-"); +                    params.Set("threshold", "-0.25"); +                } +                break; +            } +        } +    } +    return params; +} + +void GCButtonFactory::BeginConfiguration() { +    polling = true; +    adapter->BeginConfiguration(); +} + +void GCButtonFactory::EndConfiguration() { +    polling = false; +    adapter->EndConfiguration(); +} + +class GCAnalog final : public Input::AnalogDevice { +public: +    GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter) +        : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {} + +    float GetAxis(int axis) const { +        std::lock_guard lock{mutex}; +        // division is not by a perfect 128 to account for some variance in center location +        // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range +        // [20-230] +        return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f; +    } + +    std::pair<float, float> GetAnalog(int axis_x, int axis_y) const { +        float x = GetAxis(axis_x); +        float y = GetAxis(axis_y); + +        // Make sure the coordinates are in the unit circle, +        // otherwise normalize it. +        float r = x * x + y * y; +        if (r > 1.0f) { +            r = std::sqrt(r); +            x /= r; +            y /= r; +        } + +        return {x, y}; +    } + +    std::tuple<float, float> GetStatus() const override { +        const auto [x, y] = GetAnalog(axis_x, axis_y); +        const float r = std::sqrt((x * x) + (y * y)); +        if (r > deadzone) { +            return {x / r * (r - deadzone) / (1 - deadzone), +                    y / r * (r - deadzone) / (1 - deadzone)}; +        } +        return {0.0f, 0.0f}; +    } + +    bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { +        const auto [x, y] = GetStatus(); +        const float directional_deadzone = 0.4f; +        switch (direction) { +        case Input::AnalogDirection::RIGHT: +            return x > directional_deadzone; +        case Input::AnalogDirection::LEFT: +            return x < -directional_deadzone; +        case Input::AnalogDirection::UP: +            return y > directional_deadzone; +        case Input::AnalogDirection::DOWN: +            return y < -directional_deadzone; +        } +        return false; +    } + +private: +    const int port; +    const int axis_x; +    const int axis_y; +    const float deadzone; +    mutable std::mutex mutex; +    GCAdapter::Adapter* gcadapter; +}; + +/// An analog device factory that creates analog devices from GC Adapter +GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_) +    : adapter(std::move(adapter_)) {} + +/** + * Creates analog device from joystick axes + * @param params contains parameters for creating the device: + *     - "port": the nth gcpad on the adapter + *     - "axis_x": the index of the axis to be bind as x-axis + *     - "axis_y": the index of the axis to be bind as y-axis + */ +std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) { +    const int port = params.Get("port", 0); +    const int axis_x = params.Get("axis_x", 0); +    const int axis_y = params.Get("axis_y", 1); +    const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); + +    return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get()); +} + +void GCAnalogFactory::BeginConfiguration() { +    polling = true; +    adapter->BeginConfiguration(); +} + +void GCAnalogFactory::EndConfiguration() { +    polling = false; +    adapter->EndConfiguration(); +} + +Common::ParamPackage GCAnalogFactory::GetNextInput() { +    GCAdapter::GCPadStatus pad; +    auto& queue = adapter->GetPadQueue(); +    for (std::size_t port = 0; port < queue.size(); ++port) { +        while (queue[port].Pop(pad)) { +            if (pad.axis == GCAdapter::PadAxes::Undefined || +                std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) { +                continue; +            } +            // An analog device needs two axes, so we need to store the axis for later and wait for +            // a second input event. The axes also must be from the same joystick. +            const u8 axis = static_cast<u8>(pad.axis); +            if (analog_x_axis == -1) { +                analog_x_axis = axis; +                controller_number = port; +            } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) { +                analog_y_axis = axis; +            } +        } +    } +    Common::ParamPackage params; +    if (analog_x_axis != -1 && analog_y_axis != -1) { +        params.Set("engine", "gcpad"); +        params.Set("port", controller_number); +        params.Set("axis_x", analog_x_axis); +        params.Set("axis_y", analog_y_axis); +        analog_x_axis = -1; +        analog_y_axis = -1; +        controller_number = -1; +        return params; +    } +    return params; +} + +} // namespace InputCommon diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h new file mode 100644 index 000000000..e96af7d51 --- /dev/null +++ b/src/input_common/gcadapter/gc_poller.h @@ -0,0 +1,67 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/frontend/input.h" +#include "input_common/gcadapter/gc_adapter.h" + +namespace InputCommon { + +/** + * A button device factory representing a gcpad. It receives gcpad events and forward them + * to all button devices it created. + */ +class GCButtonFactory final : public Input::Factory<Input::ButtonDevice> { +public: +    explicit GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); + +    /** +     * Creates a button device from a button press +     * @param params contains parameters for creating the device: +     *     - "code": the code of the key to bind with the button +     */ +    std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; + +    Common::ParamPackage GetNextInput(); + +    /// For device input configuration/polling +    void BeginConfiguration(); +    void EndConfiguration(); + +    bool IsPolling() const { +        return polling; +    } + +private: +    std::shared_ptr<GCAdapter::Adapter> adapter; +    bool polling = false; +}; + +/// An analog device factory that creates analog devices from GC Adapter +class GCAnalogFactory final : public Input::Factory<Input::AnalogDevice> { +public: +    explicit GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_); + +    std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; +    Common::ParamPackage GetNextInput(); + +    /// For device input configuration/polling +    void BeginConfiguration(); +    void EndConfiguration(); + +    bool IsPolling() const { +        return polling; +    } + +private: +    std::shared_ptr<GCAdapter::Adapter> adapter; +    int analog_x_axis = -1; +    int analog_y_axis = -1; +    int controller_number = -1; +    bool polling = false; +}; + +} // namespace InputCommon diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 95e351e24..fd0af1019 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -4,8 +4,11 @@  #include <memory>  #include <thread> +#include <libusb.h>  #include "common/param_package.h"  #include "input_common/analog_from_button.h" +#include "input_common/gcadapter/gc_adapter.h" +#include "input_common/gcadapter/gc_poller.h"  #include "input_common/keyboard.h"  #include "input_common/main.h"  #include "input_common/motion_emu.h" @@ -22,8 +25,16 @@ static std::shared_ptr<MotionEmu> motion_emu;  static std::unique_ptr<SDL::State> sdl;  #endif  static std::unique_ptr<CemuhookUDP::State> udp; +static std::shared_ptr<GCButtonFactory> gcbuttons; +static std::shared_ptr<GCAnalogFactory> gcanalog;  void Init() { +    auto gcadapter = std::make_shared<GCAdapter::Adapter>(); +    gcbuttons = std::make_shared<GCButtonFactory>(gcadapter); +    Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons); +    gcanalog = std::make_shared<GCAnalogFactory>(gcadapter); +    Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog); +      keyboard = std::make_shared<Keyboard>();      Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);      Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", @@ -48,6 +59,11 @@ void Shutdown() {      sdl.reset();  #endif      udp.reset(); +    Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); +    Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); + +    gcbuttons.reset(); +    gcanalog.reset();  }  Keyboard* GetKeyboard() { @@ -58,6 +74,14 @@ MotionEmu* GetMotionEmu() {      return motion_emu.get();  } +GCButtonFactory* GetGCButtons() { +    return gcbuttons.get(); +} + +GCAnalogFactory* GetGCAnalogs() { +    return gcanalog.get(); +} +  std::string GenerateKeyboardParam(int key_code) {      Common::ParamPackage param{          {"engine", "keyboard"}, diff --git a/src/input_common/main.h b/src/input_common/main.h index 77a0ce90b..0e32856f6 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -7,6 +7,7 @@  #include <memory>  #include <string>  #include <vector> +#include "input_common/gcadapter/gc_poller.h"  namespace Common {  class ParamPackage; @@ -30,6 +31,10 @@ class MotionEmu;  /// Gets the motion emulation factory.  MotionEmu* GetMotionEmu(); +GCButtonFactory* GetGCButtons(); + +GCAnalogFactory* GetGCAnalogs(); +  /// Generates a serialized param package for creating a keyboard button device  std::string GenerateKeyboardParam(int key_code); diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index cf8bdd021..c6479af9f 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -322,8 +322,7 @@ protected:      }  private: -    MapInterval* MapAddress(const Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, -                            std::size_t size) { +    MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) {          const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size);          if (overlaps.empty()) {              auto& memory_manager = system.GPU().MemoryManager(); @@ -377,8 +376,7 @@ private:          return map;      } -    void UpdateBlock(const Buffer* block, VAddr start, VAddr end, -                     const VectorMapInterval& overlaps) { +    void UpdateBlock(Buffer* block, VAddr start, VAddr end, const VectorMapInterval& overlaps) {          const IntervalType base_interval{start, end};          IntervalSet interval_set{};          interval_set.add(base_interval); diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index d9f7b4cc6..e461e4c70 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -34,20 +34,27 @@ Buffer::Buffer(const Device& device, VAddr cpu_addr, std::size_t size)  Buffer::~Buffer() = default; -void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { +void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {      glNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size),                           data);  } -void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { +void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {      MICROPROFILE_SCOPE(OpenGL_Buffer_Download); +    const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size); +    const GLintptr gl_offset = static_cast<GLintptr>(offset); +    if (read_buffer.handle == 0) { +        read_buffer.Create(); +        glNamedBufferData(read_buffer.handle, static_cast<GLsizeiptr>(Size()), nullptr, +                          GL_STREAM_READ); +    }      glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); -    glGetNamedBufferSubData(Handle(), static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), -                            data); +    glCopyNamedBufferSubData(gl_buffer.handle, read_buffer.handle, gl_offset, gl_offset, gl_size); +    glGetNamedBufferSubData(read_buffer.handle, gl_offset, gl_size, data);  }  void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, -                      std::size_t size) const { +                      std::size_t size) {      glCopyNamedBufferSubData(src.Handle(), Handle(), static_cast<GLintptr>(src_offset),                               static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size));  } diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 59d95adbc..88fdc0536 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -28,12 +28,12 @@ public:      explicit Buffer(const Device& device, VAddr cpu_addr, std::size_t size);      ~Buffer(); -    void Upload(std::size_t offset, std::size_t size, const u8* data) const; +    void Upload(std::size_t offset, std::size_t size, const u8* data); -    void Download(std::size_t offset, std::size_t size, u8* data) const; +    void Download(std::size_t offset, std::size_t size, u8* data);      void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, -                  std::size_t size) const; +                  std::size_t size);      GLuint Handle() const noexcept {          return gl_buffer.handle; @@ -45,6 +45,7 @@ public:  private:      OGLBuffer gl_buffer; +    OGLBuffer read_buffer;      u64 gpu_address = 0;  }; diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 774e70a5b..fe9bd4b5a 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -191,6 +191,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {          } else {              return GL_MIRROR_CLAMP_TO_EDGE;          } +    case Tegra::Texture::WrapMode::MirrorOnceClampOGL: +        if (GL_EXT_texture_mirror_clamp) { +            return GL_MIRROR_CLAMP_EXT; +        } else { +            return GL_MIRROR_CLAMP_TO_EDGE; +        }      }      UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));      return GL_REPEAT; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index f10f96cd8..2be38d419 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -56,7 +56,7 @@ Buffer::Buffer(const VKDevice& device, VKMemoryManager& memory_manager, VKSchedu  Buffer::~Buffer() = default; -void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const { +void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) {      const auto& staging = staging_pool.GetUnusedBuffer(size, true);      std::memcpy(staging.commit->Map(size), data, size); @@ -81,7 +81,7 @@ void Buffer::Upload(std::size_t offset, std::size_t size, const u8* data) const      });  } -void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const { +void Buffer::Download(std::size_t offset, std::size_t size, u8* data) {      const auto& staging = staging_pool.GetUnusedBuffer(size, true);      scheduler.RequestOutsideRenderPassOperationContext(); @@ -110,7 +110,7 @@ void Buffer::Download(std::size_t offset, std::size_t size, u8* data) const {  }  void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, -                      std::size_t size) const { +                      std::size_t size) {      scheduler.RequestOutsideRenderPassOperationContext();      const VkBuffer dst_buffer = Handle(); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 3630aca77..991ee451c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -29,12 +29,12 @@ public:                      VKStagingBufferPool& staging_pool, VAddr cpu_addr, std::size_t size);      ~Buffer(); -    void Upload(std::size_t offset, std::size_t size, const u8* data) const; +    void Upload(std::size_t offset, std::size_t size, const u8* data); -    void Download(std::size_t offset, std::size_t size, u8* data) const; +    void Download(std::size_t offset, std::size_t size, u8* data);      void CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst_offset, -                  std::size_t size) const; +                  std::size_t size);      VkBuffer Handle() const {          return *buffer.handle; diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index a05fa64ba..00433926d 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -70,6 +70,20 @@ static QString ButtonToText(const Common::ParamPackage& param) {          return GetKeyName(param.Get("code", 0));      } +    if (param.Get("engine", "") == "gcpad") { +        if (param.Has("axis")) { +            const QString axis_str = QString::fromStdString(param.Get("axis", "")); +            const QString direction_str = QString::fromStdString(param.Get("direction", "")); + +            return QObject::tr("GC Axis %1%2").arg(axis_str, direction_str); +        } +        if (param.Has("button")) { +            const QString button_str = QString::number(int(std::log2(param.Get("button", 0)))); +            return QObject::tr("GC Button %1").arg(button_str); +        } +        return GetKeyName(param.Get("code", 0)); +    } +      if (param.Get("engine", "") == "sdl") {          if (param.Has("hat")) {              const QString hat_str = QString::fromStdString(param.Get("hat", "")); @@ -126,6 +140,25 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string          return {};      } +    if (param.Get("engine", "") == "gcpad") { +        if (dir == "modifier") { +            return QObject::tr("[unused]"); +        } + +        if (dir == "left" || dir == "right") { +            const QString axis_x_str = QString::fromStdString(param.Get("axis_x", "")); + +            return QObject::tr("GC Axis %1").arg(axis_x_str); +        } + +        if (dir == "up" || dir == "down") { +            const QString axis_y_str = QString::fromStdString(param.Get("axis_y", "")); + +            return QObject::tr("GC Axis %1").arg(axis_y_str); +        } + +        return {}; +    }      return QObject::tr("[unknown]");  } @@ -332,7 +365,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i          connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {              const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); -            if (analogs_param[analog_id].Get("engine", "") == "sdl") { +            if (analogs_param[analog_id].Get("engine", "") == "sdl" || +                analogs_param[analog_id].Get("engine", "") == "gcpad") {                  analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(                      tr("Deadzone: %1%").arg(slider_value));                  analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); @@ -352,6 +386,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i      connect(poll_timer.get(), &QTimer::timeout, [this] {          Common::ParamPackage params; +        if (InputCommon::GetGCButtons()->IsPolling()) { +            params = InputCommon::GetGCButtons()->GetNextInput(); +            if (params.Has("engine")) { +                SetPollingResult(params, false); +                return; +            } +        } +        if (InputCommon::GetGCAnalogs()->IsPolling()) { +            params = InputCommon::GetGCAnalogs()->GetNextInput(); +            if (params.Has("engine")) { +                SetPollingResult(params, false); +                return; +            } +        }          for (auto& poller : device_pollers) {              params = poller->GetNextInput();              if (params.Has("engine")) { @@ -534,7 +582,7 @@ void ConfigureInputPlayer::UpdateButtonLabels() {              analog_map_deadzone_and_modifier_slider_label[analog_id];          if (param.Has("engine")) { -            if (param.Get("engine", "") == "sdl") { +            if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") {                  if (!param.Has("deadzone")) {                      param.Set("deadzone", 0.1f);                  } @@ -583,6 +631,11 @@ void ConfigureInputPlayer::HandleClick(      grabKeyboard();      grabMouse(); +    if (type == InputCommon::Polling::DeviceType::Button) { +        InputCommon::GetGCButtons()->BeginConfiguration(); +    } else { +        InputCommon::GetGCAnalogs()->BeginConfiguration(); +    }      timeout_timer->start(5000); // Cancel after 5 seconds      poll_timer->start(200);     // Check for new inputs every 200ms  } @@ -596,6 +649,9 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,          poller->Stop();      } +    InputCommon::GetGCButtons()->EndConfiguration(); +    InputCommon::GetGCAnalogs()->EndConfiguration(); +      if (!abort) {          (*input_setter)(params);      } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f586950e7..fb299a39b 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2226,7 +2226,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {                           "title.keys_autogenerated");      } -    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::instance(); +    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();      if (keys.BaseDeriveNecessary()) {          Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(              FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};  | 
