Compare commits

...

41 Commits

Author SHA1 Message Date
Morph
49f6deecb8 video_core/macro: Make use of Common::HashValue 2023-03-25 23:52:26 -04:00
Morph
f09d192aac tests: Implement tests for verifying HashValue
Values were randomly generated and the verification was done against boost 1.79.
2023-03-25 23:52:26 -04:00
Morph
9971cd1d55 common: Port boost's hash_value implementation
Ports a small subset of boost's hash_value implementation (<= 1.80.0).
2023-03-25 23:52:25 -04:00
Morph
c4f5615c6b CMakeLists: Require a minimum of boost 1.79.0 2023-03-25 23:03:28 -04:00
bunnei
09da9da6fb Merge pull request #9985 from liamwhite/funny-meme
vulkan: fix scheduler chunk reserve
2023-03-24 23:40:17 -07:00
Morph
6892a0942f Merge pull request #9988 from rschlaikjer/rs-gpu-page-table-copy-elision
Pass GPU page table by reference inside TextureCache::ForEachImageInRegionGPU
2023-03-25 01:59:08 -04:00
Ross Schlaikjer
f38ae8e953 Pass GPU page table by reference 2023-03-25 00:25:02 -04:00
liamwhite
cfb9672093 Merge pull request #9983 from Morph1984/boost
CMakeLists: Update boost to 1.81.0
2023-03-24 10:53:30 -04:00
liamwhite
462c430c8b Merge pull request #9981 from german77/nfp_connect
nfc: Initialize device when controller is connected
2023-03-24 10:53:05 -04:00
Liam
5a2dff87bf vulkan: fix scheduler chunk reserve 2023-03-24 09:09:01 -04:00
Morph
7a8a7545f2 Merge pull request #9975 from liamwhite/more-waiting
vulkan: fix more excessive waiting in scheduler
2023-03-24 00:19:43 -04:00
Morph
abe2ad7aac zstd: Use ZSTD_getFrameContentSize instead of ZSTD_getDecompressedSize 2023-03-23 22:16:20 -04:00
Morph
877e8991c7 CMakeLists: Update boost to 1.81.0 2023-03-23 20:53:39 -04:00
Morph
032e5b983c vcpkg: Update vcpkg to 2023.02.24 2023-03-23 20:53:39 -04:00
liamwhite
ac3927074b Merge pull request #9971 from Morph1984/q
bounded_threadsafe_queue: Use simplified impl of bounded queue
2023-03-23 10:00:31 -04:00
liamwhite
c41a4baf06 Merge pull request #9964 from liamwhite/typed-address
kernel: use KTypedAddress for addresses
2023-03-23 10:00:19 -04:00
Morph
6adaa0d5e2 Merge pull request #9962 from Kelebek1/disable_srgb
[video_core] Disable SRGB border color conversion in samplers
2023-03-23 03:07:00 -04:00
Morph
197d756560 bounded_threadsafe_queue: Refactor Pop
Introduces PopModes to bring waiting logic into Pop, similar to Push.
2023-03-21 22:33:58 -04:00
Morph
8c56481249 bounded_threadsafe_queue: Add producer cv to avoid busy waiting 2023-03-21 22:33:57 -04:00
Narr the Reg
6ff4bf9b1c nfc: Initialize device when controller is connected 2023-03-21 20:09:36 -06:00
bunnei
dba86ee007 Merge pull request #9965 from german77/thankYouEpicBoy
config: Fix controller config from resetting
2023-03-21 17:52:25 -07:00
Morph
407dc917f1 bounded_threadsafe_queue: Deduplicate and add PushModes
Adds the PushModes Try and Wait to allow producers to specify how they want to push their data to the queue if the queue is full.
If the queue is full:
- Try will fail to push to the queue, returning false. Try only returns true if it successfully pushes to the queue. This may result in items not being pushed into the queue.
- Wait will wait until a slot is available to push to the queue, resulting in potential for deadlock if a consumer is not running.
2023-03-21 19:20:21 -04:00
Morph
15d573194c bounded_threadsafe_queue: Add TryPush 2023-03-21 19:17:38 -04:00
Morph
f28ca5361f logging: Make use of bounded queue 2023-03-21 19:17:38 -04:00
Morph
306840a580 bounded_threadsafe_queue: Use simplified impl of bounded queue
Provides a simplified SPSC, MPSC, and MPMC bounded queue implementation using mutexes.
2023-03-21 19:17:32 -04:00
bunnei
3d4c113037 Merge pull request #9970 from bunnei/string-util-view
common: string_util: Use std::string_view for UTF16ToUTF8/UTF8ToUTF16W.
2023-03-19 11:10:16 -07:00
bunnei
230d118252 Merge pull request #9972 from liamwhite/ipc-trace
kernel: fix LOG_TRACE in ipc
2023-03-19 11:00:42 -07:00
Liam
b9b1318bea vulkan: fix more excessive waiting in scheduler 2023-03-19 13:40:33 -04:00
Liam
43d909949e kernel: fix LOG_TRACE in ipc 2023-03-19 10:02:20 -04:00
bunnei
00d401d639 common: string_util: Use std::string_view for UTF16ToUTF8/UTF8ToUTF16W. 2023-03-18 22:42:25 -07:00
liamwhite
0e7e98e24e Merge pull request #9966 from bunnei/bounded-polyfill
common: bounded_threadsafe_queue: Use polyfill_thread.
2023-03-18 12:39:52 -04:00
bunnei
0eb3fa05e5 common: bounded_threadsafe_queue: Use polyfill_thread. 2023-03-17 23:42:17 -07:00
bunnei
889454f9bf Merge pull request #9778 from behunin/my-box-chevy
gpu_thread: Use bounded queue
2023-03-17 22:14:29 -07:00
bunnei
8bcaa8c2e4 Merge pull request #9953 from german77/amiibo_crc
service: nfp: Actually write correct crc
2023-03-17 22:13:57 -07:00
Narr the Reg
c95baf92ce config: Fix controller config from resetting 2023-03-17 22:08:16 -06:00
Kelebek1
a7651168dd Disable SRGB border color conversion for now, to fix shadows in Xenoblade. 2023-03-17 04:46:38 +00:00
Narr the Reg
075a3d1172 service: nfp: Replace crc function with boost equivalent 2023-03-16 17:47:32 -06:00
Narr the Reg
7187732454 service: nfp: Close app area and recreate crc 2023-03-15 17:01:06 -06:00
Narr the Reg
5031f5b8b0 service: nfp: Convert mii colors to v3 2023-03-15 17:01:05 -06:00
Narr the Reg
026fe2e4f4 service: nfp: Actually write correct crc 2023-03-14 23:42:41 -06:00
Behunin
44518b225c gpu_thread: Use bounded queue 2023-03-03 18:20:56 -07:00
29 changed files with 611 additions and 246 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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;
};

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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(

View File

@@ -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());
}

View File

@@ -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;

View File

@@ -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{};

View File

@@ -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>;

View File

@@ -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) {

View File

@@ -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};

View File

@@ -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;

View File

@@ -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

View File

@@ -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{};

View File

@@ -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

View File

@@ -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

View 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);
}

View File

@@ -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] {

View File

@@ -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{};

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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",