Compare commits
41 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49f6deecb8 | ||
|
|
f09d192aac | ||
|
|
9971cd1d55 | ||
|
|
c4f5615c6b | ||
|
|
09da9da6fb | ||
|
|
6892a0942f | ||
|
|
f38ae8e953 | ||
|
|
cfb9672093 | ||
|
|
462c430c8b | ||
|
|
5a2dff87bf | ||
|
|
7a8a7545f2 | ||
|
|
abe2ad7aac | ||
|
|
877e8991c7 | ||
|
|
032e5b983c | ||
|
|
ac3927074b | ||
|
|
c41a4baf06 | ||
|
|
6adaa0d5e2 | ||
|
|
197d756560 | ||
|
|
8c56481249 | ||
|
|
6ff4bf9b1c | ||
|
|
dba86ee007 | ||
|
|
407dc917f1 | ||
|
|
15d573194c | ||
|
|
f28ca5361f | ||
|
|
306840a580 | ||
|
|
3d4c113037 | ||
|
|
230d118252 | ||
|
|
b9b1318bea | ||
|
|
43d909949e | ||
|
|
00d401d639 | ||
|
|
0e7e98e24e | ||
|
|
0eb3fa05e5 | ||
|
|
889454f9bf | ||
|
|
8bcaa8c2e4 | ||
|
|
c95baf92ce | ||
|
|
a7651168dd | ||
|
|
075a3d1172 | ||
|
|
7187732454 | ||
|
|
5031f5b8b0 | ||
|
|
026fe2e4f4 | ||
|
|
44518b225c |
@@ -210,7 +210,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
# =======================================================================
|
||||
|
||||
# Enforce the search mode of non-required packages for better and shorter failure messages
|
||||
find_package(Boost 1.73.0 REQUIRED context)
|
||||
find_package(Boost 1.79.0 REQUIRED context)
|
||||
find_package(enet 1.3 MODULE)
|
||||
find_package(fmt 9 REQUIRED)
|
||||
find_package(inih 52 MODULE COMPONENTS INIReader)
|
||||
|
||||
2
externals/vcpkg
vendored
2
externals/vcpkg
vendored
Submodule externals/vcpkg updated: 9b22b40c6c...a7b6122f6b
@@ -38,6 +38,7 @@ add_library(common STATIC
|
||||
common_precompiled_headers.h
|
||||
common_types.h
|
||||
concepts.h
|
||||
container_hash.h
|
||||
demangle.cpp
|
||||
demangle.h
|
||||
div_ceil.h
|
||||
|
||||
@@ -1,158 +1,249 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <stop_token>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "common/polyfill_thread.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if defined(__cpp_lib_hardware_interference_size)
|
||||
constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
|
||||
#else
|
||||
constexpr size_t hardware_interference_size = 64;
|
||||
#endif
|
||||
namespace detail {
|
||||
constexpr size_t DefaultCapacity = 0x1000;
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, size_t Capacity = detail::DefaultCapacity>
|
||||
class SPSCQueue {
|
||||
static_assert((Capacity & (Capacity - 1)) == 0, "Capacity must be a power of two.");
|
||||
|
||||
template <typename T, size_t capacity = 0x400>
|
||||
class MPSCQueue {
|
||||
public:
|
||||
explicit MPSCQueue() : allocator{std::allocator<Slot<T>>()} {
|
||||
// Allocate one extra slot to prevent false sharing on the last slot
|
||||
slots = allocator.allocate(capacity + 1);
|
||||
// Allocators are not required to honor alignment for over-aligned types
|
||||
// (see http://eel.is/c++draft/allocator.requirements#10) so we verify
|
||||
// alignment here
|
||||
if (reinterpret_cast<uintptr_t>(slots) % alignof(Slot<T>) != 0) {
|
||||
allocator.deallocate(slots, capacity + 1);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
std::construct_at(&slots[i]);
|
||||
}
|
||||
static_assert(std::has_single_bit(capacity), "capacity must be an integer power of 2");
|
||||
static_assert(alignof(Slot<T>) == hardware_interference_size,
|
||||
"Slot must be aligned to cache line boundary to prevent false sharing");
|
||||
static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
|
||||
"Slot size must be a multiple of cache line size to prevent "
|
||||
"false sharing between adjacent slots");
|
||||
static_assert(sizeof(MPSCQueue) % hardware_interference_size == 0,
|
||||
"Queue size must be a multiple of cache line size to "
|
||||
"prevent false sharing between adjacent queues");
|
||||
template <typename... Args>
|
||||
bool TryEmplace(Args&&... args) {
|
||||
return Emplace<PushMode::Try>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
~MPSCQueue() noexcept {
|
||||
for (size_t i = 0; i < capacity; ++i) {
|
||||
std::destroy_at(&slots[i]);
|
||||
}
|
||||
allocator.deallocate(slots, capacity + 1);
|
||||
template <typename... Args>
|
||||
void EmplaceWait(Args&&... args) {
|
||||
Emplace<PushMode::Wait>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// The queue must be both non-copyable and non-movable
|
||||
MPSCQueue(const MPSCQueue&) = delete;
|
||||
MPSCQueue& operator=(const MPSCQueue&) = delete;
|
||||
|
||||
MPSCQueue(MPSCQueue&&) = delete;
|
||||
MPSCQueue& operator=(MPSCQueue&&) = delete;
|
||||
|
||||
void Push(const T& v) noexcept {
|
||||
static_assert(std::is_nothrow_copy_constructible_v<T>,
|
||||
"T must be nothrow copy constructible");
|
||||
emplace(v);
|
||||
bool TryPop(T& t) {
|
||||
return Pop<PopMode::Try>(t);
|
||||
}
|
||||
|
||||
template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
|
||||
void Push(P&& v) noexcept {
|
||||
emplace(std::forward<P>(v));
|
||||
void PopWait(T& t) {
|
||||
Pop<PopMode::Wait>(t);
|
||||
}
|
||||
|
||||
void Pop(T& v, std::stop_token stop) noexcept {
|
||||
auto const tail = tail_.fetch_add(1);
|
||||
auto& slot = slots[idx(tail)];
|
||||
if (!slot.turn.test()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
|
||||
}
|
||||
v = slot.move();
|
||||
slot.destroy();
|
||||
slot.turn.clear();
|
||||
slot.turn.notify_one();
|
||||
void PopWait(T& t, std::stop_token stop_token) {
|
||||
Pop<PopMode::WaitWithStopToken>(t, stop_token);
|
||||
}
|
||||
|
||||
T PopWait() {
|
||||
T t;
|
||||
Pop<PopMode::Wait>(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
T PopWait(std::stop_token stop_token) {
|
||||
T t;
|
||||
Pop<PopMode::WaitWithStopToken>(t, stop_token);
|
||||
return t;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename U = T>
|
||||
struct Slot {
|
||||
~Slot() noexcept {
|
||||
if (turn.test()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible_v<U, Args&&...>,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
std::construct_at(reinterpret_cast<U*>(&storage), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void destroy() noexcept {
|
||||
static_assert(std::is_nothrow_destructible_v<U>, "T must be nothrow destructible");
|
||||
std::destroy_at(reinterpret_cast<U*>(&storage));
|
||||
}
|
||||
|
||||
U&& move() noexcept {
|
||||
return reinterpret_cast<U&&>(storage);
|
||||
}
|
||||
|
||||
// Align to avoid false sharing between adjacent slots
|
||||
alignas(hardware_interference_size) std::atomic_flag turn{};
|
||||
struct aligned_store {
|
||||
struct type {
|
||||
alignas(U) unsigned char data[sizeof(U)];
|
||||
};
|
||||
};
|
||||
typename aligned_store::type storage;
|
||||
enum class PushMode {
|
||||
Try,
|
||||
Wait,
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class PopMode {
|
||||
Try,
|
||||
Wait,
|
||||
WaitWithStopToken,
|
||||
Count,
|
||||
};
|
||||
|
||||
template <PushMode Mode, typename... Args>
|
||||
bool Emplace(Args&&... args) {
|
||||
const size_t write_index = m_write_index.load(std::memory_order::relaxed);
|
||||
|
||||
if constexpr (Mode == PushMode::Try) {
|
||||
// Check if we have free slots to write to.
|
||||
if ((write_index - m_read_index.load(std::memory_order::acquire)) == Capacity) {
|
||||
return false;
|
||||
}
|
||||
} else if constexpr (Mode == PushMode::Wait) {
|
||||
// Wait until we have free slots to write to.
|
||||
std::unique_lock lock{producer_cv_mutex};
|
||||
producer_cv.wait(lock, [this, write_index] {
|
||||
return (write_index - m_read_index.load(std::memory_order::acquire)) < Capacity;
|
||||
});
|
||||
} else {
|
||||
static_assert(Mode < PushMode::Count, "Invalid PushMode.");
|
||||
}
|
||||
|
||||
// Determine the position to write to.
|
||||
const size_t pos = write_index % Capacity;
|
||||
|
||||
// Emplace into the queue.
|
||||
std::construct_at(std::addressof(m_data[pos]), std::forward<Args>(args)...);
|
||||
|
||||
// Increment the write index.
|
||||
++m_write_index;
|
||||
|
||||
// Notify the consumer that we have pushed into the queue.
|
||||
std::scoped_lock lock{consumer_cv_mutex};
|
||||
consumer_cv.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <PopMode Mode>
|
||||
bool Pop(T& t, [[maybe_unused]] std::stop_token stop_token = {}) {
|
||||
const size_t read_index = m_read_index.load(std::memory_order::relaxed);
|
||||
|
||||
if constexpr (Mode == PopMode::Try) {
|
||||
// Check if the queue is empty.
|
||||
if (read_index == m_write_index.load(std::memory_order::acquire)) {
|
||||
return false;
|
||||
}
|
||||
} else if constexpr (Mode == PopMode::Wait) {
|
||||
// Wait until the queue is not empty.
|
||||
std::unique_lock lock{consumer_cv_mutex};
|
||||
consumer_cv.wait(lock, [this, read_index] {
|
||||
return read_index != m_write_index.load(std::memory_order::acquire);
|
||||
});
|
||||
} else if constexpr (Mode == PopMode::WaitWithStopToken) {
|
||||
// Wait until the queue is not empty.
|
||||
std::unique_lock lock{consumer_cv_mutex};
|
||||
Common::CondvarWait(consumer_cv, lock, stop_token, [this, read_index] {
|
||||
return read_index != m_write_index.load(std::memory_order::acquire);
|
||||
});
|
||||
if (stop_token.stop_requested()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
static_assert(Mode < PopMode::Count, "Invalid PopMode.");
|
||||
}
|
||||
|
||||
// Determine the position to read from.
|
||||
const size_t pos = read_index % Capacity;
|
||||
|
||||
// Pop the data off the queue, moving it.
|
||||
t = std::move(m_data[pos]);
|
||||
|
||||
// Increment the read index.
|
||||
++m_read_index;
|
||||
|
||||
// Notify the producer that we have popped off the queue.
|
||||
std::scoped_lock lock{producer_cv_mutex};
|
||||
producer_cv.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
alignas(128) std::atomic_size_t m_read_index{0};
|
||||
alignas(128) std::atomic_size_t m_write_index{0};
|
||||
|
||||
std::array<T, Capacity> m_data;
|
||||
|
||||
std::condition_variable_any producer_cv;
|
||||
std::mutex producer_cv_mutex;
|
||||
std::condition_variable_any consumer_cv;
|
||||
std::mutex consumer_cv_mutex;
|
||||
};
|
||||
|
||||
template <typename T, size_t Capacity = detail::DefaultCapacity>
|
||||
class MPSCQueue {
|
||||
public:
|
||||
template <typename... Args>
|
||||
void emplace(Args&&... args) noexcept {
|
||||
static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
|
||||
"T must be nothrow constructible with Args&&...");
|
||||
auto const head = head_.fetch_add(1);
|
||||
auto& slot = slots[idx(head)];
|
||||
slot.turn.wait(true);
|
||||
slot.construct(std::forward<Args>(args)...);
|
||||
slot.turn.test_and_set();
|
||||
cv.notify_one();
|
||||
bool TryEmplace(Args&&... args) {
|
||||
std::scoped_lock lock{write_mutex};
|
||||
return spsc_queue.TryEmplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
constexpr size_t idx(size_t i) const noexcept {
|
||||
return i & mask;
|
||||
template <typename... Args>
|
||||
void EmplaceWait(Args&&... args) {
|
||||
std::scoped_lock lock{write_mutex};
|
||||
spsc_queue.EmplaceWait(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static constexpr size_t mask = capacity - 1;
|
||||
bool TryPop(T& t) {
|
||||
return spsc_queue.TryPop(t);
|
||||
}
|
||||
|
||||
// Align to avoid false sharing between head_ and tail_
|
||||
alignas(hardware_interference_size) std::atomic<size_t> head_{0};
|
||||
alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
|
||||
void PopWait(T& t) {
|
||||
spsc_queue.PopWait(t);
|
||||
}
|
||||
|
||||
std::mutex cv_mutex;
|
||||
std::condition_variable_any cv;
|
||||
void PopWait(T& t, std::stop_token stop_token) {
|
||||
spsc_queue.PopWait(t, stop_token);
|
||||
}
|
||||
|
||||
Slot<T>* slots;
|
||||
[[no_unique_address]] std::allocator<Slot<T>> allocator;
|
||||
T PopWait() {
|
||||
return spsc_queue.PopWait();
|
||||
}
|
||||
|
||||
static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
|
||||
"T must be nothrow copy or move assignable");
|
||||
T PopWait(std::stop_token stop_token) {
|
||||
return spsc_queue.PopWait(stop_token);
|
||||
}
|
||||
|
||||
static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
|
||||
private:
|
||||
SPSCQueue<T, Capacity> spsc_queue;
|
||||
std::mutex write_mutex;
|
||||
};
|
||||
|
||||
template <typename T, size_t Capacity = detail::DefaultCapacity>
|
||||
class MPMCQueue {
|
||||
public:
|
||||
template <typename... Args>
|
||||
bool TryEmplace(Args&&... args) {
|
||||
std::scoped_lock lock{write_mutex};
|
||||
return spsc_queue.TryEmplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void EmplaceWait(Args&&... args) {
|
||||
std::scoped_lock lock{write_mutex};
|
||||
spsc_queue.EmplaceWait(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
bool TryPop(T& t) {
|
||||
std::scoped_lock lock{read_mutex};
|
||||
return spsc_queue.TryPop(t);
|
||||
}
|
||||
|
||||
void PopWait(T& t) {
|
||||
std::scoped_lock lock{read_mutex};
|
||||
spsc_queue.PopWait(t);
|
||||
}
|
||||
|
||||
void PopWait(T& t, std::stop_token stop_token) {
|
||||
std::scoped_lock lock{read_mutex};
|
||||
spsc_queue.PopWait(t, stop_token);
|
||||
}
|
||||
|
||||
T PopWait() {
|
||||
std::scoped_lock lock{read_mutex};
|
||||
return spsc_queue.PopWait();
|
||||
}
|
||||
|
||||
T PopWait(std::stop_token stop_token) {
|
||||
std::scoped_lock lock{read_mutex};
|
||||
return spsc_queue.PopWait(stop_token);
|
||||
}
|
||||
|
||||
private:
|
||||
SPSCQueue<T, Capacity> spsc_queue;
|
||||
std::mutex write_mutex;
|
||||
std::mutex read_mutex;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
91
src/common/container_hash.h
Normal file
91
src/common/container_hash.h
Normal file
@@ -0,0 +1,91 @@
|
||||
// SPDX-FileCopyrightText: 2005-2014 Daniel James
|
||||
// SPDX-FileCopyrightText: 2016 Austin Appleby
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
inline std::size_t HashValue(T val) {
|
||||
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
||||
const unsigned int length =
|
||||
(std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
||||
|
||||
std::size_t seed = 0;
|
||||
|
||||
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) {
|
||||
seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
template <size_t Bits>
|
||||
struct HashCombineImpl {
|
||||
template <typename T>
|
||||
static inline T fn(T seed, T value) {
|
||||
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashCombineImpl<64> {
|
||||
static inline std::uint64_t fn(std::uint64_t h, std::uint64_t k) {
|
||||
const std::uint64_t m = (std::uint64_t(0xc6a4a793) << 32) + 0x5bd1e995;
|
||||
const int r = 47;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
|
||||
// Completely arbitrary number, to prevent 0's
|
||||
// from hashing to 0.
|
||||
h += 0xe6546b64;
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
inline void HashCombine(std::size_t& seed, const T& v) {
|
||||
seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, detail::HashValue(v));
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
inline std::size_t HashRange(It first, It last) {
|
||||
std::size_t seed = 0;
|
||||
|
||||
for (; first != last; ++first) {
|
||||
HashCombine<typename std::iterator_traits<It>::value_type>(seed, *first);
|
||||
}
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
template <typename T, size_t Size>
|
||||
std::size_t HashValue(const std::array<T, Size>& v) {
|
||||
return HashRange(v.cbegin(), v.cend());
|
||||
}
|
||||
|
||||
template <typename T, typename Allocator>
|
||||
std::size_t HashValue(const std::vector<T, Allocator>& v) {
|
||||
return HashRange(v.cbegin(), v.cend());
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -28,7 +28,7 @@
|
||||
#ifdef _WIN32
|
||||
#include "common/string_util.h"
|
||||
#endif
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/bounded_threadsafe_queue.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
@@ -204,11 +204,11 @@ public:
|
||||
|
||||
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||
const char* function, std::string&& message) {
|
||||
if (!filter.CheckMessage(log_class, log_level))
|
||||
if (!filter.CheckMessage(log_class, log_level)) {
|
||||
return;
|
||||
const Entry& entry =
|
||||
CreateEntry(log_class, log_level, filename, line_num, function, std::move(message));
|
||||
message_queue.Push(entry);
|
||||
}
|
||||
message_queue.EmplaceWait(
|
||||
CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -225,7 +225,7 @@ private:
|
||||
ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
|
||||
};
|
||||
while (!stop_token.stop_requested()) {
|
||||
entry = message_queue.PopWait(stop_token);
|
||||
message_queue.PopWait(entry, stop_token);
|
||||
if (entry.filename != nullptr) {
|
||||
write_logs();
|
||||
}
|
||||
@@ -233,7 +233,7 @@ private:
|
||||
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
|
||||
// case where a system is repeatedly spamming logs even on close.
|
||||
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
|
||||
while (max_logs_to_write-- && message_queue.Pop(entry)) {
|
||||
while (max_logs_to_write-- && message_queue.TryPop(entry)) {
|
||||
write_logs();
|
||||
}
|
||||
});
|
||||
@@ -273,7 +273,7 @@ private:
|
||||
ColorConsoleBackend color_console_backend{};
|
||||
FileBackend file_backend;
|
||||
|
||||
MPSCQueue<Entry, true> message_queue{};
|
||||
MPSCQueue<Entry> message_queue{};
|
||||
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
||||
std::jthread backend_thread;
|
||||
};
|
||||
|
||||
@@ -125,18 +125,18 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string UTF16ToUTF8(const std::u16string& input) {
|
||||
std::string UTF16ToUTF8(std::u16string_view input) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.to_bytes(input);
|
||||
return convert.to_bytes(input.data(), input.data() + input.size());
|
||||
}
|
||||
|
||||
std::u16string UTF8ToUTF16(const std::string& input) {
|
||||
std::u16string UTF8ToUTF16(std::string_view input) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.from_bytes(input);
|
||||
return convert.from_bytes(input.data(), input.data() + input.size());
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::wstring CPToUTF16(u32 code_page, const std::string& input) {
|
||||
static std::wstring CPToUTF16(u32 code_page, std::string_view input) {
|
||||
const auto size =
|
||||
MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
|
||||
|
||||
@@ -154,7 +154,7 @@ static std::wstring CPToUTF16(u32 code_page, const std::string& input) {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string UTF16ToUTF8(const std::wstring& input) {
|
||||
std::string UTF16ToUTF8(std::wstring_view input) {
|
||||
const auto size = WideCharToMultiByte(CP_UTF8, 0, input.data(), static_cast<int>(input.size()),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
if (size == 0) {
|
||||
@@ -172,7 +172,7 @@ std::string UTF16ToUTF8(const std::wstring& input) {
|
||||
return output;
|
||||
}
|
||||
|
||||
std::wstring UTF8ToUTF16W(const std::string& input) {
|
||||
std::wstring UTF8ToUTF16W(std::string_view input) {
|
||||
return CPToUTF16(CP_UTF8, input);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,12 +36,12 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
|
||||
const std::string& dest);
|
||||
|
||||
[[nodiscard]] std::string UTF16ToUTF8(const std::u16string& input);
|
||||
[[nodiscard]] std::u16string UTF8ToUTF16(const std::string& input);
|
||||
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
||||
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
||||
|
||||
#ifdef _WIN32
|
||||
[[nodiscard]] std::string UTF16ToUTF8(const std::wstring& input);
|
||||
[[nodiscard]] std::wstring UTF8ToUTF16W(const std::string& str);
|
||||
[[nodiscard]] std::string UTF16ToUTF8(std::wstring_view input);
|
||||
[[nodiscard]] std::wstring UTF8ToUTF16W(std::string_view str);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_siz
|
||||
|
||||
std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed) {
|
||||
const std::size_t decompressed_size =
|
||||
ZSTD_getDecompressedSize(compressed.data(), compressed.size());
|
||||
ZSTD_getFrameContentSize(compressed.data(), compressed.size());
|
||||
std::vector<u8> decompressed(decompressed_size);
|
||||
|
||||
const std::size_t uncompressed_result_size = ZSTD_decompress(
|
||||
|
||||
@@ -17,7 +17,7 @@ Result SendSyncRequest(Core::System& system, Handle handle) {
|
||||
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle);
|
||||
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
|
||||
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle);
|
||||
|
||||
R_RETURN(session->SendSyncRequest());
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const {
|
||||
return mii;
|
||||
}
|
||||
|
||||
Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
|
||||
Ver3StoreData MiiManager::BuildFromStoreData(const CharInfo& mii) const {
|
||||
Service::Mii::MiiManager manager;
|
||||
Ver3StoreData mii_v3{};
|
||||
|
||||
@@ -534,16 +534,13 @@ Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
|
||||
mii_v3.region_information.character_set.Assign(mii.font_region);
|
||||
|
||||
mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type);
|
||||
mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color);
|
||||
mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle);
|
||||
mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make);
|
||||
|
||||
mii_v3.hair_style = mii.hair_type;
|
||||
mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color);
|
||||
mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip);
|
||||
|
||||
mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type);
|
||||
mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color);
|
||||
mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale);
|
||||
mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect);
|
||||
mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate);
|
||||
@@ -551,7 +548,6 @@ Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
|
||||
mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y);
|
||||
|
||||
mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type);
|
||||
mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color);
|
||||
mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale);
|
||||
mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect);
|
||||
mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate);
|
||||
@@ -563,7 +559,6 @@ Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
|
||||
mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y);
|
||||
|
||||
mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type);
|
||||
mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color);
|
||||
mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale);
|
||||
mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect);
|
||||
mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y);
|
||||
@@ -573,10 +568,7 @@ Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
|
||||
mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y);
|
||||
|
||||
mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type);
|
||||
mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color);
|
||||
|
||||
mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type);
|
||||
mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color);
|
||||
mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale);
|
||||
mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y);
|
||||
|
||||
@@ -585,11 +577,36 @@ Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
|
||||
mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x);
|
||||
mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y);
|
||||
|
||||
// These types are converted to V3 from a table
|
||||
mii_v3.appearance_bits1.skin_color.Assign(Ver3FacelineColorTable[mii.faceline_color]);
|
||||
mii_v3.appearance_bits3.hair_color.Assign(Ver3HairColorTable[mii.hair_color]);
|
||||
mii_v3.appearance_bits4.eye_color.Assign(Ver3EyeColorTable[mii.eye_color]);
|
||||
mii_v3.appearance_bits5.eyebrow_color.Assign(Ver3HairColorTable[mii.eyebrow_color]);
|
||||
mii_v3.appearance_bits7.mouth_color.Assign(Ver3MouthlineColorTable[mii.mouth_color]);
|
||||
mii_v3.appearance_bits9.facial_hair_color.Assign(Ver3HairColorTable[mii.beard_color]);
|
||||
mii_v3.appearance_bits10.glasses_color.Assign(Ver3GlassColorTable[mii.glasses_color]);
|
||||
mii_v3.appearance_bits10.glasses_type.Assign(Ver3GlassTypeTable[mii.glasses_type]);
|
||||
|
||||
mii_v3.crc = GenerateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16));
|
||||
|
||||
// TODO: Validate mii_v3 data
|
||||
|
||||
return mii_v3;
|
||||
}
|
||||
|
||||
NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const {
|
||||
return {
|
||||
.faceline_color = static_cast<u8>(mii.faceline_color & 0xf),
|
||||
.hair_color = static_cast<u8>(mii.hair_color & 0x7f),
|
||||
.eye_color = static_cast<u8>(mii.eyebrow_color & 0x7f),
|
||||
.eyebrow_color = static_cast<u8>(mii.eyebrow_color & 0x7f),
|
||||
.mouth_color = static_cast<u8>(mii.mouth_color & 0x7f),
|
||||
.beard_color = static_cast<u8>(mii.beard_color & 0x7f),
|
||||
.glass_color = static_cast<u8>(mii.glasses_color & 0x7f),
|
||||
.glass_type = static_cast<u8>(mii.glasses_type & 0x1f),
|
||||
};
|
||||
}
|
||||
|
||||
bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
|
||||
bool is_valid = mii_v3.version == 0 || mii_v3.version == 3;
|
||||
|
||||
|
||||
@@ -23,11 +23,16 @@ public:
|
||||
CharInfo BuildRandom(Age age, Gender gender, Race race);
|
||||
CharInfo BuildDefault(std::size_t index);
|
||||
CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
|
||||
Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;
|
||||
bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
|
||||
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
|
||||
Result GetIndex(const CharInfo& info, u32& index);
|
||||
|
||||
// This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData
|
||||
Ver3StoreData BuildFromStoreData(const CharInfo& mii) const;
|
||||
|
||||
// This is nn::mii::detail::NfpStoreDataExtentionRaw::SetFromStoreData
|
||||
NfpStoreDataExtension SetFromStoreData(const CharInfo& mii) const;
|
||||
|
||||
private:
|
||||
const Common::UUID user_id{};
|
||||
u64 update_counter{};
|
||||
|
||||
@@ -365,10 +365,68 @@ struct Ver3StoreData {
|
||||
} appearance_bits11;
|
||||
|
||||
std::array<u16_le, 0xA> author_name;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
INSERT_PADDING_BYTES(0x2);
|
||||
u16_be crc;
|
||||
};
|
||||
static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
|
||||
|
||||
struct NfpStoreDataExtension {
|
||||
u8 faceline_color;
|
||||
u8 hair_color;
|
||||
u8 eye_color;
|
||||
u8 eyebrow_color;
|
||||
u8 mouth_color;
|
||||
u8 beard_color;
|
||||
u8 glass_color;
|
||||
u8 glass_type;
|
||||
};
|
||||
static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size");
|
||||
|
||||
constexpr std::array<u8, 0x10> Ver3FacelineColorTable{
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5,
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 100> Ver3HairColorTable{
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0,
|
||||
0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
|
||||
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4,
|
||||
0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5,
|
||||
0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7,
|
||||
0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4,
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 100> Ver3EyeColorTable{
|
||||
0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4,
|
||||
0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
|
||||
0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4,
|
||||
0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
|
||||
0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2,
|
||||
0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 100> Ver3MouthlineColorTable{
|
||||
0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
|
||||
0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
|
||||
0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
|
||||
0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
|
||||
0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3,
|
||||
0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3,
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 100> Ver3GlassColorTable{
|
||||
0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3,
|
||||
0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
|
||||
0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
|
||||
0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5,
|
||||
0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4,
|
||||
0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
|
||||
};
|
||||
|
||||
constexpr std::array<u8, 20> Ver3GlassTypeTable{
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1,
|
||||
0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7,
|
||||
};
|
||||
|
||||
struct MiiStoreData {
|
||||
using Name = std::array<char16_t, 10>;
|
||||
|
||||
|
||||
@@ -42,8 +42,18 @@ NfcDevice::~NfcDevice() {
|
||||
};
|
||||
|
||||
void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Connected ||
|
||||
type == Core::HID::ControllerTriggerType::Disconnected) {
|
||||
if (!is_initalized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == Core::HID::ControllerTriggerType::Connected) {
|
||||
Initialize();
|
||||
availability_change_event->Signal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == Core::HID::ControllerTriggerType::Disconnected) {
|
||||
device_state = NFP::DeviceState::Unavailable;
|
||||
availability_change_event->Signal();
|
||||
return;
|
||||
}
|
||||
@@ -113,6 +123,7 @@ void NfcDevice::Initialize() {
|
||||
device_state =
|
||||
npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable;
|
||||
encrypted_tag_data = {};
|
||||
is_initalized = true;
|
||||
}
|
||||
|
||||
void NfcDevice::Finalize() {
|
||||
@@ -121,6 +132,7 @@ void NfcDevice::Finalize() {
|
||||
StopDetection();
|
||||
}
|
||||
device_state = NFP::DeviceState::Unavailable;
|
||||
is_initalized = false;
|
||||
}
|
||||
|
||||
Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
|
||||
|
||||
@@ -67,6 +67,7 @@ private:
|
||||
Kernel::KEvent* deactivate_event = nullptr;
|
||||
Kernel::KEvent* availability_change_event = nullptr;
|
||||
|
||||
bool is_initalized{};
|
||||
NFP::TagProtocol allowed_protocols{};
|
||||
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
|
||||
|
||||
|
||||
@@ -88,8 +88,9 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
|
||||
encoded_data.application_area_id = nfc_data.user_memory.application_area_id;
|
||||
encoded_data.application_id_byte = nfc_data.user_memory.application_id_byte;
|
||||
encoded_data.unknown = nfc_data.user_memory.unknown;
|
||||
encoded_data.mii_extension = nfc_data.user_memory.mii_extension;
|
||||
encoded_data.unknown2 = nfc_data.user_memory.unknown2;
|
||||
encoded_data.application_area_crc = nfc_data.user_memory.application_area_crc;
|
||||
encoded_data.register_info_crc = nfc_data.user_memory.register_info_crc;
|
||||
encoded_data.application_area = nfc_data.user_memory.application_area;
|
||||
encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
|
||||
encoded_data.lock_bytes = nfc_data.uuid.lock_bytes;
|
||||
@@ -122,8 +123,9 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
|
||||
nfc_data.user_memory.application_area_id = encoded_data.application_area_id;
|
||||
nfc_data.user_memory.application_id_byte = encoded_data.application_id_byte;
|
||||
nfc_data.user_memory.unknown = encoded_data.unknown;
|
||||
nfc_data.user_memory.mii_extension = encoded_data.mii_extension;
|
||||
nfc_data.user_memory.unknown2 = encoded_data.unknown2;
|
||||
nfc_data.user_memory.application_area_crc = encoded_data.application_area_crc;
|
||||
nfc_data.user_memory.register_info_crc = encoded_data.register_info_crc;
|
||||
nfc_data.user_memory.application_area = encoded_data.application_area;
|
||||
nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag;
|
||||
nfc_data.user_memory.model_info = encoded_data.model_info;
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
|
||||
#endif
|
||||
|
||||
#include <boost/crc.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "common/input.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -55,8 +66,18 @@ NfpDevice::~NfpDevice() {
|
||||
};
|
||||
|
||||
void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Connected ||
|
||||
type == Core::HID::ControllerTriggerType::Disconnected) {
|
||||
if (!is_initalized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == Core::HID::ControllerTriggerType::Connected) {
|
||||
Initialize();
|
||||
availability_change_event->Signal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == Core::HID::ControllerTriggerType::Disconnected) {
|
||||
device_state = DeviceState::Unavailable;
|
||||
availability_change_event->Signal();
|
||||
return;
|
||||
}
|
||||
@@ -134,6 +155,7 @@ void NfpDevice::Initialize() {
|
||||
device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
|
||||
encrypted_tag_data = {};
|
||||
tag_data = {};
|
||||
is_initalized = true;
|
||||
}
|
||||
|
||||
void NfpDevice::Finalize() {
|
||||
@@ -144,6 +166,7 @@ void NfpDevice::Finalize() {
|
||||
StopDetection();
|
||||
}
|
||||
device_state = DeviceState::Unavailable;
|
||||
is_initalized = false;
|
||||
}
|
||||
|
||||
Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
|
||||
@@ -448,7 +471,7 @@ Result NfpDevice::DeleteRegisterInfo() {
|
||||
rng.GenerateRandomBytes(&tag_data.unknown, sizeof(u8));
|
||||
rng.GenerateRandomBytes(&tag_data.unknown2[0], sizeof(u32));
|
||||
rng.GenerateRandomBytes(&tag_data.unknown2[1], sizeof(u32));
|
||||
rng.GenerateRandomBytes(&tag_data.application_area_crc, sizeof(u32));
|
||||
rng.GenerateRandomBytes(&tag_data.register_info_crc, sizeof(u32));
|
||||
rng.GenerateRandomBytes(&tag_data.settings.init_date, sizeof(u32));
|
||||
tag_data.settings.settings.font_region.Assign(0);
|
||||
tag_data.settings.settings.amiibo_initialized.Assign(0);
|
||||
@@ -471,6 +494,7 @@ Result NfpDevice::SetRegisterInfoPrivate(const AmiiboName& amiibo_name) {
|
||||
}
|
||||
|
||||
Service::Mii::MiiManager manager;
|
||||
const auto mii = manager.BuildDefault(0);
|
||||
auto& settings = tag_data.settings;
|
||||
|
||||
if (tag_data.settings.settings.amiibo_initialized == 0) {
|
||||
@@ -479,16 +503,15 @@ Result NfpDevice::SetRegisterInfoPrivate(const AmiiboName& amiibo_name) {
|
||||
}
|
||||
|
||||
SetAmiiboName(settings, amiibo_name);
|
||||
tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0));
|
||||
tag_data.owner_mii = manager.BuildFromStoreData(mii);
|
||||
tag_data.mii_extension = manager.SetFromStoreData(mii);
|
||||
tag_data.unknown = 0;
|
||||
tag_data.unknown2[6] = 0;
|
||||
tag_data.unknown2 = {};
|
||||
settings.country_code_id = 0;
|
||||
settings.settings.font_region.Assign(0);
|
||||
settings.settings.amiibo_initialized.Assign(1);
|
||||
|
||||
// TODO: this is a mix of tag.file input
|
||||
std::array<u8, 0x7e> unknown_input{};
|
||||
tag_data.application_area_crc = CalculateCrc(unknown_input);
|
||||
UpdateRegisterInfoCrc();
|
||||
|
||||
return Flush();
|
||||
}
|
||||
@@ -685,6 +708,11 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> dat
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
if (is_app_area_open) {
|
||||
LOG_ERROR(Service_NFP, "Application area is open");
|
||||
return WrongDeviceState;
|
||||
}
|
||||
|
||||
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||
return WrongDeviceState;
|
||||
@@ -715,10 +743,9 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> dat
|
||||
tag_data.settings.settings.appdata_initialized.Assign(1);
|
||||
tag_data.application_area_id = access_id;
|
||||
tag_data.unknown = {};
|
||||
tag_data.unknown2 = {};
|
||||
|
||||
// TODO: this is a mix of tag_data input
|
||||
std::array<u8, 0x7e> unknown_input{};
|
||||
tag_data.application_area_crc = CalculateCrc(unknown_input);
|
||||
UpdateRegisterInfoCrc();
|
||||
|
||||
return Flush();
|
||||
}
|
||||
@@ -752,6 +779,10 @@ Result NfpDevice::DeleteApplicationArea() {
|
||||
rng.GenerateRandomBytes(&tag_data.application_id_byte, sizeof(u8));
|
||||
tag_data.settings.settings.appdata_initialized.Assign(0);
|
||||
tag_data.unknown = {};
|
||||
tag_data.unknown2 = {};
|
||||
is_app_area_open = false;
|
||||
|
||||
UpdateRegisterInfoCrc();
|
||||
|
||||
return Flush();
|
||||
}
|
||||
@@ -835,32 +866,34 @@ void NfpDevice::UpdateSettingsCrc() {
|
||||
|
||||
// TODO: this reads data from a global, find what it is
|
||||
std::array<u8, 8> unknown_input{};
|
||||
settings.crc = CalculateCrc(unknown_input);
|
||||
boost::crc_32_type crc;
|
||||
crc.process_bytes(&unknown_input, sizeof(unknown_input));
|
||||
settings.crc = crc.checksum();
|
||||
}
|
||||
|
||||
u32 NfpDevice::CalculateCrc(std::span<const u8> data) {
|
||||
constexpr u32 magic = 0xedb88320;
|
||||
u32 crc = 0xffffffff;
|
||||
void NfpDevice::UpdateRegisterInfoCrc() {
|
||||
#pragma pack(push, 1)
|
||||
struct CrcData {
|
||||
Mii::Ver3StoreData mii;
|
||||
u8 application_id_byte;
|
||||
u8 unknown;
|
||||
Mii::NfpStoreDataExtension mii_extension;
|
||||
std::array<u32, 0x5> unknown2;
|
||||
};
|
||||
static_assert(sizeof(CrcData) == 0x7e, "CrcData is an invalid size");
|
||||
#pragma pack(pop)
|
||||
|
||||
if (data.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
const CrcData crc_data{
|
||||
.mii = tag_data.owner_mii,
|
||||
.application_id_byte = tag_data.application_id_byte,
|
||||
.unknown = tag_data.unknown,
|
||||
.mii_extension = tag_data.mii_extension,
|
||||
.unknown2 = tag_data.unknown2,
|
||||
};
|
||||
|
||||
for (u8 input : data) {
|
||||
u32 temp = (crc ^ input) >> 1;
|
||||
if (((crc ^ input) & 1) != 0) {
|
||||
temp = temp ^ magic;
|
||||
}
|
||||
|
||||
for (std::size_t step = 0; step < 7; ++step) {
|
||||
crc = temp >> 1;
|
||||
if ((temp & 1) != 0) {
|
||||
crc = temp >> 1 ^ magic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
boost::crc_32_type crc;
|
||||
crc.process_bytes(&crc_data, sizeof(CrcData));
|
||||
tag_data.register_info_crc = crc.checksum();
|
||||
}
|
||||
|
||||
} // namespace Service::NFP
|
||||
|
||||
@@ -80,7 +80,7 @@ private:
|
||||
AmiiboDate GetAmiiboDate(s64 posix_time) const;
|
||||
u64 RemoveVersionByte(u64 application_id) const;
|
||||
void UpdateSettingsCrc();
|
||||
u32 CalculateCrc(std::span<const u8>);
|
||||
void UpdateRegisterInfoCrc();
|
||||
|
||||
bool is_controller_set{};
|
||||
int callback_key;
|
||||
@@ -92,6 +92,7 @@ private:
|
||||
Kernel::KEvent* deactivate_event = nullptr;
|
||||
Kernel::KEvent* availability_change_event = nullptr;
|
||||
|
||||
bool is_initalized{};
|
||||
bool is_data_moddified{};
|
||||
bool is_app_area_open{};
|
||||
TagProtocol allowed_protocols{};
|
||||
|
||||
@@ -259,8 +259,9 @@ struct EncryptedAmiiboFile {
|
||||
u32_be application_area_id; // Encrypted Game id
|
||||
u8 application_id_byte;
|
||||
u8 unknown;
|
||||
std::array<u32, 0x7> unknown2;
|
||||
u32_be application_area_crc;
|
||||
Service::Mii::NfpStoreDataExtension mii_extension;
|
||||
std::array<u32, 0x5> unknown2;
|
||||
u32_be register_info_crc;
|
||||
ApplicationArea application_area; // Encrypted Game data
|
||||
};
|
||||
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
|
||||
@@ -280,8 +281,9 @@ struct NTAG215File {
|
||||
u32_be application_area_id;
|
||||
u8 application_id_byte;
|
||||
u8 unknown;
|
||||
std::array<u32, 0x7> unknown2;
|
||||
u32_be application_area_crc;
|
||||
Service::Mii::NfpStoreDataExtension mii_extension;
|
||||
std::array<u32, 0x5> unknown2;
|
||||
u32_be register_info_crc;
|
||||
ApplicationArea application_area; // Encrypted Game data
|
||||
HashData hmac_tag; // Hash
|
||||
UniqueSerialNumber uid; // Unique serial number
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
add_executable(tests
|
||||
common/bit_field.cpp
|
||||
common/cityhash.cpp
|
||||
common/container_hash.cpp
|
||||
common/fibers.cpp
|
||||
common/host_memory.cpp
|
||||
common/param_package.cpp
|
||||
|
||||
41
src/tests/common/container_hash.cpp
Normal file
41
src/tests/common/container_hash.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/container_hash.h"
|
||||
|
||||
TEST_CASE("ContainerHash", "[common]") {
|
||||
constexpr std::array<u8, 32> U8Values{
|
||||
114, 10, 238, 189, 199, 242, 86, 96, 53, 193, 195, 247, 249, 56, 253, 61,
|
||||
205, 3, 172, 4, 210, 197, 43, 72, 103, 8, 99, 89, 5, 97, 68, 196,
|
||||
};
|
||||
constexpr std::array<u16, 32> U16Values{
|
||||
61586, 49151, 3313, 11641, 31695, 54795, 46764, 20965, 23287, 14039, 19265,
|
||||
49093, 58932, 22518, 27139, 42825, 57417, 54237, 48057, 14586, 42813, 32994,
|
||||
33970, 45501, 5619, 15895, 33227, 27509, 25391, 37275, 60218, 17599,
|
||||
};
|
||||
constexpr std::array<u32, 32> U32Values{
|
||||
3838402410, 2029146863, 1730869921, 985528872, 186773874, 2094639868, 3324775932,
|
||||
1795512424, 2571165571, 3256934519, 2358691590, 2752682538, 1484336451, 378124520,
|
||||
3463015699, 3395942161, 1263211979, 3473632889, 3039822212, 2068707357, 2223837919,
|
||||
1823232191, 1583884041, 1264393380, 4087566993, 3188607101, 3933680362, 1464520765,
|
||||
1786838406, 1311734848, 2773642241, 3993641692,
|
||||
};
|
||||
constexpr std::array<u64, 32> U64Values{
|
||||
5908025796157537817, 10947547850358315100, 844798943576724669, 7999662937458523703,
|
||||
4006550374705895164, 1832550525423503632, 9323088254855830976, 12028890075598379412,
|
||||
6021511300787826236, 7864675007938747948, 18099387408859708806, 6438638299316820708,
|
||||
9029399285648501543, 18195459433089960253, 17214335092761966083, 5549347964591337833,
|
||||
14899526073304962015, 5058883181561464475, 7436311795731206973, 7535129567768649864,
|
||||
1287169596809258072, 8237671246353565927, 1715230541978016153, 8443157615068813300,
|
||||
6098675262328527839, 704652094100376853, 1303411723202926503, 7808312933946424854,
|
||||
6863726670433556594, 9870361541383217495, 9273671094091079488, 17541434976160119010,
|
||||
};
|
||||
|
||||
REQUIRE(Common::HashValue(U8Values) == 5867183267093890552);
|
||||
REQUIRE(Common::HashValue(U16Values) == 9594135570564347135);
|
||||
REQUIRE(Common::HashValue(U32Values) == 13123757214696618460);
|
||||
REQUIRE(Common::HashValue(U64Values) == 7296500016546938380);
|
||||
}
|
||||
@@ -31,8 +31,10 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||
auto current_context = context.Acquire();
|
||||
VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
|
||||
|
||||
CommandDataContainer next;
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
CommandDataContainer next = state.queue.PopWait(stop_token);
|
||||
state.queue.PopWait(next, stop_token);
|
||||
if (stop_token.stop_requested()) {
|
||||
break;
|
||||
}
|
||||
@@ -116,7 +118,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
|
||||
|
||||
std::unique_lock lk(state.write_lock);
|
||||
const u64 fence{++state.last_fence};
|
||||
state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
|
||||
state.queue.EmplaceWait(std::move(command_data), fence, block);
|
||||
|
||||
if (block) {
|
||||
Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] {
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
|
||||
#include "common/bounded_threadsafe_queue.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "video_core/framebuffer_config.h"
|
||||
|
||||
namespace Tegra {
|
||||
@@ -97,7 +97,7 @@ struct CommandDataContainer {
|
||||
|
||||
/// Struct used to synchronize the GPU thread
|
||||
struct SynchState final {
|
||||
using CommandQueue = Common::MPSCQueue<CommandDataContainer, true>;
|
||||
using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
|
||||
std::mutex write_lock;
|
||||
CommandQueue queue;
|
||||
u64 last_fence{};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include "common/container_hash.h"
|
||||
|
||||
#include <fstream>
|
||||
#include "common/assert.h"
|
||||
@@ -89,7 +89,7 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
|
||||
if (!mid_method.has_value()) {
|
||||
cache_info.lle_program = Compile(macro_code->second);
|
||||
cache_info.hash = boost::hash_value(macro_code->second);
|
||||
cache_info.hash = Common::HashValue(macro_code->second);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, macro_code->second);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
code.resize(macro_cached.size() - rebased_method);
|
||||
std::memcpy(code.data(), macro_cached.data() + rebased_method,
|
||||
code.size() * sizeof(u32));
|
||||
cache_info.hash = boost::hash_value(code);
|
||||
cache_info.hash = Common::HashValue(code);
|
||||
cache_info.lle_program = Compile(code);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, code);
|
||||
|
||||
@@ -65,12 +65,13 @@ void Scheduler::WaitWorker() {
|
||||
DispatchWork();
|
||||
|
||||
// Ensure the queue is drained.
|
||||
std::unique_lock ql{queue_mutex};
|
||||
event_cv.wait(ql, [this] { return work_queue.empty(); });
|
||||
{
|
||||
std::unique_lock ql{queue_mutex};
|
||||
event_cv.wait(ql, [this] { return work_queue.empty(); });
|
||||
}
|
||||
|
||||
// Now wait for execution to finish.
|
||||
// This needs to be done in the same order as WorkerThread.
|
||||
std::unique_lock el{execution_mutex};
|
||||
std::scoped_lock el{execution_mutex};
|
||||
}
|
||||
|
||||
void Scheduler::DispatchWork() {
|
||||
@@ -327,7 +328,7 @@ void Scheduler::AcquireNewChunk() {
|
||||
chunk = std::make_unique<CommandChunk>();
|
||||
} else {
|
||||
// Otherwise, we can just take from the reserve.
|
||||
chunk = std::make_unique<CommandChunk>();
|
||||
chunk = std::move(chunk_reserve.back());
|
||||
chunk_reserve.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1616,37 +1616,38 @@ void TextureCache<P>::ForEachImageInRegionGPU(size_t as_id, GPUVAddr gpu_addr, s
|
||||
return;
|
||||
}
|
||||
auto& gpu_page_table = gpu_page_table_storage[*storage_id];
|
||||
ForEachGPUPage(gpu_addr, size, [this, gpu_page_table, &images, gpu_addr, size, func](u64 page) {
|
||||
const auto it = gpu_page_table.find(page);
|
||||
if (it == gpu_page_table.end()) {
|
||||
if constexpr (BOOL_BREAK) {
|
||||
return false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const ImageId image_id : it->second) {
|
||||
Image& image = slot_images[image_id];
|
||||
if (True(image.flags & ImageFlagBits::Picked)) {
|
||||
continue;
|
||||
}
|
||||
if (!image.OverlapsGPU(gpu_addr, size)) {
|
||||
continue;
|
||||
}
|
||||
image.flags |= ImageFlagBits::Picked;
|
||||
images.push_back(image_id);
|
||||
if constexpr (BOOL_BREAK) {
|
||||
if (func(image_id, image)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
func(image_id, image);
|
||||
}
|
||||
}
|
||||
if constexpr (BOOL_BREAK) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
ForEachGPUPage(gpu_addr, size,
|
||||
[this, &gpu_page_table, &images, gpu_addr, size, func](u64 page) {
|
||||
const auto it = gpu_page_table.find(page);
|
||||
if (it == gpu_page_table.end()) {
|
||||
if constexpr (BOOL_BREAK) {
|
||||
return false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const ImageId image_id : it->second) {
|
||||
Image& image = slot_images[image_id];
|
||||
if (True(image.flags & ImageFlagBits::Picked)) {
|
||||
continue;
|
||||
}
|
||||
if (!image.OverlapsGPU(gpu_addr, size)) {
|
||||
continue;
|
||||
}
|
||||
image.flags |= ImageFlagBits::Picked;
|
||||
images.push_back(image_id);
|
||||
if constexpr (BOOL_BREAK) {
|
||||
if (func(image_id, image)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
func(image_id, image);
|
||||
}
|
||||
}
|
||||
if constexpr (BOOL_BREAK) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
for (const ImageId image_id : images) {
|
||||
slot_images[image_id].flags &= ~ImageFlagBits::Picked;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Tegra::Texture {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
|
||||
[[maybe_unused]] constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
|
||||
0.000000f, 0.000000f, 0.000000f, 0.000012f, 0.000021f, 0.000033f, 0.000046f, 0.000062f,
|
||||
0.000081f, 0.000102f, 0.000125f, 0.000151f, 0.000181f, 0.000214f, 0.000251f, 0.000293f,
|
||||
0.000338f, 0.000388f, 0.000443f, 0.000503f, 0.000568f, 0.000639f, 0.000715f, 0.000798f,
|
||||
@@ -52,11 +52,13 @@ constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
|
||||
} // Anonymous namespace
|
||||
|
||||
std::array<float, 4> TSCEntry::BorderColor() const noexcept {
|
||||
if (!srgb_conversion) {
|
||||
return border_color;
|
||||
}
|
||||
return {SRGB_CONVERSION_LUT[srgb_border_color_r], SRGB_CONVERSION_LUT[srgb_border_color_g],
|
||||
SRGB_CONVERSION_LUT[srgb_border_color_b], border_color[3]};
|
||||
// TODO: Handle SRGB correctly. Using this breaks shadows in some games (Xenoblade).
|
||||
// if (!srgb_conversion) {
|
||||
// return border_color;
|
||||
//}
|
||||
// return {SRGB_CONVERSION_LUT[srgb_border_color_r], SRGB_CONVERSION_LUT[srgb_border_color_g],
|
||||
// SRGB_CONVERSION_LUT[srgb_border_color_b], border_color[3]};
|
||||
return border_color;
|
||||
}
|
||||
|
||||
float TSCEntry::MaxAnisotropy() const noexcept {
|
||||
|
||||
@@ -189,6 +189,8 @@ QList<QWidget*> ConfigureInput::GetSubTabs() const {
|
||||
}
|
||||
|
||||
void ConfigureInput::ApplyConfiguration() {
|
||||
const bool was_global = Settings::values.players.UsingGlobal();
|
||||
Settings::values.players.SetGlobal(true);
|
||||
for (auto* controller : player_controllers) {
|
||||
controller->ApplyConfiguration();
|
||||
}
|
||||
@@ -201,6 +203,7 @@ void ConfigureInput::ApplyConfiguration() {
|
||||
|
||||
Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
|
||||
Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
|
||||
Settings::values.players.SetGlobal(was_global);
|
||||
}
|
||||
|
||||
void ConfigureInput::changeEvent(QEvent* event) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||
"name": "yuzu",
|
||||
"builtin-baseline": "9b22b40c6c61bf0da2d46346dd44a11e90972cc9",
|
||||
"builtin-baseline": "acc3bcf76b84ae5041c86ab55fe138ae7b8255c7",
|
||||
"version": "1.0",
|
||||
"dependencies": [
|
||||
"boost-algorithm",
|
||||
|
||||
Reference in New Issue
Block a user