Compare commits
19 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0dbf39ff53 | ||
|
|
7978fe5178 | ||
|
|
40416d9ec8 | ||
|
|
1651506a8e | ||
|
|
e4a134c5a6 | ||
|
|
08b1e5756e | ||
|
|
4a6fa559c4 | ||
|
|
39319f09d8 | ||
|
|
ded0b9d093 | ||
|
|
827ff077e7 | ||
|
|
38980b2471 | ||
|
|
57e43682ed | ||
|
|
abfdc3aa7d | ||
|
|
0ee7c985da | ||
|
|
91cbe52122 | ||
|
|
bafef3d1c9 | ||
|
|
4c1a95ed61 | ||
|
|
01d1b5cdaf | ||
|
|
85db5f4091 |
3
externals/libusb/CMakeLists.txt
vendored
3
externals/libusb/CMakeLists.txt
vendored
@@ -21,6 +21,9 @@ if(WIN32)
|
||||
if (NOT MINGW)
|
||||
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
||||
endif()
|
||||
|
||||
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
|
||||
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
|
||||
else()
|
||||
target_include_directories(usb
|
||||
# turns out other projects also have "config.h", so make sure the
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
using Common::AsArray;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
@@ -31,7 +31,8 @@ constexpr std::array partition_names{
|
||||
|
||||
XCI::XCI(VirtualFile file_)
|
||||
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
|
||||
partitions(partition_names.size()), partitions_raw(partition_names.size()) {
|
||||
partitions(partition_names.size()),
|
||||
partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
|
||||
status = Loader::ResultStatus::ErrorBadXCIHeader;
|
||||
return;
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
class KeyManager;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
}
|
||||
@@ -140,6 +143,6 @@ private:
|
||||
|
||||
u64 update_normal_partition_end;
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
Core::Crypto::KeyManager& keys;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/ctr_encryption_layer.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_patch.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
@@ -119,7 +119,8 @@ static bool IsValidNCA(const NCAHeader& header) {
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
|
||||
: file(std::move(file_)),
|
||||
bktr_base_romfs(std::move(bktr_base_romfs_)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
|
||||
@@ -158,7 +158,7 @@ private:
|
||||
bool encrypted = false;
|
||||
bool is_update = false;
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
Core::Crypto::KeyManager& keys;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/kernel_executable.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -4,10 +4,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
namespace FileSys {
|
||||
class CNMT;
|
||||
|
||||
@@ -21,7 +21,7 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const {
|
||||
magic == Common::MakeMagic('P', 'F', 'S', '0');
|
||||
}
|
||||
|
||||
PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
PartitionFilesystem::PartitionFilesystem(VirtualFile file) {
|
||||
// At least be as large as the header
|
||||
if (file->GetSize() < sizeof(Header)) {
|
||||
status = Loader::ResultStatus::ErrorBadPFSHeader;
|
||||
@@ -89,11 +89,11 @@ std::map<std::string, u64> PartitionFilesystem::GetFileSizes() const {
|
||||
return sizes;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
|
||||
std::vector<VirtualFile> PartitionFilesystem::GetFiles() const {
|
||||
return pfs_files;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
|
||||
std::vector<VirtualDir> PartitionFilesystem::GetSubdirectories() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ std::string PartitionFilesystem::GetName() const {
|
||||
return is_hfs ? "HFS0" : "PFS0";
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const {
|
||||
VirtualDir PartitionFilesystem::GetParentDirectory() const {
|
||||
// TODO(DarkLordZach): Add support for nested containers.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace FileSys {
|
||||
*/
|
||||
class PartitionFilesystem : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
|
||||
explicit PartitionFilesystem(VirtualFile file);
|
||||
~PartitionFilesystem() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
@@ -32,10 +32,10 @@ public:
|
||||
std::map<std::string, u64> GetFileOffsets() const;
|
||||
std::map<std::string, u64> GetFileSizes() const;
|
||||
|
||||
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
|
||||
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
|
||||
std::vector<VirtualFile> GetFiles() const override;
|
||||
std::vector<VirtualDir> GetSubdirectories() const override;
|
||||
std::string GetName() const override;
|
||||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
|
||||
VirtualDir GetParentDirectory() const override;
|
||||
void PrintDebugInfo() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -49,8 +49,7 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
|
||||
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir,
|
||||
std::string_view name) {
|
||||
VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) {
|
||||
#ifdef _WIN32
|
||||
return dir->GetSubdirectory(name);
|
||||
#else
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/memory/dmnt_cheat_types.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -31,8 +32,7 @@ std::string FormatTitleVersion(u32 version,
|
||||
|
||||
// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
|
||||
// doesn't have a directory with name.
|
||||
std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir,
|
||||
std::string_view name);
|
||||
VirtualDir FindSubdirectoryCaseless(VirtualDir dir, std::string_view name);
|
||||
|
||||
// A centralized class to manage patches to games.
|
||||
class PatchManager {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <memory>
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/sdmc_factory.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/xts_archive.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -54,7 +54,7 @@ void SetTicketKeys(const std::vector<VirtualFile>& files) {
|
||||
|
||||
NSP::NSP(VirtualFile file_)
|
||||
: file(std::move(file_)), status{Loader::ResultStatus::Success},
|
||||
pfs(std::make_shared<PartitionFilesystem>(file)) {
|
||||
pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
if (pfs->GetStatus() != Loader::ResultStatus::Success) {
|
||||
status = pfs->GetStatus();
|
||||
return;
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Core::Crypto {
|
||||
class KeyManager;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
}
|
||||
@@ -73,7 +77,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;
|
||||
|
||||
VirtualFile romfs;
|
||||
VirtualDir exefs;
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
#include "common/hex_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/crypto/xts_encryption_layer.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/file_sys/xts_archive.h"
|
||||
#include "core/loader/loader.h"
|
||||
@@ -43,7 +44,9 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
|
||||
return true;
|
||||
}
|
||||
|
||||
NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
|
||||
NAX::NAX(VirtualFile file_)
|
||||
: header(std::make_unique<NAXHeader>()),
|
||||
file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
std::string path = Common::FS::SanitizePath(file->GetFullPath());
|
||||
static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
|
||||
std::regex_constants::ECMAScript |
|
||||
@@ -60,7 +63,8 @@ NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::m
|
||||
}
|
||||
|
||||
NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
|
||||
: header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
|
||||
: header(std::make_unique<NAXHeader>()),
|
||||
file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus : u16;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class NCA;
|
||||
|
||||
struct NAXHeader {
|
||||
std::array<u8, 0x20> hmac;
|
||||
u64_le magic;
|
||||
@@ -62,6 +66,6 @@ private:
|
||||
|
||||
VirtualFile dec_file;
|
||||
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
Core::Crypto::KeyManager& keys;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -844,8 +844,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileSys::StorageId id;
|
||||
|
||||
FileSys::StorageId id{};
|
||||
switch (parameters.space_id) {
|
||||
case FileSys::SaveDataSpaceId::NandUser:
|
||||
id = FileSys::StorageId::NandUser;
|
||||
@@ -857,6 +856,10 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
case FileSys::SaveDataSpaceId::NandSystem:
|
||||
id = FileSys::StorageId::NandSystem;
|
||||
break;
|
||||
case FileSys::SaveDataSpaceId::TemporaryStorage:
|
||||
case FileSys::SaveDataSpaceId::ProperSystem:
|
||||
case FileSys::SaveDataSpaceId::SafeMode:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
auto filesystem =
|
||||
@@ -902,7 +905,14 @@ void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
|
||||
// Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData
|
||||
constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None);
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, flags={}", flags);
|
||||
LOG_WARNING(Service_FS,
|
||||
"(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n"
|
||||
"attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n"
|
||||
"attribute.type={}, attribute.rank={}, attribute.index={}",
|
||||
flags, static_cast<u32>(parameters.space_id), parameters.attribute.title_id,
|
||||
parameters.attribute.user_id[1], parameters.attribute.user_id[0],
|
||||
parameters.attribute.save_id, static_cast<u32>(parameters.attribute.type),
|
||||
static_cast<u32>(parameters.attribute.rank), parameters.attribute.index);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/ns/errors.h"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_emu.h"
|
||||
#include "input_common/touch_from_button.h"
|
||||
#include "input_common/udp/client.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
#ifdef HAVE_SDL2
|
||||
#include "input_common/sdl/sdl.h"
|
||||
@@ -40,7 +41,11 @@ struct InputSubsystem::Impl {
|
||||
sdl = SDL::Init();
|
||||
#endif
|
||||
|
||||
udp = CemuhookUDP::Init();
|
||||
udp = std::make_shared<InputCommon::CemuhookUDP::Client>();
|
||||
udpmotion = std::make_shared<UDPMotionFactory>(udp);
|
||||
Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
|
||||
udptouch = std::make_shared<UDPTouchFactory>(udp);
|
||||
Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
@@ -53,12 +58,17 @@ struct InputSubsystem::Impl {
|
||||
#ifdef HAVE_SDL2
|
||||
sdl.reset();
|
||||
#endif
|
||||
udp.reset();
|
||||
Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
|
||||
Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
|
||||
|
||||
gcbuttons.reset();
|
||||
gcanalog.reset();
|
||||
|
||||
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
|
||||
Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
|
||||
|
||||
udpmotion.reset();
|
||||
udptouch.reset();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
|
||||
@@ -114,9 +124,11 @@ struct InputSubsystem::Impl {
|
||||
#ifdef HAVE_SDL2
|
||||
std::unique_ptr<SDL::State> sdl;
|
||||
#endif
|
||||
std::unique_ptr<CemuhookUDP::State> udp;
|
||||
std::shared_ptr<GCButtonFactory> gcbuttons;
|
||||
std::shared_ptr<GCAnalogFactory> gcanalog;
|
||||
std::shared_ptr<UDPMotionFactory> udpmotion;
|
||||
std::shared_ptr<UDPTouchFactory> udptouch;
|
||||
std::shared_ptr<InputCommon::CemuhookUDP::Client> udp;
|
||||
};
|
||||
|
||||
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
|
||||
@@ -175,6 +187,22 @@ const GCButtonFactory* InputSubsystem::GetGCButtons() const {
|
||||
return impl->gcbuttons.get();
|
||||
}
|
||||
|
||||
UDPMotionFactory* InputSubsystem::GetUDPMotions() {
|
||||
return impl->udpmotion.get();
|
||||
}
|
||||
|
||||
const UDPMotionFactory* InputSubsystem::GetUDPMotions() const {
|
||||
return impl->udpmotion.get();
|
||||
}
|
||||
|
||||
UDPTouchFactory* InputSubsystem::GetUDPTouch() {
|
||||
return impl->udptouch.get();
|
||||
}
|
||||
|
||||
const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
|
||||
return impl->udptouch.get();
|
||||
}
|
||||
|
||||
void InputSubsystem::ReloadInputDevices() {
|
||||
if (!impl->udp) {
|
||||
return;
|
||||
|
||||
@@ -50,6 +50,8 @@ public:
|
||||
|
||||
class GCAnalogFactory;
|
||||
class GCButtonFactory;
|
||||
class UDPMotionFactory;
|
||||
class UDPTouchFactory;
|
||||
class Keyboard;
|
||||
class MotionEmu;
|
||||
|
||||
@@ -115,6 +117,18 @@ public:
|
||||
/// Retrieves the underlying GameCube button handler.
|
||||
[[nodiscard]] const GCButtonFactory* GetGCButtons() const;
|
||||
|
||||
/// Retrieves the underlying udp motion handler.
|
||||
[[nodiscard]] UDPMotionFactory* GetUDPMotions();
|
||||
|
||||
/// Retrieves the underlying udp motion handler.
|
||||
[[nodiscard]] const UDPMotionFactory* GetUDPMotions() const;
|
||||
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] UDPTouchFactory* GetUDPTouch();
|
||||
|
||||
/// Retrieves the underlying udp touch handler.
|
||||
[[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
|
||||
|
||||
/// Reloads the input devices
|
||||
void ReloadInputDevices();
|
||||
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/udp/client.h"
|
||||
#include "input_common/udp/protocol.h"
|
||||
|
||||
@@ -131,21 +130,60 @@ static void SocketLoop(Socket* socket) {
|
||||
socket->Loop();
|
||||
}
|
||||
|
||||
Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port,
|
||||
u8 pad_index, u32 client_id)
|
||||
: status(std::move(status)) {
|
||||
StartCommunication(host, port, pad_index, client_id);
|
||||
Client::Client() {
|
||||
LOG_INFO(Input, "Udp Initialization started");
|
||||
for (std::size_t client = 0; client < clients.size(); client++) {
|
||||
u8 pad = client % 4;
|
||||
StartCommunication(client, Settings::values.udp_input_address,
|
||||
Settings::values.udp_input_port, pad, 24872);
|
||||
// Set motion parameters
|
||||
// SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
|
||||
// Real HW values are unkown, 0.00005 is an aproximate to Standard
|
||||
clients[client].motion.SetGyroThreshold(0.00005f);
|
||||
}
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
socket->Stop();
|
||||
thread.join();
|
||||
Reset();
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> Client::GetInputDevices() const {
|
||||
std::vector<Common::ParamPackage> devices;
|
||||
for (std::size_t client = 0; client < clients.size(); client++) {
|
||||
if (!DeviceConnected(client)) {
|
||||
continue;
|
||||
}
|
||||
std::string name = fmt::format("UDP Controller{} {} {}", clients[client].active,
|
||||
clients[client].active == 1, client);
|
||||
devices.emplace_back(Common::ParamPackage{
|
||||
{"class", "cemuhookudp"},
|
||||
{"display", std::move(name)},
|
||||
{"port", std::to_string(client)},
|
||||
});
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
bool Client::DeviceConnected(std::size_t pad) const {
|
||||
// Use last timestamp to detect if the socket has stopped sending data
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
u64 time_difference =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update)
|
||||
.count();
|
||||
return time_difference < 1000 && clients[pad].active == 1;
|
||||
}
|
||||
|
||||
void Client::ReloadUDPClient() {
|
||||
for (std::size_t client = 0; client < clients.size(); client++) {
|
||||
ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client);
|
||||
}
|
||||
}
|
||||
void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) {
|
||||
socket->Stop();
|
||||
thread.join();
|
||||
StartCommunication(host, port, pad_index, client_id);
|
||||
// client number must be determined from host / port and pad index
|
||||
std::size_t client = pad_index;
|
||||
clients[client].socket->Stop();
|
||||
clients[client].thread.join();
|
||||
StartCommunication(client, host, port, pad_index, client_id);
|
||||
}
|
||||
|
||||
void Client::OnVersion(Response::Version data) {
|
||||
@@ -157,23 +195,39 @@ void Client::OnPortInfo(Response::PortInfo data) {
|
||||
}
|
||||
|
||||
void Client::OnPadData(Response::PadData data) {
|
||||
// client number must be determined from host / port and pad index
|
||||
std::size_t client = data.info.id;
|
||||
LOG_TRACE(Input, "PadData packet received");
|
||||
if (data.packet_counter <= packet_sequence) {
|
||||
if (data.packet_counter == clients[client].packet_sequence) {
|
||||
LOG_WARNING(
|
||||
Input,
|
||||
"PadData packet dropped because its stale info. Current count: {} Packet count: {}",
|
||||
packet_sequence, data.packet_counter);
|
||||
clients[client].packet_sequence, data.packet_counter);
|
||||
return;
|
||||
}
|
||||
packet_sequence = data.packet_counter;
|
||||
// TODO: Check how the Switch handles motions and how the CemuhookUDP motion
|
||||
// directions correspond to the ones of the Switch
|
||||
Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z);
|
||||
Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll);
|
||||
{
|
||||
std::lock_guard guard(status->update_mutex);
|
||||
clients[client].active = data.info.is_pad_active;
|
||||
clients[client].packet_sequence = data.packet_counter;
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
u64 time_difference = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
now - clients[client].last_motion_update)
|
||||
.count();
|
||||
clients[client].last_motion_update = now;
|
||||
Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
|
||||
clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
|
||||
// Gyroscope values are not it the correct scale from better joy.
|
||||
// By dividing by 312 allow us to make one full turn = 1 turn
|
||||
// This must be a configurable valued called sensitivity
|
||||
clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f);
|
||||
clients[client].motion.UpdateRotation(time_difference);
|
||||
clients[client].motion.UpdateOrientation(time_difference);
|
||||
Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
|
||||
Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
|
||||
Common::Vec3f rotation = clients[client].motion.GetRotations();
|
||||
std::array<Common::Vec3f, 3> orientation = clients[client].motion.GetOrientation();
|
||||
|
||||
status->motion_status = {accel, gyro};
|
||||
{
|
||||
std::lock_guard guard(clients[client].status.update_mutex);
|
||||
clients[client].status.motion_status = {accelerometer, gyroscope, rotation, orientation};
|
||||
|
||||
// TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
|
||||
// between a simple "tap" and a hard press that causes the touch screen to click.
|
||||
@@ -182,11 +236,11 @@ void Client::OnPadData(Response::PadData data) {
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
|
||||
if (is_active && status->touch_calibration) {
|
||||
const u16 min_x = status->touch_calibration->min_x;
|
||||
const u16 max_x = status->touch_calibration->max_x;
|
||||
const u16 min_y = status->touch_calibration->min_y;
|
||||
const u16 max_y = status->touch_calibration->max_y;
|
||||
if (is_active && clients[client].status.touch_calibration) {
|
||||
const u16 min_x = clients[client].status.touch_calibration->min_x;
|
||||
const u16 max_x = clients[client].status.touch_calibration->max_x;
|
||||
const u16 min_y = clients[client].status.touch_calibration->min_y;
|
||||
const u16 max_y = clients[client].status.touch_calibration->max_y;
|
||||
|
||||
x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) /
|
||||
static_cast<float>(max_x - min_x);
|
||||
@@ -194,17 +248,82 @@ void Client::OnPadData(Response::PadData data) {
|
||||
static_cast<float>(max_y - min_y);
|
||||
}
|
||||
|
||||
status->touch_status = {x, y, is_active};
|
||||
clients[client].status.touch_status = {x, y, is_active};
|
||||
|
||||
if (configuring) {
|
||||
UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) {
|
||||
void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index,
|
||||
u32 client_id) {
|
||||
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this](Response::PadData data) { OnPadData(data); }};
|
||||
LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
|
||||
socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
|
||||
thread = std::thread{SocketLoop, this->socket.get()};
|
||||
clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
|
||||
clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
|
||||
}
|
||||
|
||||
void Client::Reset() {
|
||||
for (std::size_t client = 0; client < clients.size(); client++) {
|
||||
clients[client].socket->Stop();
|
||||
clients[client].thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
|
||||
const Common::Vec3<float>& gyro, bool touch) {
|
||||
if (configuring) {
|
||||
UDPPadStatus pad;
|
||||
if (touch) {
|
||||
pad.touch = PadTouch::Click;
|
||||
pad_queue[client].Push(pad);
|
||||
}
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
if (gyro[i] > 6.0f || gyro[i] < -6.0f) {
|
||||
pad.motion = static_cast<PadMotion>(i);
|
||||
pad.motion_value = gyro[i];
|
||||
pad_queue[client].Push(pad);
|
||||
}
|
||||
if (acc[i] > 2.0f || acc[i] < -2.0f) {
|
||||
pad.motion = static_cast<PadMotion>(i + 3);
|
||||
pad.motion_value = acc[i];
|
||||
pad_queue[client].Push(pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::BeginConfiguration() {
|
||||
for (auto& pq : pad_queue) {
|
||||
pq.Clear();
|
||||
}
|
||||
configuring = true;
|
||||
}
|
||||
|
||||
void Client::EndConfiguration() {
|
||||
for (auto& pq : pad_queue) {
|
||||
pq.Clear();
|
||||
}
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
DeviceStatus& Client::GetPadState(std::size_t pad) {
|
||||
return clients[pad].status;
|
||||
}
|
||||
|
||||
const DeviceStatus& Client::GetPadState(std::size_t pad) const {
|
||||
return clients[pad].status;
|
||||
}
|
||||
|
||||
std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id,
|
||||
|
||||
@@ -12,8 +12,12 @@
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include "common/common_types.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/motion_input.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
@@ -28,9 +32,30 @@ struct PortInfo;
|
||||
struct Version;
|
||||
} // namespace Response
|
||||
|
||||
enum class PadMotion {
|
||||
GyroX,
|
||||
GyroY,
|
||||
GyroZ,
|
||||
AccX,
|
||||
AccY,
|
||||
AccZ,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
enum class PadTouch {
|
||||
Click,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
struct UDPPadStatus {
|
||||
PadTouch touch{PadTouch::Undefined};
|
||||
PadMotion motion{PadMotion::Undefined};
|
||||
f32 motion_value{0.0f};
|
||||
};
|
||||
|
||||
struct DeviceStatus {
|
||||
std::mutex update_mutex;
|
||||
std::tuple<Common::Vec3<float>, Common::Vec3<float>> motion_status;
|
||||
Input::MotionStatus motion_status;
|
||||
std::tuple<float, float, bool> touch_status;
|
||||
|
||||
// calibration data for scaling the device's touch area to 3ds
|
||||
@@ -45,22 +70,58 @@ struct DeviceStatus {
|
||||
|
||||
class Client {
|
||||
public:
|
||||
explicit Client(std::shared_ptr<DeviceStatus> status, const std::string& host = DEFAULT_ADDR,
|
||||
u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872);
|
||||
// Initialize the UDP client capture and read sequence
|
||||
Client();
|
||||
|
||||
// Close and release the client
|
||||
~Client();
|
||||
|
||||
// Used for polling
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const;
|
||||
|
||||
bool DeviceConnected(std::size_t pad) const;
|
||||
void ReloadUDPClient();
|
||||
void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0,
|
||||
u32 client_id = 24872);
|
||||
|
||||
std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue();
|
||||
const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const;
|
||||
|
||||
DeviceStatus& GetPadState(std::size_t pad);
|
||||
const DeviceStatus& GetPadState(std::size_t pad) const;
|
||||
|
||||
private:
|
||||
struct ClientData {
|
||||
std::unique_ptr<Socket> socket;
|
||||
DeviceStatus status;
|
||||
std::thread thread;
|
||||
u64 packet_sequence = 0;
|
||||
u8 active;
|
||||
|
||||
// Realtime values
|
||||
// motion is initalized with PID values for drift correction on joycons
|
||||
InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
|
||||
std::chrono::time_point<std::chrono::system_clock> last_motion_update;
|
||||
};
|
||||
|
||||
// For shutting down, clear all data, join all threads, release usb
|
||||
void Reset();
|
||||
|
||||
void OnVersion(Response::Version);
|
||||
void OnPortInfo(Response::PortInfo);
|
||||
void OnPadData(Response::PadData);
|
||||
void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id);
|
||||
void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index,
|
||||
u32 client_id);
|
||||
void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
|
||||
const Common::Vec3<float>& gyro, bool touch);
|
||||
|
||||
std::unique_ptr<Socket> socket;
|
||||
std::shared_ptr<DeviceStatus> status;
|
||||
std::thread thread;
|
||||
u64 packet_sequence = 0;
|
||||
bool configuring = false;
|
||||
|
||||
std::array<ClientData, 4> clients;
|
||||
std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue;
|
||||
};
|
||||
|
||||
/// An async job allowing configuration of the touchpad calibration.
|
||||
|
||||
@@ -1,105 +1,145 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// 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 <optional>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/settings.h"
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/udp/client.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
namespace InputCommon {
|
||||
|
||||
class UDPTouchDevice final : public Input::TouchDevice {
|
||||
class UDPMotion final : public Input::MotionDevice {
|
||||
public:
|
||||
explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
std::lock_guard guard(status->update_mutex);
|
||||
return status->touch_status;
|
||||
UDPMotion(std::string ip_, int port_, int pad_, InputCommon::CemuhookUDP::Client* client_)
|
||||
: ip(ip_), port(port_), pad(pad_), client(client_) {}
|
||||
|
||||
Input::MotionStatus GetStatus() const override {
|
||||
return client->GetPadState(pad).motion_status;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<DeviceStatus> status;
|
||||
const std::string ip;
|
||||
const int port;
|
||||
const int pad;
|
||||
InputCommon::CemuhookUDP::Client* client;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
class UDPMotionDevice final : public Input::MotionDevice {
|
||||
public:
|
||||
explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
|
||||
std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override {
|
||||
std::lock_guard guard(status->update_mutex);
|
||||
return status->motion_status;
|
||||
}
|
||||
/// A motion device factory that creates motion devices from JC Adapter
|
||||
UDPMotionFactory::UDPMotionFactory(std::shared_ptr<InputCommon::CemuhookUDP::Client> client_)
|
||||
: client(std::move(client_)) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<DeviceStatus> status;
|
||||
};
|
||||
/**
|
||||
* Creates motion device
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "port": the nth jcpad on the adapter
|
||||
*/
|
||||
std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
|
||||
const std::string ip = params.Get("ip", "127.0.0.1");
|
||||
const int port = params.Get("port", 26760);
|
||||
const int pad = params.Get("pad_index", 0);
|
||||
|
||||
class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
|
||||
public:
|
||||
explicit UDPTouchFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
|
||||
return std::make_unique<UDPMotion>(ip, port, pad, client.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override {
|
||||
{
|
||||
std::lock_guard guard(status->update_mutex);
|
||||
status->touch_calibration = DeviceStatus::CalibrationData{};
|
||||
// These default values work well for DS4 but probably not other touch inputs
|
||||
status->touch_calibration->min_x = params.Get("min_x", 100);
|
||||
status->touch_calibration->min_y = params.Get("min_y", 50);
|
||||
status->touch_calibration->max_x = params.Get("max_x", 1800);
|
||||
status->touch_calibration->max_y = params.Get("max_y", 850);
|
||||
void UDPMotionFactory::BeginConfiguration() {
|
||||
polling = true;
|
||||
client->BeginConfiguration();
|
||||
}
|
||||
|
||||
void UDPMotionFactory::EndConfiguration() {
|
||||
polling = false;
|
||||
client->EndConfiguration();
|
||||
}
|
||||
|
||||
Common::ParamPackage UDPMotionFactory::GetNextInput() {
|
||||
Common::ParamPackage params;
|
||||
InputCommon::CemuhookUDP::UDPPadStatus pad;
|
||||
auto& queue = client->GetPadQueue();
|
||||
for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
|
||||
while (queue[pad_number].Pop(pad)) {
|
||||
if (pad.motion == InputCommon::CemuhookUDP::PadMotion::Undefined ||
|
||||
std::abs(pad.motion_value) < 1) {
|
||||
continue;
|
||||
}
|
||||
params.Set("engine", "cemuhookudp");
|
||||
params.Set("ip", "127.0.0.1");
|
||||
params.Set("port", 26760);
|
||||
params.Set("pad_index", static_cast<int>(pad_number));
|
||||
params.Set("motion", static_cast<u16>(pad.motion));
|
||||
return params;
|
||||
}
|
||||
return std::make_unique<UDPTouchDevice>(status);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<DeviceStatus> status;
|
||||
};
|
||||
|
||||
class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
|
||||
class UDPTouch final : public Input::TouchDevice {
|
||||
public:
|
||||
explicit UDPMotionFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
|
||||
UDPTouch(std::string ip_, int port_, int pad_, InputCommon::CemuhookUDP::Client* client_)
|
||||
: ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
|
||||
|
||||
std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
|
||||
return std::make_unique<UDPMotionDevice>(status);
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
return client->GetPadState(pad).touch_status;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<DeviceStatus> status;
|
||||
const std::string ip;
|
||||
const int port;
|
||||
const int pad;
|
||||
InputCommon::CemuhookUDP::Client* client;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
State::State() {
|
||||
auto status = std::make_shared<DeviceStatus>();
|
||||
client =
|
||||
std::make_unique<Client>(status, Settings::values.udp_input_address,
|
||||
Settings::values.udp_input_port, Settings::values.udp_pad_index);
|
||||
/// A motion device factory that creates motion devices from JC Adapter
|
||||
UDPTouchFactory::UDPTouchFactory(std::shared_ptr<InputCommon::CemuhookUDP::Client> client_)
|
||||
: client(std::move(client_)) {}
|
||||
|
||||
motion_factory = std::make_shared<UDPMotionFactory>(status);
|
||||
touch_factory = std::make_shared<UDPTouchFactory>(status);
|
||||
/**
|
||||
* Creates motion device
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "port": the nth jcpad on the adapter
|
||||
*/
|
||||
std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
|
||||
const std::string ip = params.Get("ip", "127.0.0.1");
|
||||
const int port = params.Get("port", 26760);
|
||||
const int pad = params.Get("pad_index", 0);
|
||||
|
||||
Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory);
|
||||
Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory);
|
||||
return std::make_unique<UDPTouch>(ip, port, pad, client.get());
|
||||
}
|
||||
|
||||
State::~State() {
|
||||
Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
|
||||
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
|
||||
void UDPTouchFactory::BeginConfiguration() {
|
||||
polling = true;
|
||||
client->BeginConfiguration();
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> State::GetInputDevices() const {
|
||||
// TODO support binding udp devices
|
||||
return {};
|
||||
void UDPTouchFactory::EndConfiguration() {
|
||||
polling = false;
|
||||
client->EndConfiguration();
|
||||
}
|
||||
|
||||
void State::ReloadUDPClient() {
|
||||
client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
|
||||
Settings::values.udp_pad_index);
|
||||
Common::ParamPackage UDPTouchFactory::GetNextInput() {
|
||||
Common::ParamPackage params;
|
||||
InputCommon::CemuhookUDP::UDPPadStatus pad;
|
||||
auto& queue = client->GetPadQueue();
|
||||
for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
|
||||
while (queue[pad_number].Pop(pad)) {
|
||||
if (pad.touch == InputCommon::CemuhookUDP::PadTouch::Undefined) {
|
||||
continue;
|
||||
}
|
||||
params.Set("engine", "cemuhookudp");
|
||||
params.Set("ip", "127.0.0.1");
|
||||
params.Set("port", 26760);
|
||||
params.Set("pad_index", static_cast<int>(pad_number));
|
||||
params.Set("touch", static_cast<u16>(pad.touch));
|
||||
return params;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
std::unique_ptr<State> Init() {
|
||||
return std::make_unique<State>();
|
||||
}
|
||||
} // namespace InputCommon::CemuhookUDP
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -1,32 +1,57 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/param_package.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/udp/client.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
namespace InputCommon {
|
||||
|
||||
class Client;
|
||||
class UDPMotionFactory;
|
||||
class UDPTouchFactory;
|
||||
|
||||
class State {
|
||||
/// A motion device factory that creates motion devices from udp clients
|
||||
class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
|
||||
public:
|
||||
State();
|
||||
~State();
|
||||
void ReloadUDPClient();
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const;
|
||||
explicit UDPMotionFactory(std::shared_ptr<InputCommon::CemuhookUDP::Client> client_);
|
||||
|
||||
std::unique_ptr<Input::MotionDevice> 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::unique_ptr<Client> client;
|
||||
std::shared_ptr<UDPMotionFactory> motion_factory;
|
||||
std::shared_ptr<UDPTouchFactory> touch_factory;
|
||||
std::shared_ptr<InputCommon::CemuhookUDP::Client> client;
|
||||
bool polling = false;
|
||||
};
|
||||
|
||||
std::unique_ptr<State> Init();
|
||||
/// A touch device factory that creates touch devices from udp clients
|
||||
class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
|
||||
public:
|
||||
explicit UDPTouchFactory(std::shared_ptr<InputCommon::CemuhookUDP::Client> client_);
|
||||
|
||||
} // namespace InputCommon::CemuhookUDP
|
||||
std::unique_ptr<Input::TouchDevice> 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<InputCommon::CemuhookUDP::Client> client;
|
||||
bool polling = false;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -73,11 +73,11 @@ void AsyncShaders::KillWorkers() {
|
||||
worker_threads.clear();
|
||||
}
|
||||
|
||||
bool AsyncShaders::HasWorkQueued() {
|
||||
bool AsyncShaders::HasWorkQueued() const {
|
||||
return !pending_queue.empty();
|
||||
}
|
||||
|
||||
bool AsyncShaders::HasCompletedWork() {
|
||||
bool AsyncShaders::HasCompletedWork() const {
|
||||
std::shared_lock lock{completed_mutex};
|
||||
return !finished_work.empty();
|
||||
}
|
||||
@@ -102,7 +102,7 @@ bool AsyncShaders::IsShaderAsync(const Tegra::GPU& gpu) const {
|
||||
}
|
||||
|
||||
std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
|
||||
std::vector<AsyncShaders::Result> results;
|
||||
std::vector<Result> results;
|
||||
{
|
||||
std::unique_lock lock{completed_mutex};
|
||||
results.assign(std::make_move_iterator(finished_work.begin()),
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
#include "common/bit_field.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_opengl/gl_device.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
@@ -17,7 +16,6 @@
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
@@ -70,20 +68,20 @@ public:
|
||||
void KillWorkers();
|
||||
|
||||
/// Check to see if any shaders have actually been compiled
|
||||
bool HasCompletedWork();
|
||||
[[nodiscard]] bool HasCompletedWork() const;
|
||||
|
||||
/// Deduce if a shader can be build on another thread of MUST be built in sync. We cannot build
|
||||
/// every shader async as some shaders are only built and executed once. We try to "guess" which
|
||||
/// shader would be used only once
|
||||
bool IsShaderAsync(const Tegra::GPU& gpu) const;
|
||||
[[nodiscard]] bool IsShaderAsync(const Tegra::GPU& gpu) const;
|
||||
|
||||
/// Pulls completed compiled shaders
|
||||
std::vector<Result> GetCompletedWork();
|
||||
[[nodiscard]] std::vector<Result> GetCompletedWork();
|
||||
|
||||
void QueueOpenGLShader(const OpenGL::Device& device, Tegra::Engines::ShaderType shader_type,
|
||||
u64 uid, std::vector<u64> code, std::vector<u64> code_b, u32 main_offset,
|
||||
VideoCommon::Shader::CompilerSettings compiler_settings,
|
||||
const VideoCommon::Shader::Registry& registry, VAddr cpu_addr);
|
||||
CompilerSettings compiler_settings, const Registry& registry,
|
||||
VAddr cpu_addr);
|
||||
|
||||
void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device,
|
||||
Vulkan::VKScheduler& scheduler,
|
||||
@@ -97,7 +95,7 @@ private:
|
||||
void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
|
||||
|
||||
/// Check our worker queue to see if we have any work queued already
|
||||
bool HasWorkQueued();
|
||||
[[nodiscard]] bool HasWorkQueued() const;
|
||||
|
||||
struct WorkerParams {
|
||||
Backend backend;
|
||||
@@ -108,8 +106,8 @@ private:
|
||||
std::vector<u64> code;
|
||||
std::vector<u64> code_b;
|
||||
u32 main_offset;
|
||||
VideoCommon::Shader::CompilerSettings compiler_settings;
|
||||
std::optional<VideoCommon::Shader::Registry> registry;
|
||||
CompilerSettings compiler_settings;
|
||||
std::optional<Registry> registry;
|
||||
VAddr cpu_address;
|
||||
|
||||
// For Vulkan
|
||||
@@ -125,13 +123,13 @@ private:
|
||||
};
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex queue_mutex;
|
||||
std::shared_mutex completed_mutex;
|
||||
mutable std::mutex queue_mutex;
|
||||
mutable std::shared_mutex completed_mutex;
|
||||
std::atomic<bool> is_thread_exiting{};
|
||||
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
|
||||
std::vector<std::thread> worker_threads;
|
||||
std::queue<WorkerParams> pending_queue;
|
||||
std::vector<AsyncShaders::Result> finished_work;
|
||||
std::vector<Result> finished_work;
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
#include "ui_configure_input_player.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_input_player.h"
|
||||
@@ -173,6 +174,14 @@ QString ButtonToText(const Common::ParamPackage& param) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "cemuhookudp") {
|
||||
if (param.Has("pad_index")) {
|
||||
const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
|
||||
return QObject::tr("UDP Motion %1").arg(motion_str);
|
||||
}
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
return QObject::tr("[unknown]");
|
||||
}
|
||||
|
||||
@@ -417,6 +426,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (input_subsystem->GetUDPMotions()->IsPolling()) {
|
||||
params = input_subsystem->GetUDPMotions()->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
SetPollingResult(params, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto& poller : device_pollers) {
|
||||
params = poller->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
@@ -683,6 +699,10 @@ void ConfigureInputPlayer::HandleClick(
|
||||
input_subsystem->GetGCAnalogs()->BeginConfiguration();
|
||||
}
|
||||
|
||||
if (type == InputCommon::Polling::DeviceType::Motion) {
|
||||
input_subsystem->GetUDPMotions()->BeginConfiguration();
|
||||
}
|
||||
|
||||
timeout_timer->start(2500); // Cancel after 2.5 seconds
|
||||
poll_timer->start(50); // Check for new inputs every 50ms
|
||||
}
|
||||
@@ -700,6 +720,8 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
|
||||
input_subsystem->GetGCButtons()->EndConfiguration();
|
||||
input_subsystem->GetGCAnalogs()->EndConfiguration();
|
||||
|
||||
input_subsystem->GetUDPMotions()->EndConfiguration();
|
||||
|
||||
if (!abort) {
|
||||
(*input_setter)(params);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user