diff options
Diffstat (limited to 'src/core')
32 files changed, 1058 insertions, 175 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c42f95705..c85c9485f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -586,6 +586,9 @@ add_library(core STATIC memory/dmnt_cheat_vm.h memory.cpp memory.h + network/network.cpp + network/network.h + network/sockets.h perf_stats.cpp perf_stats.h reporter.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 69a1aa0a5..42277e2cd 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -43,6 +43,7 @@ #include "core/loader/loader.h" #include "core/memory.h" #include "core/memory/cheat_engine.h" +#include "core/network/network.h" #include "core/perf_stats.h" #include "core/reporter.h" #include "core/settings.h" @@ -145,7 +146,7 @@ struct System::Impl { ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { LOG_DEBUG(HW_Memory, "initialized OK"); - device_memory = std::make_unique<Core::DeviceMemory>(system); + device_memory = std::make_unique<Core::DeviceMemory>(); is_multicore = Settings::values.use_multi_core.GetValue(); is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); @@ -394,6 +395,9 @@ struct System::Impl { /// Telemetry session for this emulation session std::unique_ptr<Core::TelemetrySession> telemetry_session; + /// Network instance + Network::NetworkInstance network_instance; + ResultStatus status = ResultStatus::Success; std::string status_details = ""; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index b5feb3f24..71af26ec5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -23,7 +23,7 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac struct CoreTiming::Event { u64 time; u64 fifo_order; - u64 userdata; + std::uintptr_t user_data; std::weak_ptr<EventType> type; // Sort by time, unless the times are the same, in which case sort by @@ -58,7 +58,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { event_fifo_id = 0; shutting_down = false; ticks = 0; - const auto empty_timed_callback = [](u64, std::chrono::nanoseconds) {}; + const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); if (is_multicore) { timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); @@ -107,22 +107,24 @@ bool CoreTiming::HasPendingEvents() const { } void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, - const std::shared_ptr<EventType>& event_type, u64 userdata) { + const std::shared_ptr<EventType>& event_type, + std::uintptr_t user_data) { { std::scoped_lock scope{basic_lock}; const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); - event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); + event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } event.Set(); } -void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { +void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, + std::uintptr_t user_data) { std::scoped_lock scope{basic_lock}; const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { - return e.type.lock().get() == event_type.get() && e.userdata == userdata; + return e.type.lock().get() == event_type.get() && e.user_data == user_data; }); // Removing random items breaks the invariant so we have to re-establish it. @@ -197,7 +199,7 @@ std::optional<s64> CoreTiming::Advance() { if (const auto event_type{evt.type.lock()}) { event_type->callback( - evt.userdata, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); + evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); } basic_lock.lock(); diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 120c74e46..b0b6036e4 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -22,7 +22,8 @@ namespace Core::Timing { /// A callback that may be scheduled for a particular core timing event. -using TimedCallback = std::function<void(u64 userdata, std::chrono::nanoseconds ns_late)>; +using TimedCallback = + std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; /// Contains the characteristics of a particular event. struct EventType { @@ -94,9 +95,9 @@ public: /// Schedules an event in core timing void ScheduleEvent(std::chrono::nanoseconds ns_into_future, - const std::shared_ptr<EventType>& event_type, u64 userdata = 0); + const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); - void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); + void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); /// We only permit one event of each type in the queue at a time. void RemoveEvent(const std::shared_ptr<EventType>& event_type); diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp index 51097ced3..0c4b440ed 100644 --- a/src/core/device_memory.cpp +++ b/src/core/device_memory.cpp @@ -2,14 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/core.h" #include "core/device_memory.h" -#include "core/memory.h" namespace Core { -DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {} - +DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {} DeviceMemory::~DeviceMemory() = default; } // namespace Core diff --git a/src/core/device_memory.h b/src/core/device_memory.h index 9efa088d0..5b1ae28f3 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h @@ -4,14 +4,11 @@ #pragma once -#include "common/assert.h" -#include "common/common_funcs.h" +#include "common/common_types.h" #include "common/virtual_buffer.h" namespace Core { -class System; - namespace DramMemoryMap { enum : u64 { Base = 0x80000000ULL, @@ -26,7 +23,7 @@ enum : u64 { class DeviceMemory : NonCopyable { public: - explicit DeviceMemory(Core::System& system); + explicit DeviceMemory(); ~DeviceMemory(); template <typename T> @@ -45,7 +42,6 @@ public: private: Common::VirtualBuffer<u8> buffer; - Core::System& system; }; } // namespace Core diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 37351c561..e94eed3b6 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -547,56 +547,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); } -bool RegisteredCache::RemoveExistingEntry(u64 title_id) { - const auto delete_nca = [this](const NcaID& id) { - const auto path = GetRelativePathFromNcaID(id, false, true, false); - - if (dir->GetFileRelative(path) == nullptr) { - return false; - } - - Core::Crypto::SHA256Hash hash{}; - mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0); - const auto dirname = fmt::format("000000{:02X}", hash[0]); - - const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); - - const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false))); - - return res; - }; - - // If an entry exists in the registered cache, remove it - if (HasEntry(title_id, ContentRecordType::Meta)) { - LOG_INFO(Loader, - "Previously installed entry (v{}) for title_id={:016X} detected! " - "Attempting to remove...", - GetEntryVersion(title_id).value_or(0), title_id); - // Get all the ncas associated with the current CNMT and delete them - const auto meta_old_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); - const auto program_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); - const auto data_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); - const auto control_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); - const auto html_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); - const auto legal_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); - - delete_nca(meta_old_id); - delete_nca(program_id); - delete_nca(data_id); - delete_nca(control_id); - delete_nca(html_id); - delete_nca(legal_id); - return true; - } - return false; -} - InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, const VfsCopyFunction& copy) { const auto ncas = nsp.GetNCAsCollapsed(); @@ -692,6 +642,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); } +bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { + const auto delete_nca = [this](const NcaID& id) { + const auto path = GetRelativePathFromNcaID(id, false, true, false); + + const bool isFile = dir->GetFileRelative(path) != nullptr; + const bool isDir = dir->GetDirectoryRelative(path) != nullptr; + + if (isFile) { + return dir->DeleteFile(path); + } else if (isDir) { + return dir->DeleteSubdirectoryRecursive(path); + } + + return false; + }; + + // If an entry exists in the registered cache, remove it + if (HasEntry(title_id, ContentRecordType::Meta)) { + LOG_INFO(Loader, + "Previously installed entry (v{}) for title_id={:016X} detected! " + "Attempting to remove...", + GetEntryVersion(title_id).value_or(0), title_id); + + // Get all the ncas associated with the current CNMT and delete them + const auto meta_old_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); + const auto program_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); + const auto data_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); + const auto control_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); + const auto html_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); + const auto legal_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); + + const auto deleted_meta = delete_nca(meta_old_id); + const auto deleted_program = delete_nca(program_id); + const auto deleted_data = delete_nca(data_id); + const auto deleted_control = delete_nca(control_id); + const auto deleted_html = delete_nca(html_id); + const auto deleted_legal = delete_nca(legal_id); + + return deleted_meta && (deleted_meta || deleted_program || deleted_data || + deleted_control || deleted_html || deleted_legal); + } + + return false; +} + InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, bool overwrite_if_exists, std::optional<NcaID> override_id) { diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 29cf0d40c..ec1d54f27 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -155,9 +155,6 @@ public: std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, std::optional<u64> title_id = {}) const override; - // Removes an existing entry based on title id - bool RemoveExistingEntry(u64 title_id); - // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure // there is a meta NCA and all of them are accessible. InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, @@ -172,6 +169,9 @@ public: InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, const VfsCopyFunction& copy = &VfsRawCopy); + // Removes an existing entry based on title id + bool RemoveExistingEntry(u64 title_id) const; + private: template <typename T> void IterateAllMetadata(std::vector<T>& out, diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index 86e06ccb9..81413c684 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -70,14 +70,18 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) NAX::~NAX() = default; Loader::ResultStatus NAX::Parse(std::string_view path) { - if (file->ReadObject(header.get()) != sizeof(NAXHeader)) + if (file == nullptr) { + return Loader::ResultStatus::ErrorNullFile; + } + if (file->ReadObject(header.get()) != sizeof(NAXHeader)) { return Loader::ResultStatus::ErrorBadNAXHeader; - - if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) + } + if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) { return Loader::ResultStatus::ErrorBadNAXHeader; - - if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) + } + if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) { return Loader::ResultStatus::ErrorIncorrectNAXFileSize; + } keys.DeriveSDSeedLazy(); std::array<Core::Crypto::Key256, 2> sd_keys{}; diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index efc1030c1..645f26e91 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp @@ -11,8 +11,8 @@ namespace Core::Hardware { InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { - gpu_interrupt_event = - Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, std::chrono::nanoseconds) { + gpu_interrupt_event = Core::Timing::CreateEvent( + "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); const u32 syncpt = static_cast<u32>(message >> 32); const u32 value = static_cast<u32>(message); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 8dd4a2637..cabe8d418 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -145,7 +145,7 @@ struct KernelCore::Impl { void InitializePreemption(KernelCore& kernel) { preemption_event = Core::Timing::CreateEvent( - "PreemptionCallback", [this, &kernel](u64, std::chrono::nanoseconds) { + "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { { SchedulerLock lock(kernel); global_scheduler.PreemptThreads(); diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index af22f4c33..7e6391c6c 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -33,8 +33,10 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern std::string name) { std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; - session->request_event = Core::Timing::CreateEvent( - name, [session](u64, std::chrono::nanoseconds) { session->CompleteSyncRequest(); }); + session->request_event = + Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) { + session->CompleteSyncRequest(); + }); session->name = std::move(name); session->parent = std::move(parent); diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 88b01b751..95f2446c9 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -16,14 +16,14 @@ namespace Kernel { TimeManager::TimeManager(Core::System& system_) : system{system_} { time_manager_event_type = Core::Timing::CreateEvent( - "Kernel::TimeManagerCallback", [this](u64 thread_handle, std::chrono::nanoseconds) { - SchedulerLock lock(system.Kernel()); - Handle proper_handle = static_cast<Handle>(thread_handle); + "Kernel::TimeManagerCallback", + [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { + const SchedulerLock lock(system.Kernel()); + const auto proper_handle = static_cast<Handle>(thread_handle); if (cancelled_events[proper_handle]) { return; } - std::shared_ptr<Thread> thread = - this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); + auto thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); thread->OnWakeUp(); }); } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4e7a0bec9..ceed20609 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1405,7 +1405,6 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { // Get supported languages from NACP, if possible // Default to 0 (all languages supported) u32 supported_languages = 0; - FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; const auto res = [this] { const auto title_id = system.CurrentProcess()->GetTitleID(); diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index fbe3686ae..289da2619 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -13,11 +13,23 @@ namespace Service::AM::Applets { +namespace { +enum class Request : u32 { + Finalize = 0x4, + SetUserWordInfo = 0x6, + SetCustomizeDic = 0x7, + Calc = 0xa, + SetCustomizedDictionaries = 0xb, + UnsetCustomizedDictionaries = 0xc, + UnknownD = 0xd, + UnknownE = 0xe, +}; +constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8; constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; constexpr std::size_t DEFAULT_MAX_LENGTH = 500; constexpr bool INTERACTIVE_STATUS_OK = false; - +} // Anonymous namespace static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( KeyboardConfig config, std::u16string initial_text) { Core::Frontend::SoftwareKeyboardParameters params{}; @@ -47,6 +59,7 @@ SoftwareKeyboard::~SoftwareKeyboard() = default; void SoftwareKeyboard::Initialize() { complete = false; + is_inline = false; initial_text.clear(); final_data.clear(); @@ -56,6 +69,11 @@ void SoftwareKeyboard::Initialize() { ASSERT(keyboard_config_storage != nullptr); const auto& keyboard_config = keyboard_config_storage->GetData(); + if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) { + is_inline = true; + return; + } + ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); @@ -87,16 +105,32 @@ void SoftwareKeyboard::ExecuteInteractive() { const auto storage = broker.PopInteractiveDataToApplet(); ASSERT(storage != nullptr); const auto data = storage->GetData(); - const auto status = static_cast<bool>(data[0]); - - if (status == INTERACTIVE_STATUS_OK) { - complete = true; + if (!is_inline) { + const auto status = static_cast<bool>(data[0]); + if (status == INTERACTIVE_STATUS_OK) { + complete = true; + } else { + std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; + std::memcpy(string.data(), data.data() + 4, string.size() * 2); + frontend.SendTextCheckDialog( + Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), + [this] { broker.SignalStateChanged(); }); + } } else { - std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; - std::memcpy(string.data(), data.data() + 4, string.size() * 2); - frontend.SendTextCheckDialog( - Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), - [this] { broker.SignalStateChanged(); }); + Request request{}; + std::memcpy(&request, data.data(), sizeof(Request)); + + switch (request) { + case Request::Calc: { + broker.PushNormalDataFromApplet( + std::make_shared<IStorage>(std::move(std::vector<u8>{1}))); + broker.SignalStateChanged(); + break; + } + default: + UNIMPLEMENTED_MSG("Request {:X} is not implemented", request); + break; + } } } @@ -108,9 +142,10 @@ void SoftwareKeyboard::Execute() { } const auto parameters = ConvertToFrontendParameters(config, initial_text); - - frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, - parameters); + if (!is_inline) { + frontend.RequestText( + [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters); + } } void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index ef4801fc6..5a3824b5a 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -78,6 +78,7 @@ private: KeyboardConfig config; std::u16string initial_text; bool complete = false; + bool is_inline = false; std::vector<u8> final_data; }; diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index cb35919e9..ad251ed4a 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -39,33 +39,36 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, cur_entry.sampling_number = last_entry.sampling_number + 1; cur_entry.sampling_number2 = cur_entry.sampling_number; - cur_entry.attribute.connected.Assign(1); - auto& pad = cur_entry.pad_state; - using namespace Settings::NativeButton; - pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); - pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); - pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); - pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); - pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); - pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); - pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); - pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); - pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); - pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); - pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); - pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); - pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); - pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); + if (Settings::values.debug_pad_enabled) { + cur_entry.attribute.connected.Assign(1); + auto& pad = cur_entry.pad_state; - const auto [stick_l_x_f, stick_l_y_f] = - analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); - const auto [stick_r_x_f, stick_r_y_f] = - analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); - cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); - cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); - cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); - cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); + using namespace Settings::NativeButton; + pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); + pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); + pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); + pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); + pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); + pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); + pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); + pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); + pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); + pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); + pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); + pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); + pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); + pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); + + const auto [stick_l_x_f, stick_l_y_f] = + analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); + const auto [stick_r_x_f, stick_r_y_f] = + analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); + cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); + cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); + cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); + cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); + } std::memcpy(data, &shared_memory, sizeof(SharedMemory)); } diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index feae89525..0b896d5ad 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -40,15 +40,16 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, cur_entry.key.fill(0); cur_entry.modifier = 0; - - for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { - cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); - } - - for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { - cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); + if (Settings::values.keyboard_enabled) { + for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { + cur_entry.key[i / KEYS_PER_BYTE] |= + (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); + } + + for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { + cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); + } } - std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 680290cbd..1e95b7580 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -77,8 +77,9 @@ IAppletResource::IAppletResource(Core::System& system) // Register update callbacks pad_update_event = Core::Timing::CreateEvent( - "HID::UpdatePadCallback", [this](u64 userdata, std::chrono::nanoseconds ns_late) { - UpdateControllers(userdata, ns_late); + "HID::UpdatePadCallback", + [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + UpdateControllers(user_data, ns_late); }); // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) @@ -108,7 +109,8 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(shared_mem); } -void IAppletResource::UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late) { +void IAppletResource::UpdateControllers(std::uintptr_t user_data, + std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index c6f0a2584..efb07547f 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -64,7 +64,7 @@ private: } void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); - void UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late); + void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); std::shared_ptr<Kernel::SharedMemory> shared_mem; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 789856118..f644a460d 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -67,8 +67,8 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { // Schedule the screen composition events composition_event = Core::Timing::CreateEvent( - "ScreenComposition", [this](u64, std::chrono::nanoseconds ns_late) { - Lock(); + "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { + const auto guard = Lock(); Compose(); const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index e4959a9af..ff85cbba6 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -54,12 +54,12 @@ public: /// Opens the specified display and returns the ID. /// /// If an invalid display name is provided, then an empty optional is returned. - std::optional<u64> OpenDisplay(std::string_view name); + [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name); /// Creates a layer on the specified display and returns the layer ID. /// /// If an invalid display ID is specified, then an empty optional is returned. - std::optional<u64> CreateLayer(u64 display_id); + [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); /// Closes a layer on all displays for the given layer ID. void CloseLayer(u64 layer_id); @@ -67,41 +67,39 @@ public: /// Finds the buffer queue ID of the specified layer in the specified display. /// /// If an invalid display ID or layer ID is provided, then an empty optional is returned. - std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; + [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; /// Gets the vsync event for the specified display. /// /// If an invalid display ID is provided, then nullptr is returned. - std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; + [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; /// Obtains a buffer queue identified by the ID. - BufferQueue& FindBufferQueue(u32 id); + [[nodiscard]] BufferQueue& FindBufferQueue(u32 id); /// Obtains a buffer queue identified by the ID. - const BufferQueue& FindBufferQueue(u32 id) const; + [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const; /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// finished. void Compose(); - s64 GetNextTicks() const; + [[nodiscard]] s64 GetNextTicks() const; - std::unique_lock<std::mutex> Lock() { - return std::unique_lock{*guard}; - } + [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; } -private: - /// Finds the display identified by the specified ID. - VI::Display* FindDisplay(u64 display_id); + private : + /// Finds the display identified by the specified ID. + [[nodiscard]] VI::Display* FindDisplay(u64 display_id); /// Finds the display identified by the specified ID. - const VI::Display* FindDisplay(u64 display_id) const; + [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const; /// Finds the layer identified by the specified ID in the desired display. - VI::Layer* FindLayer(u64 display_id, u64 layer_id); + [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); /// Finds the layer identified by the specified ID in the desired display. - const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; + [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; static void VSyncThread(NVFlinger& nv_flinger); diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index ea7b4ae13..825d11a3f 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -511,7 +511,7 @@ private: LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, static_cast<u32>(transaction), flags); - nv_flinger->Lock(); + const auto guard = nv_flinger->Lock(); auto& buffer_queue = nv_flinger->FindBufferQueue(id); switch (transaction) { @@ -551,7 +551,7 @@ private: [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available - nv_flinger->Lock(); + const auto guard = nv_flinger->Lock(); auto& buffer_queue = nv_flinger->FindBufferQueue(id); auto result = buffer_queue.DequeueBuffer(width, height); ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index ced41b1fe..eeebdf02e 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -188,11 +188,11 @@ CheatEngine::~CheatEngine() { } void CheatEngine::Initialize() { - event = Core::Timing::CreateEvent("CheatEngine::FrameCallback::" + - Common::HexToString(metadata.main_nso_build_id), - [this](u64 userdata, std::chrono::nanoseconds ns_late) { - FrameCallback(userdata, ns_late); - }); + event = Core::Timing::CreateEvent( + "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), + [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + FrameCallback(user_data, ns_late); + }); core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); metadata.process_id = system.CurrentProcess()->GetProcessID(); @@ -219,7 +219,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) { MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); -void CheatEngine::FrameCallback(u64, std::chrono::nanoseconds ns_late) { +void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { if (is_pending_reload.exchange(false)) { vm.LoadProgram(cheats); } diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index d4068cf84..fa039a831 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -72,7 +72,7 @@ public: void Reload(std::vector<CheatEntry> cheats); private: - void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late); + void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); DmntCheatVm vm; CheatProcessMetadata metadata; diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp new file mode 100644 index 000000000..56d173b5e --- /dev/null +++ b/src/core/network/network.cpp @@ -0,0 +1,654 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstring> +#include <limits> +#include <utility> +#include <vector> + +#ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname +#include <winsock2.h> +#elif __unix__ +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <netinet/in.h> +#include <poll.h> +#include <sys/socket.h> +#include <unistd.h> +#else +#error "Unimplemented platform" +#endif + +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/network/network.h" +#include "core/network/sockets.h" + +namespace Network { + +namespace { + +#ifdef _WIN32 + +using socklen_t = int; + +void Initialize() { + WSADATA wsa_data; + (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); +} + +void Finalize() { + WSACleanup(); +} + +constexpr IPv4Address TranslateIPv4(in_addr addr) { + auto& bytes = addr.S_un.S_un_b; + return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; +} + +sockaddr TranslateFromSockAddrIn(SockAddrIn input) { + sockaddr_in result; + +#ifdef __unix__ + result.sin_len = sizeof(result); +#endif + + switch (static_cast<Domain>(input.family)) { + case Domain::INET: + result.sin_family = AF_INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family)); + result.sin_family = AF_INET; + break; + } + + result.sin_port = htons(input.portno); + + auto& ip = result.sin_addr.S_un.S_un_b; + ip.s_b1 = input.ip[0]; + ip.s_b2 = input.ip[1]; + ip.s_b3 = input.ip[2]; + ip.s_b4 = input.ip[3]; + + sockaddr addr; + std::memcpy(&addr, &result, sizeof(addr)); + return addr; +} + +LINGER MakeLinger(bool enable, u32 linger_value) { + ASSERT(linger_value <= std::numeric_limits<u_short>::max()); + + LINGER value; + value.l_onoff = enable ? 1 : 0; + value.l_linger = static_cast<u_short>(linger_value); + return value; +} + +int LastError() { + return WSAGetLastError(); +} + +bool EnableNonBlock(SOCKET fd, bool enable) { + u_long value = enable ? 1 : 0; + return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; +} + +#elif __unix__ // ^ _WIN32 v __unix__ + +using SOCKET = int; +using WSAPOLLFD = pollfd; +using ULONG = u64; + +constexpr SOCKET INVALID_SOCKET = -1; +constexpr SOCKET SOCKET_ERROR = -1; + +constexpr int WSAEWOULDBLOCK = EAGAIN; +constexpr int WSAENOTCONN = ENOTCONN; + +constexpr int SD_RECEIVE = SHUT_RD; +constexpr int SD_SEND = SHUT_WR; +constexpr int SD_BOTH = SHUT_RDWR; + +void Initialize() {} + +void Finalize() {} + +constexpr IPv4Address TranslateIPv4(in_addr addr) { + const u32 bytes = addr.s_addr; + return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), + static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; +} + +sockaddr TranslateFromSockAddrIn(SockAddrIn input) { + sockaddr_in result; + + switch (static_cast<Domain>(input.family)) { + case Domain::INET: + result.sin_family = AF_INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family)); + result.sin_family = AF_INET; + break; + } + + result.sin_port = htons(input.portno); + + result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; + + sockaddr addr; + std::memcpy(&addr, &result, sizeof(addr)); + return addr; +} + +int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { + return poll(fds, nfds, timeout); +} + +int closesocket(SOCKET fd) { + return close(fd); +} + +linger MakeLinger(bool enable, u32 linger_value) { + linger value; + value.l_onoff = enable ? 1 : 0; + value.l_linger = linger_value; + return value; +} + +int LastError() { + return errno; +} + +bool EnableNonBlock(int fd, bool enable) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + return false; + } + if (enable) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + return fcntl(fd, F_SETFD, flags) == 0; +} + +#endif + +int TranslateDomain(Domain domain) { + switch (domain) { + case Domain::INET: + return AF_INET; + default: + UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain)); + return 0; + } +} + +int TranslateType(Type type) { + switch (type) { + case Type::STREAM: + return SOCK_STREAM; + case Type::DGRAM: + return SOCK_DGRAM; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type)); + return 0; + } +} + +int TranslateProtocol(Protocol protocol) { + switch (protocol) { + case Protocol::TCP: + return IPPROTO_TCP; + case Protocol::UDP: + return IPPROTO_UDP; + default: + UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol)); + return 0; + } +} + +SockAddrIn TranslateToSockAddrIn(sockaddr input_) { + sockaddr_in input; + std::memcpy(&input, &input_, sizeof(input)); + + SockAddrIn result; + + switch (input.sin_family) { + case AF_INET: + result.family = Domain::INET; + break; + default: + UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); + result.family = Domain::INET; + break; + } + + result.portno = ntohs(input.sin_port); + + result.ip = TranslateIPv4(input.sin_addr); + + return result; +} + +u16 TranslatePollEvents(u16 events) { + u16 result = 0; + + if (events & POLL_IN) { + events &= ~POLL_IN; + result |= POLLIN; + } + if (events & POLL_PRI) { + events &= ~POLL_PRI; +#ifdef _WIN32 + LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); +#else + result |= POLL_PRI; +#endif + } + if (events & POLL_OUT) { + events &= ~POLL_OUT; + result |= POLLOUT; + } + + UNIMPLEMENTED_IF_MSG(events != 0, "Unhandled guest events=0x{:x}", events); + + return result; +} + +u16 TranslatePollRevents(u16 revents) { + u16 result = 0; + const auto translate = [&result, &revents](int host, unsigned guest) { + if (revents & host) { + revents &= ~host; + result |= guest; + } + }; + + translate(POLLIN, POLL_IN); + translate(POLLPRI, POLL_PRI); + translate(POLLOUT, POLL_OUT); + translate(POLLERR, POLL_ERR); + translate(POLLHUP, POLL_HUP); + + UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); + + return result; +} + +template <typename T> +Errno SetSockOpt(SOCKET fd, int option, T value) { + const int result = + setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); + if (result != SOCKET_ERROR) { + return Errno::SUCCESS; + } + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return Errno::SUCCESS; +} + +} // Anonymous namespace + +NetworkInstance::NetworkInstance() { + Initialize(); +} + +NetworkInstance::~NetworkInstance() { + Finalize(); +} + +std::pair<IPv4Address, Errno> GetHostIPv4Address() { + std::array<char, 256> name{}; + if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { + UNIMPLEMENTED_MSG("Unhandled gethostname error"); + return {IPv4Address{}, Errno::SUCCESS}; + } + + hostent* const ent = gethostbyname(name.data()); + if (!ent) { + UNIMPLEMENTED_MSG("Unhandled gethostbyname error"); + return {IPv4Address{}, Errno::SUCCESS}; + } + if (ent->h_addr_list == nullptr) { + UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); + return {IPv4Address{}, Errno::SUCCESS}; + } + if (ent->h_length != sizeof(in_addr)) { + UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length); + } + + in_addr addr; + std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); + return {TranslateIPv4(addr), Errno::SUCCESS}; +} + +std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { + const size_t num = pollfds.size(); + + std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); + std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { + WSAPOLLFD result; + result.fd = fd.socket->fd; + result.events = TranslatePollEvents(fd.events); + result.revents = 0; + return result; + }); + + const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); + if (result == 0) { + ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), + [](WSAPOLLFD fd) { return fd.revents == 0; })); + return {0, Errno::SUCCESS}; + } + + for (size_t i = 0; i < num; ++i) { + pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); + } + + if (result > 0) { + return {result, Errno::SUCCESS}; + } + + ASSERT(result == SOCKET_ERROR); + + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return {-1, Errno::SUCCESS}; +} + +Socket::~Socket() { + if (fd == INVALID_SOCKET) { + return; + } + (void)closesocket(fd); + fd = INVALID_SOCKET; +} + +Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} + +Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { + fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); + if (fd != INVALID_SOCKET) { + return Errno::SUCCESS; + } + + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return Errno::SUCCESS; +} + +std::pair<Socket::AcceptResult, Errno> Socket::Accept() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + const SOCKET new_socket = accept(fd, &addr, &addrlen); + + if (new_socket == INVALID_SOCKET) { + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return {AcceptResult{}, Errno::SUCCESS}; + } + + AcceptResult result; + result.socket = std::make_unique<Socket>(); + result.socket->fd = new_socket; + + ASSERT(addrlen == sizeof(sockaddr_in)); + result.sockaddr_in = TranslateToSockAddrIn(addr); + + return {std::move(result), Errno::SUCCESS}; +} + +Errno Socket::Connect(SockAddrIn addr_in) { + const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); + if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != INVALID_SOCKET) { + return Errno::SUCCESS; + } + + switch (const int ec = LastError()) { + case WSAEWOULDBLOCK: + LOG_DEBUG(Service, "EAGAIN generated"); + return Errno::AGAIN; + default: + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return Errno::SUCCESS; + } +} + +std::pair<SockAddrIn, Errno> Socket::GetPeerName() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return {SockAddrIn{}, Errno::SUCCESS}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; +} + +std::pair<SockAddrIn, Errno> Socket::GetSockName() { + sockaddr addr; + socklen_t addrlen = sizeof(addr); + if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return {SockAddrIn{}, Errno::SUCCESS}; + } + + ASSERT(addrlen == sizeof(sockaddr_in)); + return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; +} + +Errno Socket::Bind(SockAddrIn addr) { + const sockaddr addr_in = TranslateFromSockAddrIn(addr); + if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return Errno::SUCCESS; +} + +Errno Socket::Listen(s32 backlog) { + if (listen(fd, backlog) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return Errno::SUCCESS; +} + +Errno Socket::Shutdown(ShutdownHow how) { + int host_how = 0; + switch (how) { + case ShutdownHow::RD: + host_how = SD_RECEIVE; + break; + case ShutdownHow::WR: + host_how = SD_SEND; + break; + case ShutdownHow::RDWR: + host_how = SD_BOTH; + break; + default: + UNIMPLEMENTED_MSG("Unimplemented flag how={}", static_cast<int>(how)); + return Errno::SUCCESS; + } + if (shutdown(fd, host_how) != SOCKET_ERROR) { + return Errno::SUCCESS; + } + + switch (const int ec = LastError()) { + case WSAENOTCONN: + LOG_ERROR(Service, "ENOTCONN generated"); + return Errno::NOTCONN; + default: + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return Errno::SUCCESS; + } +} + +std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + + const int result = + recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0); + if (result != SOCKET_ERROR) { + return {result, Errno::SUCCESS}; + } + + switch (const int ec = LastError()) { + case WSAEWOULDBLOCK: + LOG_DEBUG(Service, "EAGAIN generated"); + return {-1, Errno::AGAIN}; + case WSAENOTCONN: + LOG_ERROR(Service, "ENOTCONN generated"); + return {-1, Errno::NOTCONN}; + default: + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return {0, Errno::SUCCESS}; + } +} + +std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { + ASSERT(flags == 0); + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + + sockaddr addr_in{}; + socklen_t addrlen = sizeof(addr_in); + socklen_t* const p_addrlen = addr ? &addrlen : nullptr; + sockaddr* const p_addr_in = addr ? &addr_in : nullptr; + + const int result = recvfrom(fd, reinterpret_cast<char*>(message.data()), + static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); + if (result != SOCKET_ERROR) { + if (addr) { + ASSERT(addrlen == sizeof(addr_in)); + *addr = TranslateToSockAddrIn(addr_in); + } + return {result, Errno::SUCCESS}; + } + + switch (const int ec = LastError()) { + case WSAEWOULDBLOCK: + LOG_DEBUG(Service, "EAGAIN generated"); + return {-1, Errno::AGAIN}; + case WSAENOTCONN: + LOG_ERROR(Service, "ENOTCONN generated"); + return {-1, Errno::NOTCONN}; + default: + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return {-1, Errno::SUCCESS}; + } +} + +std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { + ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); + ASSERT(flags == 0); + + const int result = send(fd, reinterpret_cast<const char*>(message.data()), + static_cast<int>(message.size()), 0); + if (result != SOCKET_ERROR) { + return {result, Errno::SUCCESS}; + } + + const int ec = LastError(); + switch (ec) { + case WSAEWOULDBLOCK: + LOG_DEBUG(Service, "EAGAIN generated"); + return {-1, Errno::AGAIN}; + case WSAENOTCONN: + LOG_ERROR(Service, "ENOTCONN generated"); + return {-1, Errno::NOTCONN}; + default: + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return {-1, Errno::SUCCESS}; + } +} + +std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, + const SockAddrIn* addr) { + ASSERT(flags == 0); + + const sockaddr* to = nullptr; + const int tolen = addr ? 0 : sizeof(sockaddr); + sockaddr host_addr_in; + + if (addr) { + host_addr_in = TranslateFromSockAddrIn(*addr); + to = &host_addr_in; + } + + const int result = sendto(fd, reinterpret_cast<const char*>(message.data()), + static_cast<int>(message.size()), 0, to, tolen); + if (result != SOCKET_ERROR) { + return {result, Errno::SUCCESS}; + } + + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return {-1, Errno::SUCCESS}; +} + +Errno Socket::Close() { + [[maybe_unused]] const int result = closesocket(fd); + ASSERT(result == 0); + fd = INVALID_SOCKET; + + return Errno::SUCCESS; +} + +Errno Socket::SetLinger(bool enable, u32 linger) { + return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); +} + +Errno Socket::SetReuseAddr(bool enable) { + return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); +} + +Errno Socket::SetBroadcast(bool enable) { + return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); +} + +Errno Socket::SetSndBuf(u32 value) { + return SetSockOpt(fd, SO_SNDBUF, value); +} + +Errno Socket::SetRcvBuf(u32 value) { + return SetSockOpt(fd, SO_RCVBUF, value); +} + +Errno Socket::SetSndTimeo(u32 value) { + return SetSockOpt(fd, SO_SNDTIMEO, value); +} + +Errno Socket::SetRcvTimeo(u32 value) { + return SetSockOpt(fd, SO_RCVTIMEO, value); +} + +Errno Socket::SetNonBlock(bool enable) { + if (EnableNonBlock(fd, enable)) { + return Errno::SUCCESS; + } + const int ec = LastError(); + UNREACHABLE_MSG("Unhandled host socket error={}", ec); + return Errno::SUCCESS; +} + +bool Socket::IsOpened() const { + return fd != INVALID_SOCKET; +} + +} // namespace Network diff --git a/src/core/network/network.h b/src/core/network/network.h new file mode 100644 index 000000000..0622e4593 --- /dev/null +++ b/src/core/network/network.h @@ -0,0 +1,87 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <utility> + +#include "common/common_types.h" + +namespace Network { + +class Socket; + +/// Error code for network functions +enum class Errno { + SUCCESS, + BADF, + INVAL, + MFILE, + NOTCONN, + AGAIN, +}; + +/// Address families +enum class Domain { + INET, ///< Address family for IPv4 +}; + +/// Socket types +enum class Type { + STREAM, + DGRAM, + RAW, + SEQPACKET, +}; + +/// Protocol values for sockets +enum class Protocol { + ICMP, + TCP, + UDP, +}; + +/// Shutdown mode +enum class ShutdownHow { + RD, + WR, + RDWR, +}; + +/// Array of IPv4 address +using IPv4Address = std::array<u8, 4>; + +/// Cross-platform sockaddr structure +struct SockAddrIn { + Domain family; + IPv4Address ip; + u16 portno; +}; + +/// Cross-platform poll fd structure +struct PollFD { + Socket* socket; + u16 events; + u16 revents; +}; + +constexpr u16 POLL_IN = 1 << 0; +constexpr u16 POLL_PRI = 1 << 1; +constexpr u16 POLL_OUT = 1 << 2; +constexpr u16 POLL_ERR = 1 << 3; +constexpr u16 POLL_HUP = 1 << 4; +constexpr u16 POLL_NVAL = 1 << 5; + +class NetworkInstance { +public: + explicit NetworkInstance(); + ~NetworkInstance(); +}; + +/// @brief Returns host's IPv4 address +/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code +std::pair<IPv4Address, Errno> GetHostIPv4Address(); + +} // namespace Network diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h new file mode 100644 index 000000000..7bdff0fe4 --- /dev/null +++ b/src/core/network/sockets.h @@ -0,0 +1,94 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <utility> + +#if defined(_WIN32) +#include <winsock.h> +#elif !defined(__unix__) +#error "Platform not implemented" +#endif + +#include "common/common_types.h" +#include "core/network/network.h" + +// TODO: C++20 Replace std::vector usages with std::span + +namespace Network { + +class Socket { +public: + struct AcceptResult { + std::unique_ptr<Socket> socket; + SockAddrIn sockaddr_in; + }; + + explicit Socket() = default; + ~Socket(); + + Socket(const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + Socket(Socket&& rhs) noexcept; + + // Avoid closing sockets implicitly + Socket& operator=(Socket&&) noexcept = delete; + + Errno Initialize(Domain domain, Type type, Protocol protocol); + + Errno Close(); + + std::pair<AcceptResult, Errno> Accept(); + + Errno Connect(SockAddrIn addr_in); + + std::pair<SockAddrIn, Errno> GetPeerName(); + + std::pair<SockAddrIn, Errno> GetSockName(); + + Errno Bind(SockAddrIn addr); + + Errno Listen(s32 backlog); + + Errno Shutdown(ShutdownHow how); + + std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); + + std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); + + std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); + + std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); + + Errno SetLinger(bool enable, u32 linger); + + Errno SetReuseAddr(bool enable); + + Errno SetBroadcast(bool enable); + + Errno SetSndBuf(u32 value); + + Errno SetRcvBuf(u32 value); + + Errno SetSndTimeo(u32 value); + + Errno SetRcvTimeo(u32 value); + + Errno SetNonBlock(bool enable); + + bool IsOpened() const; + +#if defined(_WIN32) + SOCKET fd = INVALID_SOCKET; +#elif defined(__unix__) + int fd = -1; +#endif +}; + +std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); + +} // namespace Network diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 44252dd81..416b2d866 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -173,7 +173,6 @@ void RestoreGlobalState() { values.use_assembly_shaders.SetGlobal(true); values.use_asynchronous_shaders.SetGlobal(true); values.use_fast_gpu_time.SetGlobal(true); - values.force_30fps_mode.SetGlobal(true); values.bg_red.SetGlobal(true); values.bg_green.SetGlobal(true); values.bg_blue.SetGlobal(true); diff --git a/src/core/settings.h b/src/core/settings.h index 386233fdf..bb145f193 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -435,7 +435,6 @@ struct Values { Setting<bool> use_vsync; Setting<bool> use_assembly_shaders; Setting<bool> use_asynchronous_shaders; - Setting<bool> force_30fps_mode; Setting<bool> use_fast_gpu_time; Setting<float> bg_red; diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 27b894b51..2003e096f 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -55,10 +55,11 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) : core_timing{core_timing_}, memory{memory_} { - event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback", - [this](u64 userdata, std::chrono::nanoseconds ns_late) { - FrameCallback(userdata, ns_late); - }); + event = Core::Timing::CreateEvent( + "MemoryFreezer::FrameCallback", + [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + FrameCallback(user_data, ns_late); + }); core_timing.ScheduleEvent(memory_freezer_ns, event); } @@ -159,7 +160,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const { return entries; } -void Freezer::FrameCallback(u64, std::chrono::nanoseconds ns_late) { +void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { if (!IsActive()) { LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); return; diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index 8438783d5..2b2326bc4 100644 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h @@ -73,7 +73,7 @@ public: std::vector<Entry> GetEntries() const; private: - void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late); + void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void FillEntryReads(); std::atomic_bool active{false}; |