Compare commits

..

3 Commits

Author SHA1 Message Date
german77
56115f7732 core: ldn: Address review comments part 2 2022-08-11 23:12:32 -05:00
Narr the Reg
8eb2c73381 core: ldn: Address review comments 2022-08-08 12:49:48 -05:00
FearlessTobi
12dc05032b ldn: Add better stubs and more data types
Co-Authored-By: Narr the Reg <5944268+german77@users.noreply.github.com>
Co-Authored-By: Morph <39850852+Morph1984@users.noreply.github.com>
2022-08-07 23:54:51 +02:00
43 changed files with 931 additions and 532 deletions

View File

@@ -8,10 +8,8 @@ cp LICENSE.txt "$DIR_NAME"
cp README.md "$DIR_NAME"
if [[ -z "${NO_SOURCE_PACK}" ]]; then
git clone --depth 1 file://$(readlink -e .) ${REV_NAME}-source
tar -cJvf "${REV_NAME}-source.tar.xz" ${REV_NAME}-source
tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md LICENSE.txt
cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME"
cp -v "${REV_NAME}-source.tar.xz" "${ARTIFACTS_DIR}/"
fi
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"

View File

@@ -42,10 +42,14 @@ mkdir $RELEASE_DIST
mkdir $MSVC_SOURCE
mkdir "artifacts"
$CURRENT_DIR = Convert-Path .
# Build a tar.xz for the source of the release
git clone --depth 1 file://$CURRENT_DIR $MSVC_SOURCE
Copy-Item .\LICENSE.txt -Destination $MSVC_SOURCE
Copy-Item .\README.md -Destination $MSVC_SOURCE
Copy-Item .\CMakeLists.txt -Destination $MSVC_SOURCE
Copy-Item .\src -Recurse -Destination $MSVC_SOURCE
Copy-Item .\externals -Recurse -Destination $MSVC_SOURCE
Copy-Item .\dist -Recurse -Destination $MSVC_SOURCE
Copy-Item .\CMakeModules -Recurse -Destination $MSVC_SOURCE
7z a -r -ttar $MSVC_SOURCE_TAR $MSVC_SOURCE
7z a -r -txz $MSVC_SOURCE_TARXZ $MSVC_SOURCE_TAR

2
.gitignore vendored
View File

@@ -7,7 +7,7 @@ doc-build/
# Generated source files
src/common/scm_rev.cpp
dist/english_plurals/generated_en.ts
.travis.descriptor.json
# Project/editor files
*.swp

View File

@@ -2,8 +2,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Comment: It is best to use this file to record copyright information about
generated, binary and third party files
Files: dist/english_plurals/*
dist/icons/controller/*.png
Files: dist/icons/controller/*.png
dist/icons/overlay/*.png
dist/languages/*
dist/qt_themes/*/icons/index.theme

View File

@@ -1,19 +0,0 @@
# English Plurals
Qt has "Translation Rules for Plurals", small example
// Take a source line like
tr("Building: %n shader(s)", "", i)
// i = 1:
Building: 1 shader
// i = 2:
Building: 2 shaders
For yuzu the source language used is English, for all other languages handling of plurals is handled by Qt and the translation collaboration site. Handling plurals in the source language (English) requires special consideration.
With CMake flag GENERATE_QT_TRANSLATION a generated_en.ts file is created from the source. It ignored by git (`.gitignore` in the project root). It is placed in this directory so that the relative refrences with the source code is correct.
Having the plurals look nice isn't critical, and automation to use translation collaboration sites may require specifing the project language as "Pirate English", so this has been done manually.
The en.ts in this directory is taken from a build, edited in Qt Linguist and then committed. As the code is in XML, using the tool is not strictly required.

View File

@@ -1,67 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US" sourcelanguage="en_US">
<context>
<name>GMainWindow</name>
<message numerus="yes">
<location filename="../../src/yuzu/main.cpp" line="2322"/>
<source>%n file(s) remaining</source>
<translation>
<numerusform>%n file remaining</numerusform>
<numerusform>%n files remaining</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../../src/yuzu/main.cpp" line="2377"/>
<source>%n file(s) were newly installed
</source>
<translation>
<numerusform>%n file was newly installed
</numerusform>
<numerusform>%n files were newly installed
</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../../src/yuzu/main.cpp" line="2380"/>
<source>%n file(s) were overwritten
</source>
<translation>
<numerusform>%n file was overwritten
</numerusform>
<numerusform>%n were overwritten
</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../../src/yuzu/main.cpp" line="2382"/>
<source>%n file(s) failed to install
</source>
<translation>
<numerusform>%n file failed to install
</numerusform>
<numerusform>%n files failed to install
</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../../src/yuzu/main.cpp" line="3264"/>
<source>Building: %n shader(s)</source>
<translation>
<numerusform>Building: %n shader</numerusform>
<numerusform>Building: %n shaders</numerusform>
</translation>
</message>
</context>
<context>
<name>GameListSearchField</name>
<message numerus="yes">
<location filename="../../src/yuzu/game_list.cpp" line="87"/>
<source>%1 of %n result(s)</source>
<translation>
<numerusform>%1 of %n result</numerusform>
<numerusform>%1 of %n results</numerusform>
</translation>
</message>
</context>
</TS>

View File

@@ -6,6 +6,8 @@ add_library(core STATIC
announce_multiplayer_session.h
arm/arm_interface.h
arm/arm_interface.cpp
arm/cpu_interrupt_handler.cpp
arm/cpu_interrupt_handler.h
arm/dynarmic/arm_dynarmic_32.cpp
arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_64.cpp
@@ -167,9 +169,6 @@ add_library(core STATIC
hle/api_version.h
hle/ipc.h
hle/ipc_helpers.h
hle/kernel/arch/arm64/exception_handlers.cpp
hle/kernel/arch/arm64/exception_handlers.h
hle/kernel/arch/arm64/k_exception_context.h
hle/kernel/board/nintendo/nx/k_memory_layout.h
hle/kernel/board/nintendo/nx/k_system_control.cpp
hle/kernel/board/nintendo/nx/k_system_control.h
@@ -505,9 +504,10 @@ add_library(core STATIC
hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/errors.h
hle/service/ldn/ldn_results.h
hle/service/ldn/ldn.cpp
hle/service/ldn/ldn.h
hle/service/ldn/ldn_types.h
hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp

View File

@@ -134,13 +134,6 @@ void ARM_Interface::Run() {
}
system.ExitDynarmicProfile();
// Exception occurred, pass on to kernel to handle it.
if (exception_context != std::nullopt) {
const auto exception_context_ = *exception_context;
exception_context.reset();
Kernel::Arch::Arm64::HandleException(system.Kernel(), exception_context_);
}
// Notify the debugger and go to sleep if a breakpoint was hit,
// or if the thread is unable to continue for any reason.
if (Has(hr, breakpoint) || Has(hr, no_execute)) {

View File

@@ -4,7 +4,6 @@
#pragma once
#include <array>
#include <optional>
#include <span>
#include <vector>
@@ -13,7 +12,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/arch/arm64/exception_handlers.h"
namespace Common {
struct PageTable;
@@ -29,6 +27,7 @@ namespace Core {
class System;
class CPUInterruptHandler;
using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
/// Generic ARMv8 CPU interface
@@ -37,8 +36,10 @@ public:
YUZU_NON_COPYABLE(ARM_Interface);
YUZU_NON_MOVEABLE(ARM_Interface);
explicit ARM_Interface(System& system_, bool uses_wall_clock_)
: system{system_}, uses_wall_clock{uses_wall_clock_} {}
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
bool uses_wall_clock_)
: system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
uses_wall_clock_} {}
virtual ~ARM_Interface() = default;
struct ThreadContext32 {
@@ -180,9 +181,6 @@ public:
/// Signal an interrupt and ask the core to halt as soon as possible.
virtual void SignalInterrupt() = 0;
/// Clear a previous interrupt.
virtual void ClearInterrupt() = 0;
struct BacktraceEntry {
std::string module;
u64 address;
@@ -210,9 +208,9 @@ public:
protected:
/// System context that this ARM interface is running under.
System& system;
CPUInterrupts& interrupt_handlers;
const WatchpointArray* watchpoints;
bool uses_wall_clock;
std::optional<Kernel::Arch::Arm64::KExceptionContext> exception_context;
static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
const Kernel::DebugWatchpoint* MatchingWatchpoint(

View File

@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/thread.h"
#include "core/arm/cpu_interrupt_handler.h"
namespace Core {
CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
CPUInterruptHandler::~CPUInterruptHandler() = default;
void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
if (is_interrupted_) {
interrupt_event->Set();
}
is_interrupted = is_interrupted_;
}
void CPUInterruptHandler::AwaitInterrupt() {
interrupt_event->Wait();
}
} // namespace Core

View File

@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <memory>
namespace Common {
class Event;
}
namespace Core {
class CPUInterruptHandler {
public:
CPUInterruptHandler();
~CPUInterruptHandler();
CPUInterruptHandler(const CPUInterruptHandler&) = delete;
CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
CPUInterruptHandler(CPUInterruptHandler&&) = delete;
CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
bool IsInterrupted() const {
return is_interrupted;
}
void SetInterrupt(bool is_interrupted);
void AwaitInterrupt();
private:
std::unique_ptr<Common::Event> interrupt_event;
std::atomic_bool is_interrupted{false};
};
} // namespace Core

View File

@@ -11,6 +11,7 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
@@ -104,11 +105,11 @@ public:
case Dynarmic::A32::Exception::NoExecuteFault:
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc);
ReturnException(pc, ARM_Interface::no_execute);
break;
return;
default:
if (debugger_enabled) {
ReturnException(pc, ARM_Interface::breakpoint);
break;
return;
}
parent.LogBacktrace();
@@ -116,21 +117,6 @@ public:
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, memory.Read32(pc), parent.IsInThumbMode());
}
// Load KExceptionContext
Kernel::Arch::Arm64::KExceptionContext exception_context{};
Core::ARM_Interface::ThreadContext32 thread_context{};
parent.LoadContext(thread_context);
for (size_t index = 0; index < thread_context.cpu_registers.size(); ++index) {
exception_context.x[index] = thread_context.cpu_registers[index];
}
exception_context.sp = thread_context.cpu_registers[13];
exception_context.pc = thread_context.cpu_registers[15];
exception_context.psr = thread_context.cpsr;
exception_context.tpidr = thread_context.tpidr;
// Kernel to handle exception on exit
parent.exception_context = exception_context;
}
void CallSVC(u32 swi) override {
@@ -139,9 +125,7 @@ public:
}
void AddTicks(u64 ticks) override {
if (parent.uses_wall_clock) {
return;
}
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -158,12 +142,7 @@ public:
}
u64 GetTicksRemaining() override {
if (parent.uses_wall_clock) {
if (!IsInterrupted()) {
return minimum_run_cycles;
}
return 0U;
}
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -189,15 +168,11 @@ public:
parent.jit.load()->HaltExecution(hr);
}
bool IsInterrupted() {
return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
}
ARM_Dynarmic_32& parent;
Core::Memory::Memory& memory;
std::size_t num_interpreted_instructions{};
bool debugger_enabled{};
static constexpr u64 minimum_run_cycles = 10000U;
static constexpr u64 minimum_run_cycles = 1000U;
};
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const {
@@ -225,7 +200,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
config.enable_cycle_counting = true;
config.enable_cycle_counting = !uses_wall_clock;
// Code cache size
config.code_cache_size = 512_MiB;
@@ -336,9 +311,11 @@ void ARM_Dynarmic_32::RewindBreakpointInstruction() {
LoadContext(breakpoint_context);
}
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
: ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
std::size_t core_index_)
: ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
cb(std::make_unique<DynarmicCallbacks32>(*this)),
cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
@@ -417,10 +394,6 @@ void ARM_Dynarmic_32::SignalInterrupt() {
jit.load()->HaltExecution(break_loop);
}
void ARM_Dynarmic_32::ClearInterrupt() {
jit.load()->ClearHalt(break_loop);
}
void ARM_Dynarmic_32::ClearInstructionCache() {
jit.load()->ClearCache();
}

View File

@@ -28,8 +28,8 @@ class System;
class ARM_Dynarmic_32 final : public ARM_Interface {
public:
ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
std::size_t core_index_);
ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
~ARM_Dynarmic_32() override;
void SetPC(u64 pc) override;
@@ -56,7 +56,6 @@ public:
void LoadContext(const ThreadContext64& ctx) override {}
void SignalInterrupt() override;
void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;

View File

@@ -10,6 +10,7 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
@@ -142,36 +143,21 @@ public:
case Dynarmic::A64::Exception::SendEvent:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
break;
return;
case Dynarmic::A64::Exception::NoExecuteFault:
LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
ReturnException(pc, ARM_Interface::no_execute);
break;
return;
default:
if (debugger_enabled) {
ReturnException(pc, ARM_Interface::breakpoint);
break;
return;
}
parent.LogBacktrace();
LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
static_cast<std::size_t>(exception), pc, memory.Read32(pc));
}
// Load KExceptionContext
Kernel::Arch::Arm64::KExceptionContext exception_context{};
Core::ARM_Interface::ThreadContext64 thread_context{};
parent.LoadContext(thread_context);
for (size_t index = 0; index < thread_context.cpu_registers.size(); ++index) {
exception_context.x[index] = thread_context.cpu_registers[index];
}
exception_context.sp = thread_context.sp;
exception_context.pc = thread_context.pc;
exception_context.psr = thread_context.pstate;
exception_context.tpidr = thread_context.tpidr;
// Kernel to handle exception on exit
parent.exception_context = exception_context;
}
void CallSVC(u32 swi) override {
@@ -180,9 +166,7 @@ public:
}
void AddTicks(u64 ticks) override {
if (parent.uses_wall_clock) {
return;
}
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -197,12 +181,7 @@ public:
}
u64 GetTicksRemaining() override {
if (parent.uses_wall_clock) {
if (!IsInterrupted()) {
return minimum_run_cycles;
}
return 0U;
}
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -232,16 +211,12 @@ public:
parent.jit.load()->HaltExecution(hr);
}
bool IsInterrupted() {
return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
}
ARM_Dynarmic_64& parent;
Core::Memory::Memory& memory;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
bool debugger_enabled{};
static constexpr u64 minimum_run_cycles = 10000U;
static constexpr u64 minimum_run_cycles = 1000U;
};
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table,
@@ -285,7 +260,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
config.enable_cycle_counting = true;
config.enable_cycle_counting = !uses_wall_clock;
// Code cache size
config.code_cache_size = 512_MiB;
@@ -396,9 +371,10 @@ void ARM_Dynarmic_64::RewindBreakpointInstruction() {
LoadContext(breakpoint_context);
}
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
: ARM_Interface{system_, uses_wall_clock_},
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
std::size_t core_index_)
: ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
@@ -485,10 +461,6 @@ void ARM_Dynarmic_64::SignalInterrupt() {
jit.load()->HaltExecution(break_loop);
}
void ARM_Dynarmic_64::ClearInterrupt() {
jit.load()->ClearHalt(break_loop);
}
void ARM_Dynarmic_64::ClearInstructionCache() {
jit.load()->ClearCache();
}

View File

@@ -20,13 +20,14 @@ class Memory;
namespace Core {
class DynarmicCallbacks64;
class CPUInterruptHandler;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic_64 final : public ARM_Interface {
public:
ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
std::size_t core_index_);
ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
~ARM_Dynarmic_64() override;
void SetPC(u64 pc) override;
@@ -49,7 +50,6 @@ public:
void LoadContext(const ThreadContext64& ctx) override;
void SignalInterrupt() override;
void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;

View File

@@ -8,10 +8,6 @@
#include "core/core.h"
#include "core/core_timing.h"
#ifdef _MSC_VER
#include <intrin.h>
#endif
using Callback = Dynarmic::A32::Coprocessor::Callback;
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
@@ -51,31 +47,12 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
switch (opc2) {
case 4:
// CP15_DATA_SYNC_BARRIER
return Callback{
[](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
#ifdef _MSC_VER
_mm_mfence();
_mm_lfence();
#else
asm volatile("mfence\n\tlfence\n\t" : : : "memory");
#endif
return 0;
},
std::nullopt,
};
// This is a dummy write, we ignore the value written here.
return &dummy_value;
case 5:
// CP15_DATA_MEMORY_BARRIER
return Callback{
[](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
#ifdef _MSC_VER
_mm_mfence();
#else
asm volatile("mfence\n\t" : : : "memory");
#endif
return 0;
},
std::nullopt,
};
// This is a dummy write, we ignore the value written here.
return &dummy_value;
}
}

View File

@@ -35,8 +35,6 @@ public:
ARM_Dynarmic_32& parent;
u32 uprw = 0;
u32 uro = 0;
friend class ARM_Dynarmic_32;
};
} // namespace Core

View File

@@ -15,7 +15,6 @@
#include "core/debugger/debugger_interface.h"
#include "core/debugger/gdbstub.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_scheduler.h"
template <typename Readable, typename Buffer, typename Callback>
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
@@ -231,12 +230,13 @@ private:
}
void PauseEmulation() {
Kernel::KScopedSchedulerLock sl{system.Kernel()};
// Put all threads to sleep on next scheduler round.
for (auto* thread : ThreadList()) {
thread->RequestSuspend(Kernel::SuspendType::Debug);
}
// Signal an interrupt so that scheduler will fire.
system.Kernel().InterruptAllPhysicalCores();
}
void ResumeEmulation(Kernel::KThread* except = nullptr) {
@@ -253,8 +253,7 @@ private:
template <typename Callback>
void MarkResumed(Callback&& cb) {
Kernel::KScopedSchedulerLock sl{system.Kernel()};
std::scoped_lock cl{connection_lock};
std::scoped_lock lk{connection_lock};
stopped = false;
cb();
}

View File

@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/thread.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/input_converter.h"
@@ -85,19 +84,18 @@ void EmulatedController::ReloadFromSettings() {
motion_params[index] = Common::ParamPackage(player.motions[index]);
}
controller.colors_state.fullkey = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
};
controller.colors_state.left = {
.body = GetNpadColor(player.body_color_left),
.button = GetNpadColor(player.button_color_left),
.body = player.body_color_left,
.button = player.button_color_left,
};
controller.colors_state.left = {
.body = GetNpadColor(player.body_color_right),
.button = GetNpadColor(player.button_color_right),
controller.colors_state.right = {
.body = player.body_color_right,
.button = player.button_color_right,
};
controller.colors_state.fullkey = controller.colors_state.left;
// Other or debug controller should always be a pro controller
if (npad_id_type != NpadIdType::Other) {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -951,9 +949,6 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
// Send a slight vibration to test for rumble support
output_devices[device_index]->SetVibration(test_vibration);
// Wait for about 15ms to ensure the controller is ready for the stop command
std::this_thread::sleep_for(std::chrono::milliseconds(15));
// Stop any vibration and return the result
return output_devices[device_index]->SetVibration(zero_vibration) ==
Common::Input::VibrationError::None;
@@ -1315,15 +1310,6 @@ const CameraState& EmulatedController::GetCamera() const {
return controller.camera_state;
}
NpadColor EmulatedController::GetNpadColor(u32 color) {
return {
.r = static_cast<u8>((color >> 16) & 0xFF),
.g = static_cast<u8>((color >> 8) & 0xFF),
.b = static_cast<u8>(color & 0xFF),
.a = 0xff,
};
}
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {

View File

@@ -424,13 +424,6 @@ private:
*/
void SetCamera(const Common::Input::CallbackStatus& callback);
/**
* Converts a color format from bgra to rgba
* @param color in bgra format
* @return NpadColor in rgba format
*/
NpadColor GetNpadColor(u32 color);
/**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger

View File

@@ -327,18 +327,10 @@ struct TouchState {
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
struct NpadColor {
u8 r{};
u8 g{};
u8 b{};
u8 a{};
};
static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");
// This is nn::hid::NpadControllerColor
struct NpadControllerColor {
NpadColor body{};
NpadColor button{};
u32 body{};
u32 button{};
};
static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");

View File

@@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/arch/arm64/exception_handlers.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel::Arch::Arm64 {
void HandleException(KernelCore& kernel, const KExceptionContext& context) {
auto* process = kernel.CurrentProcess();
if (!process) {
return;
}
const bool is_user_mode = (context.psr & 0xF) == 0;
if (is_user_mode) {
// If the user disable count is set, we may need to pin the current thread.
if (GetCurrentThread(kernel).GetUserDisableCount() != 0 &&
process->GetPinnedThread(GetCurrentCoreId(kernel)) == nullptr) {
KScopedSchedulerLock sl{kernel};
// Pin the current thread.
process->PinCurrentThread(GetCurrentCoreId(kernel));
// Set the interrupt flag for the thread.
GetCurrentThread(kernel).SetInterruptFlag();
}
}
}
} // namespace Kernel::Arch::Arm64

View File

@@ -1,15 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/arch/arm64/k_exception_context.h"
namespace Kernel {
class KernelCore;
}
namespace Kernel::Arch::Arm64 {
void HandleException(KernelCore& kernel, const KExceptionContext& context);
}

View File

@@ -1,21 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Kernel::Arch::Arm64 {
struct KExceptionContext {
u64 x[(30 - 0) + 1];
u64 sp;
u64 pc;
u32 psr;
u32 write;
u64 tpidr;
u64 reserved;
};
static_assert(sizeof(KExceptionContext) == 0x120);
} // namespace Kernel::Arch::Arm64

View File

@@ -17,6 +17,7 @@
#include "common/thread.h"
#include "common/thread_worker.h"
#include "core/arm/arm_interface.h"
#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -81,7 +82,7 @@ struct KernelCore::Impl {
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
cores[core_id]->Initialize((*current_process).Is64BitProcess());
cores[core_id].Initialize((*current_process).Is64BitProcess());
system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
@@ -99,9 +100,7 @@ struct KernelCore::Impl {
next_user_process_id = KProcess::ProcessIDMin;
next_thread_id = 1;
for (auto& core : cores) {
core = nullptr;
}
cores.clear();
global_handle_table->Finalize();
global_handle_table.reset();
@@ -200,7 +199,7 @@ struct KernelCore::Impl {
const s32 core{static_cast<s32>(i)};
schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]);
cores.emplace_back(i, system, *schedulers[i], interrupts);
auto* main_thread{Kernel::KThread::Create(system.Kernel())};
main_thread->SetName(fmt::format("MainThread:{}", core));
@@ -762,7 +761,7 @@ struct KernelCore::Impl {
std::unordered_set<KAutoObject*> registered_in_use_objects;
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
std::vector<Kernel::PhysicalCore> cores;
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
@@ -786,6 +785,7 @@ struct KernelCore::Impl {
Common::ThreadWorker service_threads_manager;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{};
@@ -874,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const {
}
Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
return *impl->cores[id];
return impl->cores[id];
}
const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
return *impl->cores[id];
return impl->cores[id];
}
size_t KernelCore::CurrentPhysicalCoreIndex() const {
@@ -890,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const {
}
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
return *impl->cores[CurrentPhysicalCoreIndex()];
return impl->cores[CurrentPhysicalCoreIndex()];
}
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
return *impl->cores[CurrentPhysicalCoreIndex()];
return impl->cores[CurrentPhysicalCoreIndex()];
}
Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -906,6 +906,15 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
return impl->schedulers[core_id].get();
}
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() {
return impl->interrupts;
}
const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts()
const {
return impl->interrupts;
}
Kernel::TimeManager& KernelCore::TimeManager() {
return impl->time_manager;
}
@@ -930,18 +939,24 @@ const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
return *impl->global_object_list_container;
}
void KernelCore::InterruptAllPhysicalCores() {
for (auto& physical_core : impl->cores) {
physical_core.Interrupt();
}
}
void KernelCore::InvalidateAllInstructionCaches() {
for (auto& physical_core : impl->cores) {
physical_core->ArmInterface().ClearInstructionCache();
physical_core.ArmInterface().ClearInstructionCache();
}
}
void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) {
for (auto& physical_core : impl->cores) {
if (!physical_core->IsInitialized()) {
if (!physical_core.IsInitialized()) {
continue;
}
physical_core->ArmInterface().InvalidateCacheRange(addr, size);
physical_core.ArmInterface().InvalidateCacheRange(addr, size);
}
}

View File

@@ -9,12 +9,14 @@
#include <string>
#include <unordered_map>
#include <vector>
#include "core/arm/cpu_interrupt_handler.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/svc_common.h"
namespace Core {
class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -181,6 +183,12 @@ public:
const KAutoObjectWithListContainer& ObjectListContainer() const;
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts();
const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
void InterruptAllPhysicalCores();
void InvalidateAllInstructionCaches();
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);

View File

@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/core.h"
@@ -10,14 +11,16 @@
namespace Kernel {
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
: core_index{core_index_}, system{system_}, scheduler{scheduler_} {
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
Core::CPUInterrupts& interrupts_)
: core_index{core_index_}, system{system_}, scheduler{scheduler_},
interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
#ifdef ARCHITECTURE_x86_64
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
auto& kernel = system.Kernel();
arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
#else
#error Platform not supported yet.
#endif
@@ -31,7 +34,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
if (!is_64_bit) {
// We already initialized a 64-bit core, replace with a 32-bit one.
arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
}
#else
#error Platform not supported yet.
@@ -44,26 +47,24 @@ void PhysicalCore::Run() {
}
void PhysicalCore::Idle() {
std::unique_lock lk{guard};
on_interrupt.wait(lk, [this] { return is_interrupted; });
interrupts[core_index].AwaitInterrupt();
}
bool PhysicalCore::IsInterrupted() const {
return is_interrupted;
return interrupts[core_index].IsInterrupted();
}
void PhysicalCore::Interrupt() {
std::unique_lock lk{guard};
is_interrupted = true;
guard->lock();
interrupts[core_index].SetInterrupt(true);
arm_interface->SignalInterrupt();
on_interrupt.notify_all();
guard->unlock();
}
void PhysicalCore::ClearInterrupt() {
std::unique_lock lk{guard};
is_interrupted = false;
arm_interface->ClearInterrupt();
on_interrupt.notify_all();
guard->lock();
interrupts[core_index].SetInterrupt(false);
guard->unlock();
}
} // namespace Kernel

View File

@@ -14,6 +14,7 @@ class KScheduler;
} // namespace Kernel
namespace Core {
class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -22,11 +23,15 @@ namespace Kernel {
class PhysicalCore {
public:
PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_);
PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
Core::CPUInterrupts& interrupts_);
~PhysicalCore();
YUZU_NON_COPYABLE(PhysicalCore);
YUZU_NON_MOVEABLE(PhysicalCore);
PhysicalCore(const PhysicalCore&) = delete;
PhysicalCore& operator=(const PhysicalCore&) = delete;
PhysicalCore(PhysicalCore&&) = default;
PhysicalCore& operator=(PhysicalCore&&) = delete;
/// Initialize the core for the specified parameters.
void Initialize(bool is_64_bit);
@@ -81,11 +86,9 @@ private:
const std::size_t core_index;
Core::System& system;
Kernel::KScheduler& scheduler;
std::mutex guard;
std::condition_variable on_interrupt;
Core::CPUInterrupts& interrupts;
std::unique_ptr<std::mutex> guard;
std::unique_ptr<Core::ARM_Interface> arm_interface;
bool is_interrupted;
};
} // namespace Kernel

View File

@@ -163,51 +163,28 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
}
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
const auto& body_colors = controller.device->GetColors();
const auto& battery_level = controller.device->GetBattery();
auto* shared_memory = controller.shared_memory;
if (controller_type == Core::HID::NpadStyleIndex::None) {
controller.styleset_changed_event->GetWritableEvent().Signal();
return;
}
// Reset memory values
shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None;
shared_memory->device_type.raw = 0;
shared_memory->system_properties.raw = 0;
shared_memory->joycon_color.attribute = ColorAttribute::NoController;
shared_memory->joycon_color.attribute = ColorAttribute::NoController;
shared_memory->fullkey_color = {};
shared_memory->joycon_color.left = {};
shared_memory->joycon_color.right = {};
shared_memory->battery_level_dual = {};
shared_memory->battery_level_left = {};
shared_memory->battery_level_right = {};
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
shared_memory->fullkey_color.fullkey = body_colors.fullkey;
shared_memory->battery_level_dual = battery_level.dual.battery_level;
shared_memory->style_tag.fullkey.Assign(1);
shared_memory->device_type.fullkey.Assign(1);
shared_memory->system_properties.is_vertical.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->system_properties.is_charging_joy_dual.Assign(
battery_level.dual.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::Handheld:
shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->fullkey_color.fullkey = body_colors.fullkey;
shared_memory->joycon_color.left = body_colors.left;
shared_memory->joycon_color.right = body_colors.right;
shared_memory->style_tag.handheld.Assign(1);
shared_memory->device_type.handheld_left.Assign(1);
shared_memory->device_type.handheld_right.Assign(1);
@@ -215,86 +192,47 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->system_properties.use_directional_buttons.Assign(1);
shared_memory->system_properties.is_charging_joy_dual.Assign(
battery_level.left.is_charging);
shared_memory->system_properties.is_charging_joy_left.Assign(
battery_level.left.is_charging);
shared_memory->system_properties.is_charging_joy_right.Assign(
battery_level.right.is_charging);
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory->applet_nfc_xcd.applet_footer.type =
AppletFooterUiType::HandheldJoyConLeftJoyConRight;
shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->style_tag.joycon_dual.Assign(1);
if (controller.is_dual_left_connected) {
shared_memory->joycon_color.left = body_colors.left;
shared_memory->battery_level_left = battery_level.left.battery_level;
shared_memory->device_type.joycon_left.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->system_properties.is_charging_joy_left.Assign(
battery_level.left.is_charging);
shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
}
if (controller.is_dual_right_connected) {
shared_memory->joycon_color.right = body_colors.right;
shared_memory->battery_level_right = battery_level.right.battery_level;
shared_memory->device_type.joycon_right.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.is_charging_joy_right.Assign(
battery_level.right.is_charging);
shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
}
shared_memory->system_properties.use_directional_buttons.Assign(1);
shared_memory->system_properties.is_vertical.Assign(1);
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual;
shared_memory->fullkey_color.fullkey = body_colors.left;
shared_memory->battery_level_dual = battery_level.left.battery_level;
shared_memory->system_properties.is_charging_joy_dual.Assign(
battery_level.left.is_charging);
} else if (controller.is_dual_left_connected) {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
shared_memory->fullkey_color.fullkey = body_colors.left;
shared_memory->battery_level_dual = battery_level.left.battery_level;
shared_memory->system_properties.is_charging_joy_dual.Assign(
battery_level.left.is_charging);
} else {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
shared_memory->fullkey_color.fullkey = body_colors.right;
shared_memory->battery_level_dual = battery_level.right.battery_level;
shared_memory->system_properties.is_charging_joy_dual.Assign(
battery_level.right.is_charging);
}
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->joycon_color.left = body_colors.left;
shared_memory->battery_level_dual = battery_level.left.battery_level;
shared_memory->style_tag.joycon_left.Assign(1);
shared_memory->device_type.joycon_left.Assign(1);
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->system_properties.is_charging_joy_left.Assign(
battery_level.left.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->joycon_color.right = body_colors.right;
shared_memory->battery_level_right = battery_level.right.battery_level;
shared_memory->style_tag.joycon_right.Assign(1);
shared_memory->device_type.joycon_right.Assign(1);
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.is_charging_joy_right.Assign(
battery_level.right.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
break;
@@ -331,6 +269,21 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
break;
}
const auto& body_colors = controller.device->GetColors();
shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
shared_memory->fullkey_color.fullkey = body_colors.fullkey;
shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->joycon_color.left = body_colors.left;
shared_memory->joycon_color.right = body_colors.right;
// TODO: Investigate when we should report all batery types
const auto& battery_level = controller.device->GetBattery();
shared_memory->battery_level_dual = battery_level.dual.battery_level;
shared_memory->battery_level_left = battery_level.left.battery_level;
shared_memory->battery_level_right = battery_level.right.battery_level;
controller.is_connected = true;
controller.device->Connect();
SignalStyleSetChangedEvent(npad_id);

View File

@@ -1,12 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::LDN {
constexpr Result ERROR_DISABLED{ErrorModule::LDN, 22};
} // namespace Service::LDN

View File

@@ -3,11 +3,15 @@
#include <memory>
#include "core/hle/ipc_helpers.h"
#include "core/hle/result.h"
#include "core/hle/service/ldn/errors.h"
#include "core/core.h"
#include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/ldn_types.h"
#include "core/internal_network/network.h"
#include "core/internal_network/network_interface.h"
// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
#undef CreateEvent
namespace Service::LDN {
@@ -100,74 +104,418 @@ class IUserLocalCommunicationService final
: public ServiceFramework<IUserLocalCommunicationService> {
public:
explicit IUserLocalCommunicationService(Core::System& system_)
: ServiceFramework{system_, "IUserLocalCommunicationService"} {
: ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew},
service_context{system, "IUserLocalCommunicationService"}, room_network{
system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IUserLocalCommunicationService::GetState, "GetState"},
{1, nullptr, "GetNetworkInfo"},
{1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
{2, nullptr, "GetIpv4Address"},
{3, nullptr, "GetDisconnectReason"},
{4, nullptr, "GetSecurityParameter"},
{5, nullptr, "GetNetworkConfig"},
{100, nullptr, "AttachStateChangeEvent"},
{101, nullptr, "GetNetworkInfoLatestUpdate"},
{102, nullptr, "Scan"},
{103, nullptr, "ScanPrivate"},
{3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
{4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
{5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
{100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"},
{101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
{102, &IUserLocalCommunicationService::Scan, "Scan"},
{103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
{104, nullptr, "SetWirelessControllerRestriction"},
{200, nullptr, "OpenAccessPoint"},
{201, nullptr, "CloseAccessPoint"},
{202, nullptr, "CreateNetwork"},
{203, nullptr, "CreateNetworkPrivate"},
{204, nullptr, "DestroyNetwork"},
{200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
{201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
{202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
{203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"},
{204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"},
{205, nullptr, "Reject"},
{206, nullptr, "SetAdvertiseData"},
{207, nullptr, "SetStationAcceptPolicy"},
{208, nullptr, "AddAcceptFilterEntry"},
{206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"},
{207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"},
{208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"},
{209, nullptr, "ClearAcceptFilter"},
{300, nullptr, "OpenStation"},
{301, nullptr, "CloseStation"},
{302, nullptr, "Connect"},
{300, &IUserLocalCommunicationService::OpenStation, "OpenStation"},
{301, &IUserLocalCommunicationService::CloseStation, "CloseStation"},
{302, &IUserLocalCommunicationService::Connect, "Connect"},
{303, nullptr, "ConnectPrivate"},
{304, nullptr, "Disconnect"},
{400, nullptr, "Initialize"},
{401, nullptr, "Finalize"},
{402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+
{304, &IUserLocalCommunicationService::Disconnect, "Disconnect"},
{400, &IUserLocalCommunicationService::Initialize, "Initialize"},
{401, &IUserLocalCommunicationService::Finalize, "Finalize"},
{402, &IUserLocalCommunicationService::Initialize2, "Initialize2"},
};
// clang-format on
RegisterHandlers(functions);
state_change_event =
service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
}
~IUserLocalCommunicationService() {
service_context.CloseEvent(state_change_event);
}
void OnEventFired() {
state_change_event->GetWritableEvent().Signal();
}
void GetState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
State state = State::Error;
LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
// Indicate a network error, as we do not actually emulate LDN
rb.Push(static_cast<u32>(State::Error));
void GetNetworkInfo(Kernel::HLERequestContext& ctx) {
const auto write_buffer_size = ctx.GetWriteBufferSize();
if (write_buffer_size != sizeof(NetworkInfo)) {
LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultBadInput);
return;
}
NetworkInfo network_info{};
const auto rc = ResultSuccess;
if (rc.IsError()) {
LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(rc);
return;
}
LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
network_info.common.ssid.GetStringValue(), network_info.ldn.node_count);
ctx.WriteBuffer<NetworkInfo>(network_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(rc);
}
void GetDisconnectReason(Kernel::HLERequestContext& ctx) {
const auto disconnect_reason = DisconnectReason::None;
LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(disconnect_reason);
}
void GetSecurityParameter(Kernel::HLERequestContext& ctx) {
SecurityParameter security_parameter{};
NetworkInfo info{};
const Result rc = ResultSuccess;
if (rc.IsError()) {
LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(rc);
return;
}
security_parameter.session_id = info.network_id.session_id;
std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
sizeof(SecurityParameter::data));
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(rc);
rb.PushRaw<SecurityParameter>(security_parameter);
}
void GetNetworkConfig(Kernel::HLERequestContext& ctx) {
NetworkConfig config{};
NetworkInfo info{};
const Result rc = ResultSuccess;
if (rc.IsError()) {
LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(rc);
return;
}
config.intent_id = info.network_id.intent_id;
config.channel = info.common.channel;
config.node_count_max = info.ldn.node_count_max;
config.local_communication_version = info.ldn.nodes[0].local_communication_version;
LOG_WARNING(Service_LDN,
"(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, "
"local_communication_version={}",
config.intent_id.local_communication_id, config.intent_id.scene_id,
config.channel, config.node_count_max, config.local_communication_version);
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(rc);
rb.PushRaw<NetworkConfig>(config);
}
void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_LDN, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(state_change_event->GetReadableEvent());
}
void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size,
node_buffer_count);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultBadInput);
return;
}
NetworkInfo info;
std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
const auto rc = ResultSuccess;
if (rc.IsError()) {
LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(rc);
return;
}
LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
info.common.ssid.GetStringValue(), info.ldn.node_count);
ctx.WriteBuffer(info, 0);
ctx.WriteBuffer(latest_update, 1);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Scan(Kernel::HLERequestContext& ctx) {
ScanImpl(ctx);
}
void ScanPrivate(Kernel::HLERequestContext& ctx) {
ScanImpl(ctx, true);
}
void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) {
IPC::RequestParser rp{ctx};
const auto channel{rp.PopEnum<WifiChannel>()};
const auto scan_filter{rp.PopRaw<ScanFilter>()};
const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo);
if (network_info_size == 0) {
LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultBadInput);
return;
}
u16 count = 0;
std::vector<NetworkInfo> network_infos(network_info_size);
LOG_WARNING(Service_LDN,
"(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}",
channel, scan_filter.flag, scan_filter.network_type);
ctx.WriteBuffer(network_infos);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(count);
}
void OpenAccessPoint(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CloseAccessPoint(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CreateNetwork(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
SecurityConfig security_config;
UserConfig user_config;
INSERT_PADDING_WORDS_NOINIT(1);
NetworkConfig network_config;
};
static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_LDN,
"(STUBBED) called, passphrase_size={}, security_mode={}, "
"local_communication_version={}",
parameters.security_config.passphrase_size,
parameters.security_config.security_mode,
parameters.network_config.local_communication_version);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
SecurityConfig security_config;
SecurityParameter security_parameter;
UserConfig user_config;
NetworkConfig network_config;
};
static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_LDN,
"(STUBBED) called, passphrase_size={}, security_mode={}, "
"local_communication_version={}",
parameters.security_config.passphrase_size,
parameters.security_config.security_mode,
parameters.network_config.local_communication_version);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void DestroyNetwork(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
std::vector<u8> read_buffer = ctx.ReadBuffer();
LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void OpenStation(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CloseStation(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Connect(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
SecurityConfig security_config;
UserConfig user_config;
u32 local_communication_version;
u32 option;
};
static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_LDN,
"(STUBBED) called, passphrase_size={}, security_mode={}, "
"local_communication_version={}",
parameters.security_config.passphrase_size,
parameters.security_config.security_mode,
parameters.local_communication_version);
const std::vector<u8> read_buffer = ctx.ReadBuffer();
NetworkInfo network_info{};
if (read_buffer.size() != sizeof(NetworkInfo)) {
LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultBadInput);
return;
}
std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Disconnect(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
const auto rc = InitializeImpl(ctx);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(rc);
}
void Finalize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
is_initialized = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Initialize2(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_LDN, "called");
LOG_WARNING(Service_LDN, "(STUBBED) called");
is_initialized = true;
const auto rc = InitializeImpl(ctx);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_DISABLED);
rb.Push(rc);
}
private:
enum class State {
None,
Initialized,
AccessPointOpened,
AccessPointCreated,
StationOpened,
StationConnected,
Error,
};
Result InitializeImpl(Kernel::HLERequestContext& ctx) {
const auto network_interface = Network::GetSelectedNetworkInterface();
if (!network_interface) {
LOG_ERROR(Service_LDN, "No network interface is set");
return ResultAirplaneModeEnabled;
}
is_initialized = true;
// TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented
return ResultAirplaneModeEnabled;
}
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* state_change_event;
Network::RoomNetwork& room_network;
bool is_initialized{};
};
@@ -273,7 +621,7 @@ public:
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_DISABLED);
rb.Push(ResultDisabled);
}
};

View File

@@ -3,6 +3,12 @@
#pragma once
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/sm/sm.h"
namespace Core {
class System;
}

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::LDN {
constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10};
constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20};
constexpr Result ResultDisabled{ErrorModule::LDN, 22};
constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23};
constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30};
constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31};
constexpr Result ResultBadState{ErrorModule::LDN, 32};
constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33};
constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50};
constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65};
constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66};
constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67};
constexpr Result ResultBadInput{ErrorModule::LDN, 96};
constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97};
constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113};
constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114};
} // namespace Service::LDN

View File

@@ -0,0 +1,284 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <fmt/format.h>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "network/network.h"
namespace Service::LDN {
constexpr size_t SsidLengthMax = 32;
constexpr size_t AdvertiseDataSizeMax = 384;
constexpr size_t UserNameBytesMax = 32;
constexpr int NodeCountMax = 8;
constexpr int StationCountMax = NodeCountMax - 1;
constexpr size_t PassphraseLengthMax = 64;
enum class SecurityMode : u16 {
All,
Retail,
Debug,
};
enum class NodeStateChange : u8 {
None,
Connect,
Disconnect,
DisconnectAndConnect,
};
enum class ScanFilterFlag : u32 {
None = 0,
LocalCommunicationId = 1 << 0,
SessionId = 1 << 1,
NetworkType = 1 << 2,
Ssid = 1 << 4,
SceneId = 1 << 5,
IntentId = LocalCommunicationId | SceneId,
NetworkId = IntentId | SessionId,
};
enum class NetworkType : u32 {
None,
General,
Ldn,
All,
};
enum class PackedNetworkType : u8 {
None,
General,
Ldn,
All,
};
enum class State : u32 {
None,
Initialized,
AccessPointOpened,
AccessPointCreated,
StationOpened,
StationConnected,
Error,
};
enum class DisconnectReason : s16 {
Unknown = -1,
None,
DisconnectedByUser,
DisconnectedBySystem,
DestroyedByUser,
DestroyedBySystem,
Rejected,
SignalLost,
};
enum class NetworkError {
Unknown = -1,
None = 0,
PortUnreachable,
TooManyPlayers,
VersionTooLow,
VersionTooHigh,
ConnectFailure,
ConnectNotFound,
ConnectTimeout,
ConnectRejected,
RejectFailed,
};
enum class AcceptPolicy : u8 {
AcceptAll,
RejectAll,
BlackList,
WhiteList,
};
enum class WifiChannel : s16 {
Default = 0,
wifi24_1 = 1,
wifi24_6 = 6,
wifi24_11 = 11,
wifi50_36 = 36,
wifi50_40 = 40,
wifi50_44 = 44,
wifi50_48 = 48,
};
enum class LinkLevel : s8 {
Bad,
Low,
Good,
Excelent,
};
struct NodeLatestUpdate {
NodeStateChange state_change;
INSERT_PADDING_BYTES(0x7); // Unknown
};
static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size");
struct SessionId {
u64 high;
u64 low;
bool operator==(const SessionId&) const = default;
};
static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");
struct IntentId {
u64 local_communication_id;
INSERT_PADDING_BYTES(0x2); // Reserved
u16 scene_id;
INSERT_PADDING_BYTES(0x4); // Reserved
};
static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");
struct NetworkId {
IntentId intent_id;
SessionId session_id;
};
static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");
struct Ssid {
u8 length;
std::array<char, SsidLengthMax + 1> raw;
std::string GetStringValue() const {
return std::string(raw.data(), length);
}
};
static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
struct Ipv4Address {
union {
u32 raw{};
std::array<u8, 4> bytes;
};
std::string GetStringValue() const {
return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
}
};
static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
struct MacAddress {
std::array<u8, 6> raw{};
friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
};
static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");
struct ScanFilter {
NetworkId network_id;
NetworkType network_type;
MacAddress mac_address;
Ssid ssid;
INSERT_PADDING_BYTES(0x10);
ScanFilterFlag flag;
};
static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size");
struct CommonNetworkInfo {
MacAddress bssid;
Ssid ssid;
WifiChannel channel;
LinkLevel link_level;
PackedNetworkType network_type;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");
struct NodeInfo {
Ipv4Address ipv4_address;
MacAddress mac_address;
s8 node_id;
u8 is_connected;
std::array<u8, UserNameBytesMax + 1> user_name;
INSERT_PADDING_BYTES(0x1); // Reserved
s16 local_communication_version;
INSERT_PADDING_BYTES(0x10); // Reserved
};
static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");
struct LdnNetworkInfo {
std::array<u8, 0x10> security_parameter;
SecurityMode security_mode;
AcceptPolicy station_accept_policy;
u8 has_action_frame;
INSERT_PADDING_BYTES(0x2); // Padding
u8 node_count_max;
u8 node_count;
std::array<NodeInfo, NodeCountMax> nodes;
INSERT_PADDING_BYTES(0x2); // Reserved
u16 advertise_data_size;
std::array<u8, AdvertiseDataSizeMax> advertise_data;
INSERT_PADDING_BYTES(0x8C); // Reserved
u64 random_authentication_id;
};
static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");
struct NetworkInfo {
NetworkId network_id;
CommonNetworkInfo common;
LdnNetworkInfo ldn;
};
static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
struct SecurityConfig {
SecurityMode security_mode;
u16 passphrase_size;
std::array<u8, PassphraseLengthMax> passphrase;
};
static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size");
struct UserConfig {
std::array<u8, UserNameBytesMax + 1> user_name;
INSERT_PADDING_BYTES(0xF); // Reserved
};
static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size");
#pragma pack(push, 4)
struct ConnectRequest {
SecurityConfig security_config;
UserConfig user_config;
u32 local_communication_version;
u32 option_unknown;
NetworkInfo network_info;
};
static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size");
#pragma pack(pop)
struct SecurityParameter {
std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig
SessionId session_id;
};
static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size");
struct NetworkConfig {
IntentId intent_id;
WifiChannel channel;
u8 node_count_max;
INSERT_PADDING_BYTES(0x1); // Reserved
u16 local_communication_version;
INSERT_PADDING_BYTES(0xA); // Reserved
};
static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size");
struct AddressEntry {
Ipv4Address ipv4_address;
MacAddress mac_address;
INSERT_PADDING_BYTES(0x2); // Reserved
};
static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size");
struct AddressList {
std::array<AddressEntry, 0x8> addresses;
};
static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");
} // namespace Service::LDN

View File

@@ -33,10 +33,9 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
}
VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
// Mailbox (triple buffering) doesn't lock the application like fifo (vsync),
// prefer it if vsync option is not selected
// Mailbox doesn't lock the application like fifo (vsync), prefer it
const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
if (found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) {
if (found_mailbox != modes.end()) {
return VK_PRESENT_MODE_MAILBOX_KHR;
}
if (!Settings::values.use_speed_limit.GetValue()) {

View File

@@ -221,9 +221,6 @@ if (ENABLE_QT_TRANSLATION)
# Update source TS file if enabled
if (GENERATE_QT_TRANSLATION)
get_target_property(SRCS yuzu SOURCES)
# these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals
# so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm
set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
qt_create_translation(QM_FILES
${SRCS}
${UIS}
@@ -232,13 +229,7 @@ if (ENABLE_QT_TRANSLATION)
-source-language en_US
-target-language en_US
)
# Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts
set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts)
set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals")
qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US)
add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE})
add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts)
endif()
# Find all TS files except en.ts
@@ -248,9 +239,6 @@ if (ENABLE_QT_TRANSLATION)
# Compile TS files to QM files
qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
# Compile english plurals TS file to en.qm
qt_add_translation(LANGUAGES_QM ${PROJECT_SOURCE_DIR}/dist/english_plurals/en.ts)
# Build a QRC file from the QM file list
set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n")

View File

@@ -1089,8 +1089,8 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
}
if (!unsupported_ext.empty()) {
const std::string gl_renderer{reinterpret_cast<const char*>(glGetString(GL_RENDERER))};
LOG_ERROR(Frontend, "GPU does not support all required extensions: {}", gl_renderer);
LOG_ERROR(Frontend, "GPU does not support all required extensions: {}",
glGetString(GL_RENDERER));
}
for (const QString& ext : unsupported_ext) {
LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString());

View File

@@ -75,7 +75,7 @@
<string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
</property>
<property name="text">
<string>Use VSync</string>
<string>Use VSync (OpenGL only)</string>
</property>
</widget>
</item>

View File

@@ -219,7 +219,6 @@ void ConfigureUi::InitializeLanguageComboBox() {
for (const auto& lang : languages) {
if (QString::fromLatin1(lang.id) == QStringLiteral("en")) {
ui->language_combobox->addItem(lang.name, QStringLiteral("en"));
language_files.removeOne(QStringLiteral("en.qm"));
continue;
}
for (int i = 0; i < language_files.size(); ++i) {

View File

@@ -3338,8 +3338,7 @@ void GMainWindow::MigrateConfigFiles() {
}
const auto origin = config_dir_fs_path / filename;
const auto destination = config_dir_fs_path / "custom" / filename;
LOG_INFO(Frontend, "Migrating config file from {} to {}", origin.string(),
destination.string());
LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination);
if (!Common::FS::RenameFile(origin, destination)) {
// Delete the old config file if one already exists in the new location.
Common::FS::RemoveFile(origin);
@@ -3980,6 +3979,11 @@ void GMainWindow::UpdateUITheme() {
}
void GMainWindow::LoadTranslation() {
// If the selected language is English, no need to install any translation
if (UISettings::values.language == QStringLiteral("en")) {
return;
}
bool loaded;
if (UISettings::values.language.isEmpty()) {
@@ -4067,15 +4071,6 @@ int main(int argc, char* argv[]) {
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
QApplication app(argc, argv);
// Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021
// so we can see if we get \u3008 instead
// TL;DR all other number formats are consecutive in unicode code points
// This bug is fixed in Qt6, specifically 6.0.0-alpha1
const QLocale locale = QLocale::system();
if (QStringLiteral("\u3008") == locale.toString(1)) {
QLocale::setDefault(QLocale::system().name());
}
// Qt changes the locale and causes issues in float conversion using std::to_string() when
// generating shaders
setlocale(LC_ALL, "C");

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
"name": "yuzu",
"builtin-baseline": "9b22b40c6c61bf0da2d46346dd44a11e90972cc9",
"builtin-baseline": "cef0b3ec767df6e83806899fe9525f6cf8d7bc91",
"version": "1.0",
"dependencies": [
"boost-algorithm",
@@ -37,10 +37,6 @@
{
"name": "catch2",
"version": "2.13.9"
},
{
"name": "fmt",
"version": "9.0.0"
}
]
}