Compare commits
167 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f989378c0 | ||
|
|
3bbf4462db | ||
|
|
267d483ed4 | ||
|
|
fb563e75e9 | ||
|
|
104c523d3d | ||
|
|
be68ee88c2 | ||
|
|
b6d73ec9c2 | ||
|
|
98b36625fa | ||
|
|
d84d9a64b3 | ||
|
|
07d080ecc8 | ||
|
|
f0125b2be8 | ||
|
|
51546ce57e | ||
|
|
201514cb50 | ||
|
|
a89dfc9183 | ||
|
|
2781201bfb | ||
|
|
9121d35e70 | ||
|
|
0648e023ea | ||
|
|
815f30dc10 | ||
|
|
f66e3181dc | ||
|
|
f26f53f35b | ||
|
|
73bb87c06b | ||
|
|
52e83f0d5c | ||
|
|
4ad69ca96e | ||
|
|
0ca7b8269a | ||
|
|
1bbc61f5f1 | ||
|
|
8794e623d9 | ||
|
|
a82fdea1ac | ||
|
|
263200f982 | ||
|
|
9f027b1af2 | ||
|
|
666b37ad56 | ||
|
|
a683e42516 | ||
|
|
6d1477f214 | ||
|
|
93fe982a0c | ||
|
|
e2730372b8 | ||
|
|
450cbcfee6 | ||
|
|
bf9c010be5 | ||
|
|
ab65de2f96 | ||
|
|
393cdb15f5 | ||
|
|
edb291b3be | ||
|
|
6989fd65f3 | ||
|
|
8160e142e1 | ||
|
|
e02687ff47 | ||
|
|
0f8b977663 | ||
|
|
edb2caaae5 | ||
|
|
b284c43385 | ||
|
|
0475a167f8 | ||
|
|
883fab2fff | ||
|
|
f2f876e3ff | ||
|
|
ed0fe04b4f | ||
|
|
c3eb42de65 | ||
|
|
db6fbd5894 | ||
|
|
f1d8c83e1c | ||
|
|
07632ad825 | ||
|
|
b2305dcee0 | ||
|
|
b971b82275 | ||
|
|
84b5804834 | ||
|
|
539675b21a | ||
|
|
8df93132cd | ||
|
|
a1dddca4ab | ||
|
|
839c91cd14 | ||
|
|
a8ba6dc3c9 | ||
|
|
32b6fc4062 | ||
|
|
9ce6ea648f | ||
|
|
a67d00ef31 | ||
|
|
739d90ee66 | ||
|
|
ed89bcc767 | ||
|
|
f1aabc21ee | ||
|
|
80a0f2a118 | ||
|
|
6001538139 | ||
|
|
63cc4e417f | ||
|
|
e60733aad3 | ||
|
|
5fb27f83cf | ||
|
|
f16a94fb39 | ||
|
|
e706501c8d | ||
|
|
e5abf11186 | ||
|
|
1074c87f18 | ||
|
|
7a051c4973 | ||
|
|
a4306b9e56 | ||
|
|
da11a27f42 | ||
|
|
505aa3a4c1 | ||
|
|
a59ad9246b | ||
|
|
6744e7ea4a | ||
|
|
0193202964 | ||
|
|
fb0fefc75c | ||
|
|
a0ee597b19 | ||
|
|
a45a57641f | ||
|
|
fca26980a2 | ||
|
|
995067538d | ||
|
|
01a1adfb0c | ||
|
|
042c6602a0 | ||
|
|
798c1b457d | ||
|
|
c574ab5aa1 | ||
|
|
d7a2dc4cea | ||
|
|
d39b457566 | ||
|
|
755506d404 | ||
|
|
7351ca8c75 | ||
|
|
75a01475d1 | ||
|
|
6d8d7ebc66 | ||
|
|
7f4d96d873 | ||
|
|
4c269e5ced | ||
|
|
b24b463c87 | ||
|
|
17242a8865 | ||
|
|
0373ead96e | ||
|
|
47e26d7bc7 | ||
|
|
0974533c96 | ||
|
|
df27813e76 | ||
|
|
bd2feb6873 | ||
|
|
48aa076049 | ||
|
|
e73c53fad1 | ||
|
|
63d23835ef | ||
|
|
0f60ccdde2 | ||
|
|
f9e748b721 | ||
|
|
1c7d106aac | ||
|
|
83b1b259bd | ||
|
|
95cd2e17ab | ||
|
|
48ff15602e | ||
|
|
77bdd09aa2 | ||
|
|
4489ea6f53 | ||
|
|
5311b562aa | ||
|
|
9c2773409a | ||
|
|
713e204a8a | ||
|
|
3c7a115afe | ||
|
|
ec13746e4d | ||
|
|
cf76769026 | ||
|
|
4e2464a713 | ||
|
|
2a87ddcce2 | ||
|
|
2a9d17b7e7 | ||
|
|
dff8f1f9f8 | ||
|
|
e7c77b90c4 | ||
|
|
6ae0b83442 | ||
|
|
b57475887b | ||
|
|
35f7740b6c | ||
|
|
2af7e40ce6 | ||
|
|
1db10b8f4c | ||
|
|
155c8ba04c | ||
|
|
e3253b5f18 | ||
|
|
86abff48e1 | ||
|
|
7ad423923d | ||
|
|
40cf9288e9 | ||
|
|
7bd3558c64 | ||
|
|
b700d657d7 | ||
|
|
fa4d6df4c5 | ||
|
|
4f678284e2 | ||
|
|
be06b21f7b | ||
|
|
76bd617b2d | ||
|
|
c0dc8f9d25 | ||
|
|
52882a93a5 | ||
|
|
3f910efb40 | ||
|
|
38b585a309 | ||
|
|
b1b1ed7597 | ||
|
|
f6cb128eac | ||
|
|
28aa697528 | ||
|
|
24c2930012 | ||
|
|
86946ea13c | ||
|
|
f3630a0713 | ||
|
|
a0c499aef7 | ||
|
|
f2eead3b5b | ||
|
|
6a0010d0c6 | ||
|
|
8562b516c0 | ||
|
|
9ff3401133 | ||
|
|
9d55e5586f | ||
|
|
8584a77eb2 | ||
|
|
1a84209418 | ||
|
|
c94b398f14 | ||
|
|
a6db8e5f4d | ||
|
|
c387a72c76 | ||
|
|
7527402a46 |
@@ -1,4 +1,13 @@
|
||||
<!--
|
||||
---
|
||||
name: Bug Report / Feature Request
|
||||
about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
|
||||
|
||||
Please read the FAQ:
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: yuzu Discord
|
||||
url: https://discord.com/invite/u77vRWY
|
||||
about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
|
||||
- name: Community forums
|
||||
url: https://community.citra-emu.org
|
||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
||||
@@ -118,8 +118,17 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||
# Configure C++ standard
|
||||
# ===========================
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
# boost asio's concept usage doesn't play nicely with some compilers yet.
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
|
||||
if (MSVC)
|
||||
add_compile_options(/std:c++latest)
|
||||
|
||||
# cubeb and boost still make use of deprecated result_of.
|
||||
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
# Output binaries to bin/
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
@@ -151,7 +160,7 @@ macro(yuzu_find_packages)
|
||||
# Cmake Pkg Prefix Version Conan Pkg
|
||||
"Boost 1.71 boost/1.72.0"
|
||||
"Catch2 2.11 catch2/2.11.0"
|
||||
"fmt 6.2 fmt/6.2.0"
|
||||
"fmt 7.0 fmt/7.0.1"
|
||||
# can't use until https://github.com/bincrafters/community/issues/1173
|
||||
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
@@ -211,7 +220,7 @@ if(ENABLE_QT)
|
||||
|
||||
set(QT_PREFIX_HINT HINTS "${QT_PREFIX}")
|
||||
endif()
|
||||
find_package(Qt5 5.9 COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
|
||||
find_package(Qt5 5.9 COMPONENTS Widgets ${QT_PREFIX_HINT})
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
@@ -287,7 +296,7 @@ if (CONAN_REQUIRED_LIBS)
|
||||
if(ENABLE_QT)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets OpenGL)
|
||||
find_package(Qt5 5.9 REQUIRED COMPONENTS Widgets)
|
||||
if (YUZU_USE_QT_WEB_ENGINE)
|
||||
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
|
||||
endif()
|
||||
@@ -330,10 +339,15 @@ elseif(SDL2_FOUND)
|
||||
endif()
|
||||
|
||||
# Ensure libusb is properly configured (based on dolphin libusb include)
|
||||
find_package(LibUSB)
|
||||
add_subdirectory(externals/libusb)
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
|
||||
if(NOT APPLE)
|
||||
include(FindPkgConfig)
|
||||
find_package(LibUSB)
|
||||
endif()
|
||||
if (NOT LIBUSB_FOUND)
|
||||
add_subdirectory(externals/libusb)
|
||||
set(LIBUSB_INCLUDE_DIR "")
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
endif()
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
|
||||
@@ -15,7 +15,6 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
icuuc*.dll
|
||||
Qt5Core$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
||||
Qt5OpenGL$<$<CONFIG:Debug>:d>.*
|
||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ yuzu emulator
|
||||
=============
|
||||
[](https://travis-ci.com/yuzu-emu/yuzu)
|
||||
[](https://dev.azure.com/yuzu-emu/yuzu/)
|
||||
[](https://discord.gg/XQV6dn9)
|
||||
[](https://discord.com/invite/u77vRWY)
|
||||
|
||||
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
|
||||
|
||||
@@ -16,7 +16,7 @@ yuzu is licensed under the GPLv2 (or any later version). Refer to the license.tx
|
||||
|
||||
Check out our [website](https://yuzu-emu.org/)!
|
||||
|
||||
For development discussion, please join us on [Discord](https://discord.gg/XQV6dn9).
|
||||
For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY).
|
||||
|
||||
### Development
|
||||
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: 9250d5ae8f...8188e3fbbc
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 4f967387c0...82417da780
43
externals/find-modules/FindLibUSB.cmake
vendored
Normal file
43
externals/find-modules/FindLibUSB.cmake
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# - Find libusb-1.0 library
|
||||
# This module defines
|
||||
# LIBUSB_INCLUDE_DIR, where to find bluetooth.h
|
||||
# LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0.
|
||||
# LIBUSB_FOUND, If false, do not try to use libusb-1.0.
|
||||
#
|
||||
# Copyright (c) 2009, Michal Cihar, <michal@cihar.com>
|
||||
#
|
||||
# vim: expandtab sw=4 ts=4 sts=4:
|
||||
|
||||
if(ANDROID)
|
||||
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "libusb-1.0 not found.")
|
||||
elseif (NOT LIBUSB_FOUND)
|
||||
pkg_check_modules (LIBUSB_PKG libusb-1.0)
|
||||
|
||||
find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h
|
||||
PATHS
|
||||
${LIBUSB_PKG_INCLUDE_DIRS}
|
||||
/usr/include/libusb-1.0
|
||||
/usr/include
|
||||
/usr/local/include/libusb-1.0
|
||||
/usr/local/include
|
||||
)
|
||||
|
||||
find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb
|
||||
PATHS
|
||||
${LIBUSB_PKG_LIBRARY_DIRS}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
)
|
||||
|
||||
if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}")
|
||||
else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
|
||||
message(STATUS "libusb-1.0 not found.")
|
||||
endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
|
||||
|
||||
mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
|
||||
endif ()
|
||||
|
||||
@@ -193,7 +193,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
|
||||
const std::size_t samples_to_write = num_channels * num_frames;
|
||||
std::size_t samples_written;
|
||||
|
||||
if (Settings::values.enable_audio_stretching) {
|
||||
if (Settings::values.enable_audio_stretching.GetValue()) {
|
||||
const std::vector<s16> in{impl->queue.Pop()};
|
||||
const std::size_t num_in{in.size() / num_channels};
|
||||
s16* const out{reinterpret_cast<s16*>(buffer)};
|
||||
|
||||
@@ -38,7 +38,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
|
||||
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
|
||||
|
||||
release_event = Core::Timing::CreateEvent(
|
||||
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
|
||||
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(cycles_late); });
|
||||
}
|
||||
|
||||
void Stream::Play() {
|
||||
@@ -66,15 +66,6 @@ s64 Stream::GetBufferReleaseNS(const Buffer& buffer) const {
|
||||
return ns.count();
|
||||
}
|
||||
|
||||
s64 Stream::GetBufferReleaseNSHostTiming(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
/// DSP signals before playing the last sample, in HLE we emulate this in this way
|
||||
s64 base_samples = std::max<s64>(static_cast<s64>(num_samples) - 1, 0);
|
||||
const auto ns =
|
||||
std::chrono::nanoseconds((static_cast<u64>(base_samples) * 1000000000ULL) / sample_rate);
|
||||
return ns.count();
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
const float volume{std::clamp(Settings::Volume() - (1.0f - game_volume), 0.0f, 1.0f)};
|
||||
|
||||
@@ -89,7 +80,7 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::PlayNextBuffer() {
|
||||
void Stream::PlayNextBuffer(s64 cycles_late) {
|
||||
if (!IsPlaying()) {
|
||||
// Ensure we are in playing state before playing the next buffer
|
||||
sink_stream.Flush();
|
||||
@@ -114,18 +105,17 @@ void Stream::PlayNextBuffer() {
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
if (core_timing.IsHostTiming()) {
|
||||
core_timing.ScheduleEvent(GetBufferReleaseNSHostTiming(*active_buffer), release_event, {});
|
||||
} else {
|
||||
core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer), release_event, {});
|
||||
}
|
||||
core_timing.ScheduleEvent(
|
||||
GetBufferReleaseNS(*active_buffer) -
|
||||
(Settings::values.enable_audio_stretching.GetValue() ? 0 : cycles_late),
|
||||
release_event, {});
|
||||
}
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
void Stream::ReleaseActiveBuffer(s64 cycles_late) {
|
||||
ASSERT(active_buffer);
|
||||
released_buffers.push(std::move(active_buffer));
|
||||
release_callback();
|
||||
PlayNextBuffer();
|
||||
PlayNextBuffer(cycles_late);
|
||||
}
|
||||
|
||||
bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||
|
||||
@@ -90,10 +90,10 @@ public:
|
||||
|
||||
private:
|
||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||
void PlayNextBuffer();
|
||||
void PlayNextBuffer(s64 cycles_late = 0);
|
||||
|
||||
/// Releases the actively playing buffer, signalling that it has been completed
|
||||
void ReleaseActiveBuffer();
|
||||
void ReleaseActiveBuffer(s64 cycles_late = 0);
|
||||
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
s64 GetBufferReleaseNS(const Buffer& buffer) const;
|
||||
|
||||
@@ -11,7 +11,9 @@ namespace Common {
|
||||
template <typename T>
|
||||
constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value + (size - value % size) % size);
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
return static_cast<T>(mod == T{0} ? value : value + size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -17,43 +17,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include "common/common_types.h"
|
||||
|
||||
// GCC
|
||||
#ifdef __GNUC__
|
||||
|
||||
#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN)
|
||||
#define COMMON_LITTLE_ENDIAN 1
|
||||
#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) && !defined(COMMON_BIG_ENDIAN)
|
||||
#define COMMON_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
// LLVM/clang
|
||||
#elif defined(__clang__)
|
||||
|
||||
#if __LITTLE_ENDIAN__ && !defined(COMMON_LITTLE_ENDIAN)
|
||||
#define COMMON_LITTLE_ENDIAN 1
|
||||
#elif __BIG_ENDIAN__ && !defined(COMMON_BIG_ENDIAN)
|
||||
#define COMMON_BIG_ENDIAN 1
|
||||
#endif
|
||||
|
||||
// MSVC
|
||||
#elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN)
|
||||
|
||||
#define COMMON_LITTLE_ENDIAN 1
|
||||
#endif
|
||||
|
||||
// Worst case, default to little endian.
|
||||
#if !COMMON_BIG_ENDIAN && !COMMON_LITTLE_ENDIAN
|
||||
#define COMMON_LITTLE_ENDIAN 1
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -675,17 +646,8 @@ struct AddEndian<T, SwapTag> {
|
||||
};
|
||||
|
||||
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
|
||||
#if COMMON_LITTLE_ENDIAN
|
||||
|
||||
using LETag = KeepTag;
|
||||
using BETag = SwapTag;
|
||||
|
||||
#else
|
||||
|
||||
using BETag = KeepTag;
|
||||
using LETag = SwapTag;
|
||||
|
||||
#endif
|
||||
using LETag = std::conditional_t<std::endian::native == std::endian::little, KeepTag, SwapTag>;
|
||||
using BETag = std::conditional_t<std::endian::native == std::endian::big, KeepTag, SwapTag>;
|
||||
|
||||
// Aliases for LE types
|
||||
using u16_le = AddEndian<u16, LETag>::type;
|
||||
|
||||
@@ -398,10 +398,13 @@ add_library(core STATIC
|
||||
hle/service/lm/manager.h
|
||||
hle/service/mig/mig.cpp
|
||||
hle/service/mig/mig.h
|
||||
hle/service/mii/manager.cpp
|
||||
hle/service/mii/manager.h
|
||||
hle/service/mii/mii.cpp
|
||||
hle/service/mii/mii.h
|
||||
hle/service/mii/mii_manager.cpp
|
||||
hle/service/mii/mii_manager.h
|
||||
hle/service/mii/raw_data.cpp
|
||||
hle/service/mii/raw_data.h
|
||||
hle/service/mii/types.h
|
||||
hle/service/mm/mm_u.cpp
|
||||
hle/service/mm/mm_u.h
|
||||
hle/service/ncm/ncm.cpp
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
|
||||
|
||||
@@ -142,10 +142,32 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Optimizations
|
||||
if (Settings::values.disable_cpu_opt) {
|
||||
config.enable_optimizations = false;
|
||||
config.enable_fast_dispatch = false;
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
config.page_table = nullptr;
|
||||
}
|
||||
if (!Settings::values.cpuopt_block_linking) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
|
||||
}
|
||||
if (!Settings::values.cpuopt_return_stack_buffer) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fast_dispatcher) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
|
||||
}
|
||||
if (!Settings::values.cpuopt_context_elimination) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
|
||||
}
|
||||
if (!Settings::values.cpuopt_const_prop) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
|
||||
}
|
||||
if (!Settings::values.cpuopt_misc_ir) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
|
||||
}
|
||||
if (!Settings::values.cpuopt_reduce_misalign_checks) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
|
||||
@@ -191,15 +191,37 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||
// Unpredictable instructions
|
||||
config.define_unpredictable_behaviour = true;
|
||||
|
||||
// Optimizations
|
||||
if (Settings::values.disable_cpu_opt) {
|
||||
config.enable_optimizations = false;
|
||||
config.enable_fast_dispatch = false;
|
||||
}
|
||||
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
config.page_table = nullptr;
|
||||
}
|
||||
if (!Settings::values.cpuopt_block_linking) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking;
|
||||
}
|
||||
if (!Settings::values.cpuopt_return_stack_buffer) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ReturnStackBuffer;
|
||||
}
|
||||
if (!Settings::values.cpuopt_fast_dispatcher) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::FastDispatch;
|
||||
}
|
||||
if (!Settings::values.cpuopt_context_elimination) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::GetSetElimination;
|
||||
}
|
||||
if (!Settings::values.cpuopt_const_prop) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::ConstProp;
|
||||
}
|
||||
if (!Settings::values.cpuopt_misc_ir) {
|
||||
config.optimizations &= ~Dynarmic::OptimizationFlag::MiscIROpt;
|
||||
}
|
||||
if (!Settings::values.cpuopt_reduce_misalign_checks) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = false;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,8 +147,8 @@ struct System::Impl {
|
||||
|
||||
device_memory = std::make_unique<Core::DeviceMemory>(system);
|
||||
|
||||
is_multicore = Settings::values.use_multi_core;
|
||||
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation;
|
||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
|
||||
kernel.SetMulticore(is_multicore);
|
||||
cpu_manager.SetMulticore(is_multicore);
|
||||
@@ -162,7 +162,7 @@ struct System::Impl {
|
||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
Settings::values.custom_rtc_differential =
|
||||
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
||||
Settings::values.custom_rtc.GetValue().value_or(current_time) - current_time;
|
||||
|
||||
// Create a default fs if one doesn't already exist.
|
||||
if (virtual_filesystem == nullptr)
|
||||
|
||||
@@ -172,7 +172,7 @@ void CoreTiming::ClearPendingEvents() {
|
||||
}
|
||||
|
||||
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
basic_lock.lock();
|
||||
std::scoped_lock lock{basic_lock};
|
||||
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type.lock().get() == event_type.get();
|
||||
@@ -183,12 +183,10 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
event_queue.erase(itr, event_queue.end());
|
||||
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
basic_lock.unlock();
|
||||
}
|
||||
|
||||
std::optional<s64> CoreTiming::Advance() {
|
||||
std::scoped_lock advance_scope{advance_lock};
|
||||
std::scoped_lock basic_scope{basic_lock};
|
||||
std::scoped_lock lock{advance_lock, basic_lock};
|
||||
global_timer = GetGlobalTimeNs().count();
|
||||
|
||||
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "common/fiber.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Common {
|
||||
@@ -46,9 +49,9 @@ public:
|
||||
|
||||
void Pause(bool paused);
|
||||
|
||||
std::function<void(void*)> GetGuestThreadStartFunc();
|
||||
std::function<void(void*)> GetIdleThreadStartFunc();
|
||||
std::function<void(void*)> GetSuspendThreadStartFunc();
|
||||
static std::function<void(void*)> GetGuestThreadStartFunc();
|
||||
static std::function<void(void*)> GetIdleThreadStartFunc();
|
||||
static std::function<void(void*)> GetSuspendThreadStartFunc();
|
||||
void* GetStartFuncParamater();
|
||||
|
||||
void PreemptSingleCore(bool from_running_enviroment = true);
|
||||
@@ -97,7 +100,6 @@ private:
|
||||
bool is_async_gpu{};
|
||||
bool is_multicore{};
|
||||
std::atomic<std::size_t> current_core{};
|
||||
std::size_t preemption_count{};
|
||||
std::size_t idle_count{};
|
||||
static constexpr std::size_t max_cycle_runs = 5;
|
||||
|
||||
|
||||
@@ -695,8 +695,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
||||
}
|
||||
|
||||
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
if (s128_keys.find({id, field1, field2}) != s128_keys.end())
|
||||
if (s128_keys.find({id, field1, field2}) != s128_keys.end() || key == Key128{}) {
|
||||
return;
|
||||
}
|
||||
if (id == S128KeyType::Titlekey) {
|
||||
Key128 rights_id;
|
||||
std::memcpy(rights_id.data(), &field2, sizeof(u64));
|
||||
@@ -716,8 +717,9 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
if (iter2 != s128_file_id.end())
|
||||
if (iter2 != s128_file_id.end()) {
|
||||
WriteKeyToFile(category, iter2->first, key);
|
||||
}
|
||||
|
||||
// Variable cases
|
||||
if (id == S128KeyType::KeyArea) {
|
||||
@@ -745,16 +747,18 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
}
|
||||
|
||||
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
|
||||
if (s256_keys.find({id, field1, field2}) != s256_keys.end())
|
||||
if (s256_keys.find({id, field1, field2}) != s256_keys.end() || key == Key256{}) {
|
||||
return;
|
||||
}
|
||||
const auto iter = std::find_if(
|
||||
s256_file_id.begin(), s256_file_id.end(),
|
||||
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
|
||||
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
|
||||
std::tie(id, field1, field2);
|
||||
});
|
||||
if (iter != s256_file_id.end())
|
||||
if (iter != s256_file_id.end()) {
|
||||
WriteKeyToFile(KeyCategory::Standard, iter->first, key);
|
||||
}
|
||||
s256_keys[{id, field1, field2}] = key;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u64 NAND_USER_SIZE = 0x680000000; // 26624 MiB
|
||||
constexpr u64 NAND_SYSTEM_SIZE = 0xA0000000; // 2560 MiB
|
||||
constexpr u64 NAND_TOTAL_SIZE = 0x747C00000; // 29820 MiB
|
||||
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
|
||||
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
|
||||
dump_root(std::move(dump_root_)),
|
||||
@@ -110,30 +114,29 @@ VirtualDir BISFactory::GetImageDirectory() const {
|
||||
|
||||
u64 BISFactory::GetSystemNANDFreeSpace() const {
|
||||
const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
|
||||
if (sys_dir == nullptr)
|
||||
return 0;
|
||||
if (sys_dir == nullptr) {
|
||||
return GetSystemNANDTotalSpace();
|
||||
}
|
||||
|
||||
return GetSystemNANDTotalSpace() - sys_dir->GetSize();
|
||||
}
|
||||
|
||||
u64 BISFactory::GetSystemNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_system_size);
|
||||
return NAND_SYSTEM_SIZE;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetUserNANDFreeSpace() const {
|
||||
const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
|
||||
if (usr_dir == nullptr)
|
||||
return 0;
|
||||
|
||||
return GetUserNANDTotalSpace() - usr_dir->GetSize();
|
||||
// For some reason games such as BioShock 1 checks whether this is exactly 0x680000000 bytes.
|
||||
// Set the free space to be 1 MiB less than the total as a workaround to this issue.
|
||||
return GetUserNANDTotalSpace() - 0x100000;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetUserNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_user_size);
|
||||
return NAND_USER_SIZE;
|
||||
}
|
||||
|
||||
u64 BISFactory::GetFullNANDTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.nand_total_size);
|
||||
return NAND_TOTAL_SIZE;
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {
|
||||
|
||||
@@ -547,6 +547,56 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
|
||||
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
|
||||
}
|
||||
|
||||
bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
|
||||
const auto delete_nca = [this](const NcaID& id) {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
if (dir->GetFileRelative(path) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
|
||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
||||
|
||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
||||
|
||||
const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
// If an entry exists in the registered cache, remove it
|
||||
if (HasEntry(title_id, ContentRecordType::Meta)) {
|
||||
LOG_INFO(Loader,
|
||||
"Previously installed entry (v{}) for title_id={:016X} detected! "
|
||||
"Attempting to remove...",
|
||||
GetEntryVersion(title_id).value_or(0), title_id);
|
||||
// Get all the ncas associated with the current CNMT and delete them
|
||||
const auto meta_old_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
|
||||
const auto program_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
|
||||
const auto data_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
|
||||
const auto control_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
|
||||
const auto html_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
|
||||
const auto legal_id =
|
||||
GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
|
||||
|
||||
delete_nca(meta_old_id);
|
||||
delete_nca(program_id);
|
||||
delete_nca(data_id);
|
||||
delete_nca(control_id);
|
||||
delete_nca(html_id);
|
||||
delete_nca(legal_id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
|
||||
const VfsCopyFunction& copy) {
|
||||
const auto ncas = nsp.GetNCAsCollapsed();
|
||||
@@ -560,31 +610,57 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
|
||||
// Install Metadata File
|
||||
const auto meta_id_raw = (*meta_iter)->GetName().substr(0, 32);
|
||||
const auto meta_id = Common::HexStringToArray<16>(meta_id_raw);
|
||||
|
||||
const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id);
|
||||
if (res != InstallResult::Success)
|
||||
return res;
|
||||
if ((*meta_iter)->GetSubdirectories().empty()) {
|
||||
LOG_ERROR(Loader,
|
||||
"The file you are attempting to install does not contain a section0 within the "
|
||||
"metadata NCA and is therefore malformed. Verify that the file is valid.");
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
|
||||
// Install all the other NCAs
|
||||
const auto section0 = (*meta_iter)->GetSubdirectories()[0];
|
||||
|
||||
if (section0->GetFiles().empty()) {
|
||||
LOG_ERROR(Loader,
|
||||
"The file you are attempting to install does not contain a CNMT within the "
|
||||
"metadata NCA and is therefore malformed. Verify that the file is valid.");
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
|
||||
const auto cnmt_file = section0->GetFiles()[0];
|
||||
const CNMT cnmt(cnmt_file);
|
||||
|
||||
const auto title_id = cnmt.GetTitleID();
|
||||
const auto result = RemoveExistingEntry(title_id);
|
||||
|
||||
// Install Metadata File
|
||||
const auto res = RawInstallNCA(**meta_iter, copy, overwrite_if_exists, meta_id);
|
||||
if (res != InstallResult::Success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Install all the other NCAs
|
||||
for (const auto& record : cnmt.GetContentRecords()) {
|
||||
// Ignore DeltaFragments, they are not useful to us
|
||||
if (record.type == ContentRecordType::DeltaFragment)
|
||||
if (record.type == ContentRecordType::DeltaFragment) {
|
||||
continue;
|
||||
}
|
||||
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
|
||||
if (nca == nullptr)
|
||||
if (nca == nullptr) {
|
||||
return InstallResult::ErrorCopyFailed;
|
||||
}
|
||||
const auto res2 = RawInstallNCA(*nca, copy, overwrite_if_exists, record.nca_id);
|
||||
if (res2 != InstallResult::Success)
|
||||
if (res2 != InstallResult::Success) {
|
||||
return res2;
|
||||
}
|
||||
}
|
||||
|
||||
Refresh();
|
||||
if (result) {
|
||||
return InstallResult::OverwriteExisting;
|
||||
}
|
||||
return InstallResult::Success;
|
||||
}
|
||||
|
||||
@@ -610,8 +686,9 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
|
||||
mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
|
||||
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
|
||||
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
|
||||
if (!RawInstallYuzuMeta(new_cnmt))
|
||||
if (!RawInstallYuzuMeta(new_cnmt)) {
|
||||
return InstallResult::ErrorMetaFailed;
|
||||
}
|
||||
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
|
||||
}
|
||||
|
||||
@@ -649,8 +726,9 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
|
||||
}
|
||||
|
||||
auto out = dir->CreateFileRelative(path);
|
||||
if (out == nullptr)
|
||||
if (out == nullptr) {
|
||||
return InstallResult::ErrorCopyFailed;
|
||||
}
|
||||
return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
|
||||
: InstallResult::ErrorCopyFailed;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile
|
||||
|
||||
enum class InstallResult {
|
||||
Success,
|
||||
OverwriteExisting,
|
||||
ErrorAlreadyExists,
|
||||
ErrorCopyFailed,
|
||||
ErrorMetaFailed,
|
||||
@@ -154,6 +155,9 @@ public:
|
||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||
std::optional<u64> title_id = {}) const override;
|
||||
|
||||
// Removes an existing entry based on title id
|
||||
bool RemoveExistingEntry(u64 title_id);
|
||||
|
||||
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
|
||||
// there is a meta NCA and all of them are accessible.
|
||||
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB
|
||||
|
||||
SDMCFactory::SDMCFactory(VirtualDir dir_)
|
||||
: dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
|
||||
@@ -46,7 +48,7 @@ u64 SDMCFactory::GetSDMCFreeSpace() const {
|
||||
}
|
||||
|
||||
u64 SDMCFactory::GetSDMCTotalSpace() const {
|
||||
return static_cast<u64>(Settings::values.sdmc_size);
|
||||
return SDMC_TOTAL_SIZE;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -112,19 +112,26 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
|
||||
const auto new_path =
|
||||
FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path))
|
||||
return nullptr;
|
||||
|
||||
if (cache.find(old_path) != cache.end()) {
|
||||
auto cached = cache[old_path];
|
||||
if (!cached.expired()) {
|
||||
auto file = cached.lock();
|
||||
file->Open(new_path, "r+b");
|
||||
cache.erase(old_path);
|
||||
cache[new_path] = file;
|
||||
auto file = cache[old_path].lock();
|
||||
|
||||
if (!cache[old_path].expired()) {
|
||||
file->Close();
|
||||
}
|
||||
|
||||
if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) ||
|
||||
FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cache.erase(old_path);
|
||||
file->Open(new_path, "r+b");
|
||||
cache[new_path] = file;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return OpenFile(new_path, Mode::ReadWrite);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
|
||||
|
||||
const float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
const float emulation_aspect_ratio = EmulationAspectRatio(
|
||||
static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio);
|
||||
static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
|
||||
|
||||
const Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -22,7 +24,7 @@ constexpr u16 GetGeneration(Handle handle) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
HandleTable::HandleTable() {
|
||||
HandleTable::HandleTable(KernelCore& kernel) : kernel{kernel} {
|
||||
Clear();
|
||||
}
|
||||
|
||||
@@ -103,9 +105,9 @@ bool HandleTable::IsValid(Handle handle) const {
|
||||
|
||||
std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
|
||||
if (handle == CurrentThread) {
|
||||
return SharedFrom(GetCurrentThread());
|
||||
return SharedFrom(kernel.CurrentScheduler().GetCurrentThread());
|
||||
} else if (handle == CurrentProcess) {
|
||||
return SharedFrom(Core::System::GetInstance().CurrentProcess());
|
||||
return SharedFrom(kernel.CurrentProcess());
|
||||
}
|
||||
|
||||
if (!IsValid(handle)) {
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
enum KernelHandle : Handle {
|
||||
InvalidHandle = 0,
|
||||
CurrentThread = 0xFFFF8000,
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
/// This is the maximum limit of handles allowed per process in Horizon
|
||||
static constexpr std::size_t MAX_COUNT = 1024;
|
||||
|
||||
HandleTable();
|
||||
explicit HandleTable(KernelCore& kernel);
|
||||
~HandleTable();
|
||||
|
||||
/**
|
||||
@@ -134,6 +136,9 @@ private:
|
||||
|
||||
/// Head of the free slots linked list.
|
||||
u16 next_free_slot = 0;
|
||||
|
||||
/// Underlying kernel instance that this handle table operates under.
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -50,7 +50,8 @@ namespace Kernel {
|
||||
|
||||
struct KernelCore::Impl {
|
||||
explicit Impl(Core::System& system, KernelCore& kernel)
|
||||
: global_scheduler{kernel}, synchronization{system}, time_manager{system}, system{system} {}
|
||||
: global_scheduler{kernel}, synchronization{system}, time_manager{system},
|
||||
global_handle_table{kernel}, system{system} {}
|
||||
|
||||
void SetMulticore(bool is_multicore) {
|
||||
this->is_multicore = is_multicore;
|
||||
@@ -160,13 +161,14 @@ struct KernelCore::Impl {
|
||||
void InitializeSuspendThreads() {
|
||||
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
std::string name = "Suspend Thread Id:" + std::to_string(i);
|
||||
std::function<void(void*)> init_func =
|
||||
system.GetCpuManager().GetSuspendThreadStartFunc();
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
ThreadType type =
|
||||
const auto type =
|
||||
static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
|
||||
auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast<u32>(i), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
auto thread_res =
|
||||
Thread::Create(system, type, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
|
||||
suspend_threads[i] = std::move(thread_res).Unwrap();
|
||||
}
|
||||
}
|
||||
@@ -307,7 +309,7 @@ struct KernelCore::Impl {
|
||||
|
||||
// This is the kernel's handle table or supervisor handle table which
|
||||
// stores all the objects in place.
|
||||
Kernel::HandleTable global_handle_table;
|
||||
HandleTable global_handle_table;
|
||||
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using
|
||||
/// the ConnectToPort SVC.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/memory/memory_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
@@ -29,40 +29,39 @@ enum : u64 {
|
||||
|
||||
// clang-format off
|
||||
constexpr std::array<AddressSpaceInfo, 13> AddressSpaceInfos{{
|
||||
{ 32 /*bit_width*/, Size_2_MB /*addr*/, Size_1_GB - Size_2_MB /*size*/, AddressSpaceInfo::Type::Is32Bit, },
|
||||
{ 32 /*bit_width*/, Size_1_GB /*addr*/, Size_4_GB - Size_1_GB /*size*/, AddressSpaceInfo::Type::Small64Bit, },
|
||||
{ 32 /*bit_width*/, Invalid /*addr*/, Size_1_GB /*size*/, AddressSpaceInfo::Type::Heap, },
|
||||
{ 32 /*bit_width*/, Invalid /*addr*/, Size_1_GB /*size*/, AddressSpaceInfo::Type::Alias, },
|
||||
{ 36 /*bit_width*/, Size_128_MB /*addr*/, Size_2_GB - Size_128_MB /*size*/, AddressSpaceInfo::Type::Is32Bit, },
|
||||
{ 36 /*bit_width*/, Size_2_GB /*addr*/, Size_64_GB - Size_2_GB /*size*/, AddressSpaceInfo::Type::Small64Bit, },
|
||||
{ 36 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Heap, },
|
||||
{ 36 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Alias, },
|
||||
{ 39 /*bit_width*/, Size_128_MB /*addr*/, Size_512_GB - Size_128_MB /*size*/, AddressSpaceInfo::Type::Large64Bit, },
|
||||
{ 39 /*bit_width*/, Invalid /*addr*/, Size_64_GB /*size*/, AddressSpaceInfo::Type::Is32Bit },
|
||||
{ 39 /*bit_width*/, Invalid /*addr*/, Size_6_GB /*size*/, AddressSpaceInfo::Type::Heap, },
|
||||
{ 39 /*bit_width*/, Invalid /*addr*/, Size_64_GB /*size*/, AddressSpaceInfo::Type::Alias, },
|
||||
{ 39 /*bit_width*/, Invalid /*addr*/, Size_2_GB /*size*/, AddressSpaceInfo::Type::Stack, },
|
||||
{ .bit_width = 32, .address = Size_2_MB , .size = Size_1_GB - Size_2_MB , .type = AddressSpaceInfo::Type::Is32Bit, },
|
||||
{ .bit_width = 32, .address = Size_1_GB , .size = Size_4_GB - Size_1_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
|
||||
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 32, .address = Invalid , .size = Size_1_GB , .type = AddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 36, .address = Size_128_MB, .size = Size_2_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Is32Bit, },
|
||||
{ .bit_width = 36, .address = Size_2_GB , .size = Size_64_GB - Size_2_GB , .type = AddressSpaceInfo::Type::Small64Bit, },
|
||||
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 36, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 39, .address = Size_128_MB, .size = Size_512_GB - Size_128_MB, .type = AddressSpaceInfo::Type::Large64Bit, },
|
||||
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Is32Bit },
|
||||
{ .bit_width = 39, .address = Invalid , .size = Size_6_GB , .type = AddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 39, .address = Invalid , .size = Size_64_GB , .type = AddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 39, .address = Invalid , .size = Size_2_GB , .type = AddressSpaceInfo::Type::Stack, },
|
||||
}};
|
||||
// clang-format on
|
||||
|
||||
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
|
||||
return index < std::size(AddressSpaceInfos) && AddressSpaceInfos[index].GetAddress() != Invalid;
|
||||
return index < AddressSpaceInfos.size() && AddressSpaceInfos[index].address != Invalid;
|
||||
}
|
||||
|
||||
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
|
||||
AddressSpaceIndices32Bit{
|
||||
0, 1, 0, 2, 0, 3,
|
||||
};
|
||||
using IndexArray = std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>;
|
||||
|
||||
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
|
||||
AddressSpaceIndices36Bit{
|
||||
4, 5, 4, 6, 4, 7,
|
||||
};
|
||||
constexpr IndexArray AddressSpaceIndices32Bit{
|
||||
0, 1, 0, 2, 0, 3,
|
||||
};
|
||||
|
||||
constexpr std::array<std::size_t, static_cast<std::size_t>(AddressSpaceInfo::Type::Count)>
|
||||
AddressSpaceIndices39Bit{
|
||||
9, 8, 8, 10, 12, 11,
|
||||
};
|
||||
constexpr IndexArray AddressSpaceIndices36Bit{
|
||||
4, 5, 4, 6, 4, 7,
|
||||
};
|
||||
|
||||
constexpr IndexArray AddressSpaceIndices39Bit{
|
||||
9, 8, 8, 10, 12, 11,
|
||||
};
|
||||
|
||||
constexpr bool IsAllowed32BitType(AddressSpaceInfo::Type type) {
|
||||
return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit &&
|
||||
@@ -80,37 +79,37 @@ constexpr bool IsAllowed39BitType(AddressSpaceInfo::Type type) {
|
||||
|
||||
} // namespace
|
||||
|
||||
u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, AddressSpaceInfo::Type type) {
|
||||
u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
|
||||
const std::size_t index{static_cast<std::size_t>(type)};
|
||||
switch (width) {
|
||||
case 32:
|
||||
ASSERT(IsAllowed32BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetAddress();
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].address;
|
||||
case 36:
|
||||
ASSERT(IsAllowed36BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetAddress();
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].address;
|
||||
case 39:
|
||||
ASSERT(IsAllowed39BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetAddress();
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, AddressSpaceInfo::Type type) {
|
||||
std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
|
||||
const std::size_t index{static_cast<std::size_t>(type)};
|
||||
switch (width) {
|
||||
case 32:
|
||||
ASSERT(IsAllowed32BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetSize();
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].size;
|
||||
case 36:
|
||||
ASSERT(IsAllowed36BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetSize();
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].size;
|
||||
case 39:
|
||||
ASSERT(IsAllowed39BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetSize();
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
namespace Kernel::Memory {
|
||||
|
||||
class AddressSpaceInfo final : NonCopyable {
|
||||
public:
|
||||
struct AddressSpaceInfo final {
|
||||
enum class Type : u32 {
|
||||
Is32Bit = 0,
|
||||
Small64Bit = 1,
|
||||
@@ -23,31 +22,13 @@ public:
|
||||
Count,
|
||||
};
|
||||
|
||||
private:
|
||||
std::size_t bit_width{};
|
||||
std::size_t addr{};
|
||||
std::size_t size{};
|
||||
Type type{};
|
||||
|
||||
public:
|
||||
static u64 GetAddressSpaceStart(std::size_t width, Type type);
|
||||
static std::size_t GetAddressSpaceSize(std::size_t width, Type type);
|
||||
|
||||
constexpr AddressSpaceInfo(std::size_t bit_width, std::size_t addr, std::size_t size, Type type)
|
||||
: bit_width{bit_width}, addr{addr}, size{size}, type{type} {}
|
||||
|
||||
constexpr std::size_t GetWidth() const {
|
||||
return bit_width;
|
||||
}
|
||||
constexpr std::size_t GetAddress() const {
|
||||
return addr;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return size;
|
||||
}
|
||||
constexpr Type GetType() const {
|
||||
return type;
|
||||
}
|
||||
const std::size_t bit_width{};
|
||||
const std::size_t address{};
|
||||
const std::size_t size{};
|
||||
const Type type{};
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
|
||||
@@ -66,8 +66,6 @@ private:
|
||||
const MemoryRegion application;
|
||||
const MemoryRegion applet;
|
||||
const MemoryRegion system;
|
||||
|
||||
const PAddr start_address{};
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
|
||||
@@ -123,7 +123,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
|
||||
: kernel.CreateNewUserProcessID();
|
||||
process->capabilities.InitializeForMetadatalessProcess();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
|
||||
[&] { return distribution(rng); });
|
||||
@@ -408,7 +408,7 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) {
|
||||
Process::Process(Core::System& system)
|
||||
: SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>(
|
||||
system)},
|
||||
address_arbiter{system}, mutex{system}, system{system} {}
|
||||
handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
|
||||
|
||||
Process::~Process() = default;
|
||||
|
||||
|
||||
@@ -382,12 +382,6 @@ private:
|
||||
/// List of threads waiting for a condition variable
|
||||
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads;
|
||||
|
||||
/// System context
|
||||
Core::System& system;
|
||||
|
||||
/// Name of this process
|
||||
std::string name;
|
||||
|
||||
/// Address of the top of the main thread's stack
|
||||
VAddr main_thread_stack_top{};
|
||||
|
||||
@@ -399,6 +393,12 @@ private:
|
||||
|
||||
/// Process total image size
|
||||
std::size_t image_size{};
|
||||
|
||||
/// Name of this process
|
||||
std::string name;
|
||||
|
||||
/// System context
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// licensed under GPLv2 or later under exception provided by the author.
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
@@ -31,22 +32,20 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
|
||||
GlobalScheduler::~GlobalScheduler() = default;
|
||||
|
||||
void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
|
||||
global_list_guard.lock();
|
||||
std::scoped_lock lock{global_list_guard};
|
||||
thread_list.push_back(std::move(thread));
|
||||
global_list_guard.unlock();
|
||||
}
|
||||
|
||||
void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
global_list_guard.lock();
|
||||
std::scoped_lock lock{global_list_guard};
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
global_list_guard.unlock();
|
||||
}
|
||||
|
||||
u32 GlobalScheduler::SelectThreads() {
|
||||
ASSERT(is_locked);
|
||||
const auto update_thread = [](Thread* thread, Scheduler& sched) {
|
||||
sched.guard.lock();
|
||||
std::scoped_lock lock{sched.guard};
|
||||
if (thread != sched.selected_thread_set.get()) {
|
||||
if (thread == nullptr) {
|
||||
++sched.idle_selection_count;
|
||||
@@ -57,7 +56,6 @@ u32 GlobalScheduler::SelectThreads() {
|
||||
sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
|
||||
sched.is_context_switch_pending = reschedule_pending;
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
sched.guard.unlock();
|
||||
return reschedule_pending;
|
||||
};
|
||||
if (!is_reselection_pending.load()) {
|
||||
@@ -757,11 +755,12 @@ void Scheduler::OnSwitch(void* this_scheduler) {
|
||||
|
||||
void Scheduler::SwitchToCurrent() {
|
||||
while (true) {
|
||||
guard.lock();
|
||||
selected_thread = selected_thread_set;
|
||||
current_thread = selected_thread;
|
||||
is_context_switch_pending = false;
|
||||
guard.unlock();
|
||||
{
|
||||
std::scoped_lock lock{guard};
|
||||
selected_thread = selected_thread_set;
|
||||
current_thread = selected_thread;
|
||||
is_context_switch_pending = false;
|
||||
}
|
||||
while (!is_context_switch_pending) {
|
||||
if (current_thread != nullptr && !current_thread->IsHLEThread()) {
|
||||
current_thread->context_guard.lock();
|
||||
@@ -803,7 +802,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
|
||||
void Scheduler::Initialize() {
|
||||
std::string name = "Idle Thread Id:" + std::to_string(core_id);
|
||||
std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc();
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
|
||||
auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
|
||||
|
||||
@@ -13,16 +13,8 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
#include "core/arm/cpu_interrupt_handler.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
@@ -36,6 +28,11 @@
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
bool Thread::ShouldWait(const Thread* thread) const {
|
||||
@@ -158,7 +155,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
|
||||
std::string name, VAddr entry_point, u32 priority,
|
||||
u64 arg, s32 processor_id, VAddr stack_top,
|
||||
Process* owner_process) {
|
||||
std::function<void(void*)> init_func = system.GetCpuManager().GetGuestThreadStartFunc();
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
|
||||
owner_process, std::move(init_func), init_func_parameter);
|
||||
@@ -540,13 +537,4 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
*/
|
||||
Thread* GetCurrentThread() {
|
||||
return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -680,9 +680,4 @@ private:
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
*/
|
||||
Thread* GetCurrentThread();
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -271,7 +272,7 @@ ISelfController::ISelfController(Core::System& system,
|
||||
{41, nullptr, "IsSystemBufferSharingEnabled"},
|
||||
{42, nullptr, "GetSystemSharedLayerHandle"},
|
||||
{43, nullptr, "GetSystemSharedBufferHandle"},
|
||||
{44, nullptr, "CreateManagedDisplaySeparableLayer"},
|
||||
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
|
||||
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
|
||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||
{51, nullptr, "ApproveToDisplay"},
|
||||
@@ -461,6 +462,24 @@ void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx)
|
||||
rb.Push(*layer_id);
|
||||
}
|
||||
|
||||
void ISelfController::CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
// This calls nn::vi::CreateRecordingLayer() which creates another layer.
|
||||
// Currently we do not support more than 1 layer per display, output 1 layer id for now.
|
||||
// Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
|
||||
// side effects.
|
||||
// TODO: Support multiple layers
|
||||
const auto display_id = nvflinger->OpenDisplay("Default");
|
||||
const auto layer_id = nvflinger->CreateLayer(*display_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(*layer_id);
|
||||
}
|
||||
|
||||
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
@@ -730,14 +749,14 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1353,14 +1372,25 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::array<u8, 0x10> version_string{};
|
||||
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
const auto res = pm.GetControlMetadata();
|
||||
const auto res = [this] {
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
|
||||
FileSys::PatchManager pm{title_id};
|
||||
auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
return res;
|
||||
}
|
||||
|
||||
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
|
||||
if (res.first != nullptr) {
|
||||
const auto& version = res.first->GetVersionString();
|
||||
std::copy(version.begin(), version.end(), version_string.begin());
|
||||
} else {
|
||||
constexpr u128 default_version = {1, 0};
|
||||
std::memcpy(version_string.data(), default_version.data(), sizeof(u128));
|
||||
constexpr char default_version[]{"1.0.0"};
|
||||
std::memcpy(version_string.data(), default_version, sizeof(default_version));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
@@ -1377,7 +1407,19 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
|
||||
u32 supported_languages = 0;
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
const auto res = [this] {
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
|
||||
FileSys::PatchManager pm{title_id};
|
||||
auto res = pm.GetControlMetadata();
|
||||
if (res.first != nullptr) {
|
||||
return res;
|
||||
}
|
||||
|
||||
FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ private:
|
||||
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetAlbumImageOrientation(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplaySeparableLayer(Kernel::HLERequestContext& ctx);
|
||||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
|
||||
484
src/core/hle/service/mii/manager.cpp
Normal file
484
src/core/hle/service/mii/manager.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/mii/manager.h"
|
||||
#include "core/hle/service/mii/raw_data.h"
|
||||
#include "core/hle/service/mii/types.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||
|
||||
constexpr std::size_t DefaultMiiCount{sizeof(RawData::DefaultMii) / sizeof(DefaultMii)};
|
||||
|
||||
constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
|
||||
constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
|
||||
constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13};
|
||||
constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23};
|
||||
constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0};
|
||||
constexpr std::array<u8, 62> EyeRotateLookup{
|
||||
{0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
|
||||
0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
|
||||
0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}};
|
||||
constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07,
|
||||
0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06,
|
||||
0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}};
|
||||
|
||||
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
|
||||
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
|
||||
std::array<T, DestArraySize> out{};
|
||||
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
|
||||
return out;
|
||||
}
|
||||
|
||||
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
|
||||
MiiStoreBitFields bf;
|
||||
std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
|
||||
|
||||
return {
|
||||
.uuid = data.data.uuid,
|
||||
.name = ResizeArray<char16_t, 10, 11>(data.data.name),
|
||||
.font_region = static_cast<u8>(bf.font_region.Value()),
|
||||
.favorite_color = static_cast<u8>(bf.favorite_color.Value()),
|
||||
.gender = static_cast<u8>(bf.gender.Value()),
|
||||
.height = static_cast<u8>(bf.height.Value()),
|
||||
.build = static_cast<u8>(bf.build.Value()),
|
||||
.type = static_cast<u8>(bf.type.Value()),
|
||||
.region_move = static_cast<u8>(bf.region_move.Value()),
|
||||
.faceline_type = static_cast<u8>(bf.faceline_type.Value()),
|
||||
.faceline_color = static_cast<u8>(bf.faceline_color.Value()),
|
||||
.faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()),
|
||||
.faceline_make = static_cast<u8>(bf.faceline_makeup.Value()),
|
||||
.hair_type = static_cast<u8>(bf.hair_type.Value()),
|
||||
.hair_color = static_cast<u8>(bf.hair_color.Value()),
|
||||
.hair_flip = static_cast<u8>(bf.hair_flip.Value()),
|
||||
.eye_type = static_cast<u8>(bf.eye_type.Value()),
|
||||
.eye_color = static_cast<u8>(bf.eye_color.Value()),
|
||||
.eye_scale = static_cast<u8>(bf.eye_scale.Value()),
|
||||
.eye_aspect = static_cast<u8>(bf.eye_aspect.Value()),
|
||||
.eye_rotate = static_cast<u8>(bf.eye_rotate.Value()),
|
||||
.eye_x = static_cast<u8>(bf.eye_x.Value()),
|
||||
.eye_y = static_cast<u8>(bf.eye_y.Value()),
|
||||
.eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()),
|
||||
.eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()),
|
||||
.eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()),
|
||||
.eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()),
|
||||
.eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()),
|
||||
.eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()),
|
||||
.eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3),
|
||||
.nose_type = static_cast<u8>(bf.nose_type.Value()),
|
||||
.nose_scale = static_cast<u8>(bf.nose_scale.Value()),
|
||||
.nose_y = static_cast<u8>(bf.nose_y.Value()),
|
||||
.mouth_type = static_cast<u8>(bf.mouth_type.Value()),
|
||||
.mouth_color = static_cast<u8>(bf.mouth_color.Value()),
|
||||
.mouth_scale = static_cast<u8>(bf.mouth_scale.Value()),
|
||||
.mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()),
|
||||
.mouth_y = static_cast<u8>(bf.mouth_y.Value()),
|
||||
.beard_color = static_cast<u8>(bf.beard_color.Value()),
|
||||
.beard_type = static_cast<u8>(bf.beard_type.Value()),
|
||||
.mustache_type = static_cast<u8>(bf.mustache_type.Value()),
|
||||
.mustache_scale = static_cast<u8>(bf.mustache_scale.Value()),
|
||||
.mustache_y = static_cast<u8>(bf.mustache_y.Value()),
|
||||
.glasses_type = static_cast<u8>(bf.glasses_type.Value()),
|
||||
.glasses_color = static_cast<u8>(bf.glasses_color.Value()),
|
||||
.glasses_scale = static_cast<u8>(bf.glasses_scale.Value()),
|
||||
.glasses_y = static_cast<u8>(bf.glasses_y.Value()),
|
||||
.mole_type = static_cast<u8>(bf.mole_type.Value()),
|
||||
.mole_scale = static_cast<u8>(bf.mole_scale.Value()),
|
||||
.mole_x = static_cast<u8>(bf.mole_x.Value()),
|
||||
.mole_y = static_cast<u8>(bf.mole_y.Value()),
|
||||
};
|
||||
}
|
||||
|
||||
u16 GenerateCrc16(const void* data, std::size_t size) {
|
||||
s32 crc{};
|
||||
for (int i = 0; i < size; i++) {
|
||||
crc ^= reinterpret_cast<const u8*>(data)[i] << 8;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc <<= 1;
|
||||
if ((crc & 0x10000) != 0) {
|
||||
crc = (crc ^ 0x1021) & 0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Common::swap16(static_cast<u16>(crc));
|
||||
}
|
||||
|
||||
Common::UUID GenerateValidUUID() {
|
||||
auto uuid{Common::UUID::Generate()};
|
||||
|
||||
// Bit 7 must be set, and bit 6 unset for the UUID to be valid
|
||||
uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL;
|
||||
uuid.uuid[1] |= 0x0000000000000080ULL;
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetRandomValue(T min, T max) {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(0, static_cast<u64>(max));
|
||||
return static_cast<T>(distribution(gen));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetRandomValue(T max) {
|
||||
return GetRandomValue<T>({}, max);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetArrayValue(const u8* data, std::size_t index) {
|
||||
T result{};
|
||||
std::memcpy(&result, &data[index * sizeof(T)], sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
if (gender == Gender::All) {
|
||||
gender = GetRandomValue<Gender>(Gender::Maximum);
|
||||
}
|
||||
|
||||
bf.gender.Assign(gender);
|
||||
bf.favorite_color.Assign(GetRandomValue<u8>(11));
|
||||
bf.region_move.Assign(0);
|
||||
bf.font_region.Assign(FontRegion::Standard);
|
||||
bf.type.Assign(0);
|
||||
bf.height.Assign(64);
|
||||
bf.build.Assign(64);
|
||||
|
||||
if (age == Age::All) {
|
||||
const auto temp{GetRandomValue<int>(10)};
|
||||
if (temp >= 8) {
|
||||
age = Age::Old;
|
||||
} else if (temp >= 4) {
|
||||
age = Age::Normal;
|
||||
} else {
|
||||
age = Age::Young;
|
||||
}
|
||||
}
|
||||
|
||||
if (race == Race::All) {
|
||||
const auto temp{GetRandomValue<int>(10)};
|
||||
if (temp >= 8) {
|
||||
race = Race::Black;
|
||||
} else if (temp >= 4) {
|
||||
race = Race::White;
|
||||
} else {
|
||||
race = Race::Asian;
|
||||
}
|
||||
}
|
||||
|
||||
u32 axis_y{};
|
||||
if (gender == Gender::Female && age == Age::Young) {
|
||||
axis_y = GetRandomValue<u32>(3);
|
||||
}
|
||||
|
||||
const std::size_t index{3 * static_cast<std::size_t>(age) +
|
||||
9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
|
||||
|
||||
const auto faceline_type_info{
|
||||
GetArrayValue<RandomMiiData4>(&RawData::RandomMiiFaceline[0], index)};
|
||||
const auto faceline_color_info{GetArrayValue<RandomMiiData3>(
|
||||
RawData::RandomMiiFacelineColor.data(),
|
||||
3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
|
||||
const auto faceline_wrinkle_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineWrinkle.data(), index)};
|
||||
const auto faceline_makeup_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineMakeup.data(), index)};
|
||||
const auto hair_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiHairType.data(), index)};
|
||||
const auto hair_color_info{GetArrayValue<RandomMiiData3>(RawData::RandomMiiHairColor.data(),
|
||||
3 * static_cast<std::size_t>(race) +
|
||||
static_cast<std::size_t>(age))};
|
||||
const auto eye_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyeType.data(), index)};
|
||||
const auto eye_color_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiEyeColor.data(),
|
||||
static_cast<std::size_t>(race))};
|
||||
const auto eyebrow_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyebrowType.data(), index)};
|
||||
const auto nose_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiNoseType.data(), index)};
|
||||
const auto mouth_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiMouthType.data(), index)};
|
||||
const auto glasses_type_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiGlassType.data(),
|
||||
static_cast<std::size_t>(age))};
|
||||
|
||||
bf.faceline_type.Assign(
|
||||
faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
|
||||
bf.faceline_color.Assign(
|
||||
faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
|
||||
bf.faceline_wrinkle.Assign(
|
||||
faceline_wrinkle_info
|
||||
.values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
|
||||
bf.faceline_makeup.Assign(
|
||||
faceline_makeup_info
|
||||
.values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
|
||||
|
||||
bf.hair_type.Assign(
|
||||
hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]);
|
||||
bf.hair_color.Assign(
|
||||
HairColorLookup[hair_color_info
|
||||
.values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]);
|
||||
bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum));
|
||||
|
||||
bf.eye_type.Assign(
|
||||
eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]);
|
||||
|
||||
const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
|
||||
const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
|
||||
const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
|
||||
const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]};
|
||||
|
||||
bf.eye_color.Assign(
|
||||
EyeColorLookup[eye_color_info
|
||||
.values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]);
|
||||
bf.eye_scale.Assign(4);
|
||||
bf.eye_aspect.Assign(3);
|
||||
bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate);
|
||||
bf.eye_x.Assign(2);
|
||||
bf.eye_y.Assign(axis_y + 12);
|
||||
|
||||
bf.eyebrow_type.Assign(
|
||||
eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
|
||||
|
||||
const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
|
||||
const auto eyebrow_y{race == Race::Asian ? 9 : 10};
|
||||
const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6};
|
||||
const auto eyebrow_rotate{
|
||||
32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]};
|
||||
|
||||
bf.eyebrow_color.Assign(bf.hair_color);
|
||||
bf.eyebrow_scale.Assign(4);
|
||||
bf.eyebrow_aspect.Assign(3);
|
||||
bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate);
|
||||
bf.eyebrow_x.Assign(2);
|
||||
bf.eyebrow_y.Assign(axis_y + eyebrow_y);
|
||||
|
||||
const auto nose_scale{gender == Gender::Female ? 3 : 4};
|
||||
|
||||
bf.nose_type.Assign(
|
||||
nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]);
|
||||
bf.nose_scale.Assign(nose_scale);
|
||||
bf.nose_y.Assign(axis_y + 9);
|
||||
|
||||
const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0};
|
||||
|
||||
bf.mouth_type.Assign(
|
||||
mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
|
||||
bf.mouth_color.Assign(MouthColorLookup[mouth_color]);
|
||||
bf.mouth_scale.Assign(4);
|
||||
bf.mouth_aspect.Assign(3);
|
||||
bf.mouth_y.Assign(axis_y + 13);
|
||||
|
||||
bf.beard_color.Assign(bf.hair_color);
|
||||
bf.mustache_scale.Assign(4);
|
||||
|
||||
if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) {
|
||||
const auto mustache_and_beard_flag{
|
||||
GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)};
|
||||
|
||||
auto beard_type{BeardType::None};
|
||||
auto mustache_type{MustacheType::None};
|
||||
|
||||
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
|
||||
BeardAndMustacheFlag::Beard) {
|
||||
beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5);
|
||||
}
|
||||
|
||||
if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
|
||||
BeardAndMustacheFlag::Mustache) {
|
||||
mustache_type =
|
||||
GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5);
|
||||
}
|
||||
|
||||
bf.mustache_type.Assign(mustache_type);
|
||||
bf.beard_type.Assign(beard_type);
|
||||
bf.mustache_y.Assign(10);
|
||||
} else {
|
||||
bf.mustache_type.Assign(MustacheType::None);
|
||||
bf.beard_type.Assign(BeardType::None);
|
||||
bf.mustache_y.Assign(axis_y + 10);
|
||||
}
|
||||
|
||||
const auto glasses_type_start{GetRandomValue<std::size_t>(100)};
|
||||
u8 glasses_type{};
|
||||
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
|
||||
if (++glasses_type >= glasses_type_info.values_count) {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bf.glasses_type.Assign(glasses_type);
|
||||
bf.glasses_color.Assign(GlassesColorLookup[0]);
|
||||
bf.glasses_scale.Assign(4);
|
||||
bf.glasses_y.Assign(axis_y + 10);
|
||||
|
||||
bf.mole_type.Assign(0);
|
||||
bf.mole_scale.Assign(4);
|
||||
bf.mole_x.Assign(2);
|
||||
bf.mole_y.Assign(20);
|
||||
|
||||
return {DefaultMiiName, bf, user_id};
|
||||
}
|
||||
|
||||
MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
bf.font_region.Assign(info.font_region);
|
||||
bf.favorite_color.Assign(info.favorite_color);
|
||||
bf.gender.Assign(info.gender);
|
||||
bf.height.Assign(info.height);
|
||||
bf.build.Assign(info.weight);
|
||||
bf.type.Assign(info.type);
|
||||
bf.region_move.Assign(info.region);
|
||||
bf.faceline_type.Assign(info.face_type);
|
||||
bf.faceline_color.Assign(info.face_color);
|
||||
bf.faceline_wrinkle.Assign(info.face_wrinkle);
|
||||
bf.faceline_makeup.Assign(info.face_makeup);
|
||||
bf.hair_type.Assign(info.hair_type);
|
||||
bf.hair_color.Assign(HairColorLookup[info.hair_color]);
|
||||
bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip));
|
||||
bf.eye_type.Assign(info.eye_type);
|
||||
bf.eye_color.Assign(EyeColorLookup[info.eye_color]);
|
||||
bf.eye_scale.Assign(info.eye_scale);
|
||||
bf.eye_aspect.Assign(info.eye_aspect);
|
||||
bf.eye_rotate.Assign(info.eye_rotate);
|
||||
bf.eye_x.Assign(info.eye_x);
|
||||
bf.eye_y.Assign(info.eye_y);
|
||||
bf.eyebrow_type.Assign(info.eyebrow_type);
|
||||
bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]);
|
||||
bf.eyebrow_scale.Assign(info.eyebrow_scale);
|
||||
bf.eyebrow_aspect.Assign(info.eyebrow_aspect);
|
||||
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
|
||||
bf.eyebrow_x.Assign(info.eyebrow_x);
|
||||
bf.eyebrow_y.Assign(info.eyebrow_y - 3);
|
||||
bf.nose_type.Assign(info.nose_type);
|
||||
bf.nose_scale.Assign(info.nose_scale);
|
||||
bf.nose_y.Assign(info.nose_y);
|
||||
bf.mouth_type.Assign(info.mouth_type);
|
||||
bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]);
|
||||
bf.mouth_scale.Assign(info.mouth_scale);
|
||||
bf.mouth_aspect.Assign(info.mouth_aspect);
|
||||
bf.mouth_y.Assign(info.mouth_y);
|
||||
bf.beard_color.Assign(HairColorLookup[info.beard_color]);
|
||||
bf.beard_type.Assign(static_cast<BeardType>(info.beard_type));
|
||||
bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type));
|
||||
bf.mustache_scale.Assign(info.mustache_scale);
|
||||
bf.mustache_y.Assign(info.mustache_y);
|
||||
bf.glasses_type.Assign(info.glasses_type);
|
||||
bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]);
|
||||
bf.glasses_scale.Assign(info.glasses_scale);
|
||||
bf.glasses_y.Assign(info.glasses_y);
|
||||
bf.mole_type.Assign(info.mole_type);
|
||||
bf.mole_scale.Assign(info.mole_scale);
|
||||
bf.mole_x.Assign(info.mole_x);
|
||||
bf.mole_y.Assign(info.mole_y);
|
||||
|
||||
return {DefaultMiiName, bf, user_id};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MiiStoreData::MiiStoreData() = default;
|
||||
|
||||
MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
|
||||
const Common::UUID& user_id) {
|
||||
data.name = name;
|
||||
data.uuid = GenerateValidUUID();
|
||||
|
||||
std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
|
||||
data_crc = GenerateCrc16(data.data.data(), sizeof(data));
|
||||
device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID));
|
||||
}
|
||||
|
||||
MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {}
|
||||
|
||||
bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) {
|
||||
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool result{current_update_counter != update_counter};
|
||||
|
||||
current_update_counter = update_counter;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MiiManager::IsFullDatabase() const {
|
||||
// TODO(bunnei): We don't implement the Mii database, so it cannot be full
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 MiiManager::GetCount(SourceFlag source_flag) const {
|
||||
u32 count{};
|
||||
if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
|
||||
// TODO(bunnei): We don't implement the Mii database, but when we do, update this
|
||||
count += 0;
|
||||
}
|
||||
if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
|
||||
count += DefaultMiiCount;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info,
|
||||
SourceFlag source_flag) {
|
||||
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
|
||||
return ERROR_CANNOT_FIND_ENTRY;
|
||||
}
|
||||
|
||||
// TODO(bunnei): We don't implement the Mii database, so we can't have an entry
|
||||
return ERROR_CANNOT_FIND_ENTRY;
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
|
||||
return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::BuildDefault(std::size_t index) {
|
||||
return ConvertStoreDataToInfo(BuildDefaultStoreData(
|
||||
GetArrayValue<DefaultMii>(RawData::DefaultMii.data(), index), user_id));
|
||||
}
|
||||
|
||||
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
|
||||
std::vector<MiiInfoElement> result;
|
||||
|
||||
if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
|
||||
return MakeResult(std::move(result));
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < DefaultMiiCount; index++) {
|
||||
result.emplace_back(BuildDefault(index), Source::Default);
|
||||
}
|
||||
|
||||
return MakeResult(std::move(result));
|
||||
}
|
||||
|
||||
ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) {
|
||||
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
|
||||
|
||||
index = INVALID_INDEX;
|
||||
|
||||
// TODO(bunnei): We don't implement the Mii database, so we can't have an index
|
||||
return ERROR_CANNOT_FIND_ENTRY;
|
||||
}
|
||||
|
||||
} // namespace Service::Mii
|
||||
331
src/core/hle/service/mii/manager.h
Normal file
331
src/core/hle/service/mii/manager.h
Normal file
@@ -0,0 +1,331 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/mii/types.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
enum class Source : u32 {
|
||||
Database = 0,
|
||||
Default = 1,
|
||||
Account = 2,
|
||||
Friend = 3,
|
||||
};
|
||||
|
||||
enum class SourceFlag : u32 {
|
||||
None = 0,
|
||||
Database = 1 << 0,
|
||||
Default = 1 << 1,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
|
||||
|
||||
struct MiiInfo {
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
std::array<char16_t, 11> name{};
|
||||
u8 font_region{};
|
||||
u8 favorite_color{};
|
||||
u8 gender{};
|
||||
u8 height{};
|
||||
u8 build{};
|
||||
u8 type{};
|
||||
u8 region_move{};
|
||||
u8 faceline_type{};
|
||||
u8 faceline_color{};
|
||||
u8 faceline_wrinkle{};
|
||||
u8 faceline_make{};
|
||||
u8 hair_type{};
|
||||
u8 hair_color{};
|
||||
u8 hair_flip{};
|
||||
u8 eye_type{};
|
||||
u8 eye_color{};
|
||||
u8 eye_scale{};
|
||||
u8 eye_aspect{};
|
||||
u8 eye_rotate{};
|
||||
u8 eye_x{};
|
||||
u8 eye_y{};
|
||||
u8 eyebrow_type{};
|
||||
u8 eyebrow_color{};
|
||||
u8 eyebrow_scale{};
|
||||
u8 eyebrow_aspect{};
|
||||
u8 eyebrow_rotate{};
|
||||
u8 eyebrow_x{};
|
||||
u8 eyebrow_y{};
|
||||
u8 nose_type{};
|
||||
u8 nose_scale{};
|
||||
u8 nose_y{};
|
||||
u8 mouth_type{};
|
||||
u8 mouth_color{};
|
||||
u8 mouth_scale{};
|
||||
u8 mouth_aspect{};
|
||||
u8 mouth_y{};
|
||||
u8 beard_color{};
|
||||
u8 beard_type{};
|
||||
u8 mustache_type{};
|
||||
u8 mustache_scale{};
|
||||
u8 mustache_y{};
|
||||
u8 glasses_type{};
|
||||
u8 glasses_color{};
|
||||
u8 glasses_scale{};
|
||||
u8 glasses_y{};
|
||||
u8 mole_type{};
|
||||
u8 mole_scale{};
|
||||
u8 mole_x{};
|
||||
u8 mole_y{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
|
||||
static_assert(std::has_unique_object_representations_v<MiiInfo>,
|
||||
"All bits of MiiInfo must contribute to its value.");
|
||||
|
||||
#pragma pack(push, 4)
|
||||
|
||||
struct MiiInfoElement {
|
||||
MiiInfoElement(const MiiInfo& info, Source source) : info{info}, source{source} {}
|
||||
|
||||
MiiInfo info{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
|
||||
|
||||
struct MiiStoreBitFields {
|
||||
union {
|
||||
u32 word_0{};
|
||||
|
||||
BitField<0, 8, u32> hair_type;
|
||||
BitField<8, 7, u32> height;
|
||||
BitField<15, 1, u32> mole_type;
|
||||
BitField<16, 7, u32> build;
|
||||
BitField<23, 1, HairFlip> hair_flip;
|
||||
BitField<24, 7, u32> hair_color;
|
||||
BitField<31, 1, u32> type;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_1{};
|
||||
|
||||
BitField<0, 7, u32> eye_color;
|
||||
BitField<7, 1, Gender> gender;
|
||||
BitField<8, 7, u32> eyebrow_color;
|
||||
BitField<16, 7, u32> mouth_color;
|
||||
BitField<24, 7, u32> beard_color;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_2{};
|
||||
|
||||
BitField<0, 7, u32> glasses_color;
|
||||
BitField<8, 6, u32> eye_type;
|
||||
BitField<14, 2, u32> region_move;
|
||||
BitField<16, 6, u32> mouth_type;
|
||||
BitField<22, 2, FontRegion> font_region;
|
||||
BitField<24, 5, u32> eye_y;
|
||||
BitField<29, 3, u32> glasses_scale;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_3{};
|
||||
|
||||
BitField<0, 5, u32> eyebrow_type;
|
||||
BitField<5, 3, MustacheType> mustache_type;
|
||||
BitField<8, 5, u32> nose_type;
|
||||
BitField<13, 3, BeardType> beard_type;
|
||||
BitField<16, 5, u32> nose_y;
|
||||
BitField<21, 3, u32> mouth_aspect;
|
||||
BitField<24, 5, u32> mouth_y;
|
||||
BitField<29, 3, u32> eyebrow_aspect;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_4{};
|
||||
|
||||
BitField<0, 5, u32> mustache_y;
|
||||
BitField<5, 3, u32> eye_rotate;
|
||||
BitField<8, 5, u32> glasses_y;
|
||||
BitField<13, 3, u32> eye_aspect;
|
||||
BitField<16, 5, u32> mole_x;
|
||||
BitField<21, 3, u32> eye_scale;
|
||||
BitField<24, 5, u32> mole_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_5{};
|
||||
|
||||
BitField<0, 5, u32> glasses_type;
|
||||
BitField<8, 4, u32> favorite_color;
|
||||
BitField<12, 4, u32> faceline_type;
|
||||
BitField<16, 4, u32> faceline_color;
|
||||
BitField<20, 4, u32> faceline_wrinkle;
|
||||
BitField<24, 4, u32> faceline_makeup;
|
||||
BitField<28, 4, u32> eye_x;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_6{};
|
||||
|
||||
BitField<0, 4, u32> eyebrow_scale;
|
||||
BitField<4, 4, u32> eyebrow_rotate;
|
||||
BitField<8, 4, u32> eyebrow_x;
|
||||
BitField<12, 4, u32> eyebrow_y;
|
||||
BitField<16, 4, u32> nose_scale;
|
||||
BitField<20, 4, u32> mouth_scale;
|
||||
BitField<24, 4, u32> mustache_scale;
|
||||
BitField<28, 4, u32> mole_scale;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
|
||||
"MiiStoreBitFields is not trivially copyable.");
|
||||
|
||||
struct MiiStoreData {
|
||||
using Name = std::array<char16_t, 10>;
|
||||
|
||||
MiiStoreData();
|
||||
MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
|
||||
const Common::UUID& user_id);
|
||||
|
||||
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
|
||||
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
|
||||
// not suitable for our uses.
|
||||
struct {
|
||||
std::array<u8, 0x1C> data{};
|
||||
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
|
||||
|
||||
Name name{};
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
} data;
|
||||
|
||||
u16 data_crc{};
|
||||
u16 device_crc{};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
|
||||
|
||||
struct MiiStoreDataElement {
|
||||
MiiStoreData data{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
|
||||
|
||||
struct MiiDatabase {
|
||||
u32 magic{}; // 'NFDB'
|
||||
std::array<MiiStoreData, 0x64> miis{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 count{};
|
||||
u16 crc{};
|
||||
};
|
||||
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
|
||||
|
||||
struct RandomMiiValues {
|
||||
std::array<u8, 0xbc> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
|
||||
|
||||
struct RandomMiiData4 {
|
||||
Gender gender{};
|
||||
Age age{};
|
||||
Race race{};
|
||||
u32 values_count{};
|
||||
std::array<u8, 0xbc> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
|
||||
|
||||
struct RandomMiiData3 {
|
||||
u32 arg_1;
|
||||
u32 arg_2;
|
||||
u32 values_count;
|
||||
std::array<u8, 0xbc> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
|
||||
|
||||
struct RandomMiiData2 {
|
||||
u32 arg_1;
|
||||
u32 values_count;
|
||||
std::array<u8, 0xbc> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
|
||||
|
||||
struct DefaultMii {
|
||||
u32 face_type{};
|
||||
u32 face_color{};
|
||||
u32 face_wrinkle{};
|
||||
u32 face_makeup{};
|
||||
u32 hair_type{};
|
||||
u32 hair_color{};
|
||||
u32 hair_flip{};
|
||||
u32 eye_type{};
|
||||
u32 eye_color{};
|
||||
u32 eye_scale{};
|
||||
u32 eye_aspect{};
|
||||
u32 eye_rotate{};
|
||||
u32 eye_x{};
|
||||
u32 eye_y{};
|
||||
u32 eyebrow_type{};
|
||||
u32 eyebrow_color{};
|
||||
u32 eyebrow_scale{};
|
||||
u32 eyebrow_aspect{};
|
||||
u32 eyebrow_rotate{};
|
||||
u32 eyebrow_x{};
|
||||
u32 eyebrow_y{};
|
||||
u32 nose_type{};
|
||||
u32 nose_scale{};
|
||||
u32 nose_y{};
|
||||
u32 mouth_type{};
|
||||
u32 mouth_color{};
|
||||
u32 mouth_scale{};
|
||||
u32 mouth_aspect{};
|
||||
u32 mouth_y{};
|
||||
u32 mustache_type{};
|
||||
u32 beard_type{};
|
||||
u32 beard_color{};
|
||||
u32 mustache_scale{};
|
||||
u32 mustache_y{};
|
||||
u32 glasses_type{};
|
||||
u32 glasses_color{};
|
||||
u32 glasses_scale{};
|
||||
u32 glasses_y{};
|
||||
u32 mole_type{};
|
||||
u32 mole_scale{};
|
||||
u32 mole_x{};
|
||||
u32 mole_y{};
|
||||
u32 height{};
|
||||
u32 weight{};
|
||||
Gender gender{};
|
||||
u32 favorite_color{};
|
||||
u32 region{};
|
||||
FontRegion font_region{};
|
||||
u32 type{};
|
||||
INSERT_PADDING_WORDS(5);
|
||||
};
|
||||
static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
|
||||
// with providing an easy interface for HLE emulation of the mii service.
|
||||
class MiiManager {
|
||||
public:
|
||||
MiiManager();
|
||||
|
||||
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
|
||||
bool IsFullDatabase() const;
|
||||
u32 GetCount(SourceFlag source_flag) const;
|
||||
ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag);
|
||||
MiiInfo BuildRandom(Age age, Gender gender, Race race);
|
||||
MiiInfo BuildDefault(std::size_t index);
|
||||
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
|
||||
ResultCode GetIndex(const MiiInfo& info, u32& index);
|
||||
|
||||
private:
|
||||
const Common::UUID user_id;
|
||||
u64 update_counter{};
|
||||
};
|
||||
|
||||
}; // namespace Service::Mii
|
||||
@@ -4,22 +4,17 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/mii/manager.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
|
||||
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||
constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
|
||||
|
||||
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
|
||||
public:
|
||||
@@ -31,19 +26,19 @@ public:
|
||||
{2, &IDatabaseService::GetCount, "GetCount"},
|
||||
{3, &IDatabaseService::Get, "Get"},
|
||||
{4, &IDatabaseService::Get1, "Get1"},
|
||||
{5, nullptr, "UpdateLatest"},
|
||||
{5, &IDatabaseService::UpdateLatest, "UpdateLatest"},
|
||||
{6, &IDatabaseService::BuildRandom, "BuildRandom"},
|
||||
{7, &IDatabaseService::BuildDefault, "BuildDefault"},
|
||||
{8, &IDatabaseService::Get2, "Get2"},
|
||||
{9, &IDatabaseService::Get3, "Get3"},
|
||||
{8, nullptr, "Get2"},
|
||||
{9, nullptr, "Get3"},
|
||||
{10, nullptr, "UpdateLatest1"},
|
||||
{11, &IDatabaseService::FindIndex, "FindIndex"},
|
||||
{12, &IDatabaseService::Move, "Move"},
|
||||
{13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
|
||||
{14, &IDatabaseService::Delete, "Delete"},
|
||||
{15, &IDatabaseService::DestroyFile, "DestroyFile"},
|
||||
{16, &IDatabaseService::DeleteFile, "DeleteFile"},
|
||||
{17, &IDatabaseService::Format, "Format"},
|
||||
{11, nullptr, "FindIndex"},
|
||||
{12, nullptr, "Move"},
|
||||
{13, nullptr, "AddOrReplace"},
|
||||
{14, nullptr, "Delete"},
|
||||
{15, nullptr, "DestroyFile"},
|
||||
{16, nullptr, "DeleteFile"},
|
||||
{17, nullptr, "Format"},
|
||||
{18, nullptr, "Import"},
|
||||
{19, nullptr, "Export"},
|
||||
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
|
||||
@@ -59,31 +54,26 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename OutType>
|
||||
std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
|
||||
u32 requested_size, u32& read_size) {
|
||||
read_size = std::min(requested_size, db.Size() - offset);
|
||||
|
||||
std::vector<u8> out(read_size * sizeof(OutType));
|
||||
|
||||
for (u32 i = 0; i < read_size; ++i) {
|
||||
const auto obj = (db.*getter)(offset + i);
|
||||
std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
|
||||
template <typename T>
|
||||
std::vector<u8> SerializeArray(const std::vector<T>& values) {
|
||||
std::vector<u8> out(values.size() * sizeof(T));
|
||||
std::size_t offset{};
|
||||
for (const auto& value : values) {
|
||||
std::memcpy(out.data() + offset, &value, sizeof(T));
|
||||
offset += sizeof(T);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void IsUpdated(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source={}", source);
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.CheckUpdatedFlag());
|
||||
db.ResetUpdatedFlag();
|
||||
rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter));
|
||||
}
|
||||
|
||||
void IsFullDatabase(Kernel::HLERequestContext& ctx) {
|
||||
@@ -91,93 +81,126 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.Full());
|
||||
rb.Push(manager.IsFullDatabase());
|
||||
}
|
||||
|
||||
void GetCount(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source={}", source);
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(db.Size());
|
||||
rb.Push<u32>(manager.GetCount(source_flag));
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiInfoElement
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[0], source);
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
|
||||
offsets[0] += read_size;
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result->size() > 0) {
|
||||
ctx.WriteBuffer(SerializeArray(*result));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiInfo
|
||||
void Get1(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[1], source);
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
|
||||
offsets[1] += read_size;
|
||||
const auto result{manager.GetDefault(source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<MiiInfo> values;
|
||||
for (const auto& element : *result) {
|
||||
values.emplace_back(element.info);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(SerializeArray(values));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
rb.Push<u32>(static_cast<u32>(result->size()));
|
||||
}
|
||||
|
||||
void UpdateLatest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<MiiInfo>()};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const auto result{manager.UpdateLatest(info, source_flag)};
|
||||
if (result.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(*result);
|
||||
}
|
||||
|
||||
void BuildRandom(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
|
||||
|
||||
if (unknown1 > 3) {
|
||||
const auto age{rp.PopRaw<Age>()};
|
||||
const auto gender{rp.PopRaw<Gender>()};
|
||||
const auto race{rp.PopRaw<Race>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with age={}, gender={}, race={}", age, gender, race);
|
||||
|
||||
if (age > Age::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
|
||||
LOG_ERROR(Service_Mii, "invalid age={}", age);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unknown2 > 2) {
|
||||
if (gender > Gender::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
|
||||
LOG_ERROR(Service_Mii, "invalid gender={}", gender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unknown3 > 3) {
|
||||
if (race > Race::All) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
|
||||
LOG_ERROR(Service_Mii, "invalid race={}", race);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
|
||||
unknown1, unknown2, unknown3);
|
||||
|
||||
const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(info);
|
||||
rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race));
|
||||
}
|
||||
|
||||
void BuildDefault(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto index{rp.PopRaw<u32>()};
|
||||
const auto index{rp.Pop<u32>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with index={}", index);
|
||||
|
||||
if (index > 5) {
|
||||
LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
|
||||
@@ -187,168 +210,20 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
|
||||
|
||||
const auto info = db.CreateDefault(index);
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(info);
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiStoreDataElement
|
||||
void Get2(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[2], source);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(
|
||||
SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
|
||||
offsets[2] += read_size;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
}
|
||||
|
||||
// Gets Miis from database at offset and index in format MiiStoreData
|
||||
void Get3(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto size{rp.PopRaw<u32>()};
|
||||
const auto source{rp.PopRaw<Source>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
|
||||
offsets[3], source);
|
||||
|
||||
u32 read_size{};
|
||||
ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
|
||||
offsets[3] += read_size;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(read_size);
|
||||
}
|
||||
|
||||
void FindIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
const auto unknown{rp.PopRaw<bool>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
const auto index = db.IndexOf(uuid);
|
||||
if (index > MAX_MIIS) {
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(RESULT_UNKNOWN);
|
||||
rb.Push(index);
|
||||
} else {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(index);
|
||||
}
|
||||
}
|
||||
|
||||
void Move(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
const auto index{rp.PopRaw<s32>()};
|
||||
|
||||
if (index < 0) {
|
||||
LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_INVALID_ARGUMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
|
||||
|
||||
const auto success = db.Move(uuid, index);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
|
||||
}
|
||||
|
||||
void AddOrReplace(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto data{rp.PopRaw<MiiStoreData>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
|
||||
Common::UTF16ToUTF8(data.Name()));
|
||||
|
||||
const auto success = db.AddOrReplace(data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find a better error code
|
||||
rb.Push(success ? RESULT_SUCCESS : RESULT_UNKNOWN);
|
||||
}
|
||||
|
||||
void Delete(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid{rp.PopRaw<Common::UUID>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
|
||||
|
||||
const auto success = db.Remove(uuid);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
|
||||
}
|
||||
|
||||
void DestroyFile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
if (!db.IsTestModeEnabled()) {
|
||||
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_NOT_IN_TEST_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.DestroyFile());
|
||||
}
|
||||
|
||||
void DeleteFile(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
if (!db.IsTestModeEnabled()) {
|
||||
LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_NOT_IN_TEST_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(db.DeleteFile());
|
||||
}
|
||||
|
||||
void Format(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
db.Clear();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<MiiInfo>(manager.BuildDefault(index));
|
||||
}
|
||||
|
||||
void GetIndex(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto info{rp.PopRaw<MiiInfo>()};
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
|
||||
Common::UTF16ToUTF8(info.Name()));
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
const auto index = db.IndexOf(info);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
u32 index{};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(manager.GetIndex(info, index));
|
||||
rb.Push(index);
|
||||
}
|
||||
|
||||
@@ -364,12 +239,14 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
MiiManager db;
|
||||
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
|
||||
return current_interface_version >= interface_version;
|
||||
}
|
||||
|
||||
u32 current_interface_version = 0;
|
||||
MiiManager manager;
|
||||
|
||||
// Last read offsets of Get functions
|
||||
std::array<u32, 4> offsets{};
|
||||
u32 current_interface_version{};
|
||||
u64 current_update_counter{};
|
||||
};
|
||||
|
||||
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
|
||||
constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
|
||||
|
||||
// This value was retrieved from HW test
|
||||
constexpr MiiStoreData DEFAULT_MII = {
|
||||
{
|
||||
0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
|
||||
0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
|
||||
},
|
||||
{'y', 'u', 'z', 'u', '\0'},
|
||||
Common::UUID{1, 0},
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
// Default values taken from multiple real databases
|
||||
const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
|
||||
|
||||
constexpr std::array<const char*, 4> SOURCE_NAMES{
|
||||
"Database",
|
||||
"Default",
|
||||
"Account",
|
||||
"Friend",
|
||||
};
|
||||
|
||||
template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
|
||||
std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
|
||||
std::array<T, DestArraySize> out{};
|
||||
std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
|
||||
return out;
|
||||
}
|
||||
|
||||
MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
|
||||
MiiStoreBitFields bf{};
|
||||
std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
|
||||
return {
|
||||
data.uuid,
|
||||
ResizeArray<char16_t, 10, 11>(data.name),
|
||||
static_cast<u8>(bf.font_region.Value()),
|
||||
static_cast<u8>(bf.favorite_color.Value()),
|
||||
static_cast<u8>(bf.gender.Value()),
|
||||
static_cast<u8>(bf.height.Value()),
|
||||
static_cast<u8>(bf.weight.Value()),
|
||||
static_cast<u8>(bf.mii_type.Value()),
|
||||
static_cast<u8>(bf.mii_region.Value()),
|
||||
static_cast<u8>(bf.face_type.Value()),
|
||||
static_cast<u8>(bf.face_color.Value()),
|
||||
static_cast<u8>(bf.face_wrinkle.Value()),
|
||||
static_cast<u8>(bf.face_makeup.Value()),
|
||||
static_cast<u8>(bf.hair_type.Value()),
|
||||
static_cast<u8>(bf.hair_color.Value()),
|
||||
static_cast<bool>(bf.hair_flip.Value()),
|
||||
static_cast<u8>(bf.eye_type.Value()),
|
||||
static_cast<u8>(bf.eye_color.Value()),
|
||||
static_cast<u8>(bf.eye_scale.Value()),
|
||||
static_cast<u8>(bf.eye_aspect.Value()),
|
||||
static_cast<u8>(bf.eye_rotate.Value()),
|
||||
static_cast<u8>(bf.eye_x.Value()),
|
||||
static_cast<u8>(bf.eye_y.Value()),
|
||||
static_cast<u8>(bf.eyebrow_type.Value()),
|
||||
static_cast<u8>(bf.eyebrow_color.Value()),
|
||||
static_cast<u8>(bf.eyebrow_scale.Value()),
|
||||
static_cast<u8>(bf.eyebrow_aspect.Value()),
|
||||
static_cast<u8>(bf.eyebrow_rotate.Value()),
|
||||
static_cast<u8>(bf.eyebrow_x.Value()),
|
||||
static_cast<u8>(bf.eyebrow_y.Value()),
|
||||
static_cast<u8>(bf.nose_type.Value()),
|
||||
static_cast<u8>(bf.nose_scale.Value()),
|
||||
static_cast<u8>(bf.nose_y.Value()),
|
||||
static_cast<u8>(bf.mouth_type.Value()),
|
||||
static_cast<u8>(bf.mouth_color.Value()),
|
||||
static_cast<u8>(bf.mouth_scale.Value()),
|
||||
static_cast<u8>(bf.mouth_aspect.Value()),
|
||||
static_cast<u8>(bf.mouth_y.Value()),
|
||||
static_cast<u8>(bf.facial_hair_color.Value()),
|
||||
static_cast<u8>(bf.beard_type.Value()),
|
||||
static_cast<u8>(bf.mustache_type.Value()),
|
||||
static_cast<u8>(bf.mustache_scale.Value()),
|
||||
static_cast<u8>(bf.mustache_y.Value()),
|
||||
static_cast<u8>(bf.glasses_type.Value()),
|
||||
static_cast<u8>(bf.glasses_color.Value()),
|
||||
static_cast<u8>(bf.glasses_scale.Value()),
|
||||
static_cast<u8>(bf.glasses_y.Value()),
|
||||
static_cast<u8>(bf.mole_type.Value()),
|
||||
static_cast<u8>(bf.mole_scale.Value()),
|
||||
static_cast<u8>(bf.mole_x.Value()),
|
||||
static_cast<u8>(bf.mole_y.Value()),
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
|
||||
MiiStoreData out{};
|
||||
out.name = ResizeArray<char16_t, 11, 10>(info.name);
|
||||
out.uuid = info.uuid;
|
||||
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
bf.hair_type.Assign(info.hair_type);
|
||||
bf.mole_type.Assign(info.mole_type);
|
||||
bf.height.Assign(info.height);
|
||||
bf.hair_flip.Assign(info.hair_flip);
|
||||
bf.weight.Assign(info.weight);
|
||||
bf.hair_color.Assign(info.hair_color);
|
||||
|
||||
bf.gender.Assign(info.gender);
|
||||
bf.eye_color.Assign(info.eye_color);
|
||||
bf.eyebrow_color.Assign(info.eyebrow_color);
|
||||
bf.mouth_color.Assign(info.mouth_color);
|
||||
bf.facial_hair_color.Assign(info.facial_hair_color);
|
||||
|
||||
bf.mii_type.Assign(info.mii_type);
|
||||
bf.glasses_color.Assign(info.glasses_color);
|
||||
bf.font_region.Assign(info.font_region);
|
||||
bf.eye_type.Assign(info.eye_type);
|
||||
bf.mii_region.Assign(info.mii_region);
|
||||
bf.mouth_type.Assign(info.mouth_type);
|
||||
bf.glasses_scale.Assign(info.glasses_scale);
|
||||
bf.eye_y.Assign(info.eye_y);
|
||||
|
||||
bf.mustache_type.Assign(info.mustache_type);
|
||||
bf.eyebrow_type.Assign(info.eyebrow_type);
|
||||
bf.beard_type.Assign(info.beard_type);
|
||||
bf.nose_type.Assign(info.nose_type);
|
||||
bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
|
||||
bf.nose_y.Assign(info.nose_y);
|
||||
bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
|
||||
bf.mouth_y.Assign(info.mouth_y);
|
||||
|
||||
bf.eye_rotate.Assign(info.eye_rotate);
|
||||
bf.mustache_y.Assign(info.mustache_y);
|
||||
bf.eye_aspect.Assign(info.eye_aspect_ratio);
|
||||
bf.glasses_y.Assign(info.glasses_y);
|
||||
bf.eye_scale.Assign(info.eye_scale);
|
||||
bf.mole_x.Assign(info.mole_x);
|
||||
bf.mole_y.Assign(info.mole_y);
|
||||
|
||||
bf.glasses_type.Assign(info.glasses_type);
|
||||
bf.face_type.Assign(info.face_type);
|
||||
bf.favorite_color.Assign(info.favorite_color);
|
||||
bf.face_wrinkle.Assign(info.face_wrinkle);
|
||||
bf.face_color.Assign(info.face_color);
|
||||
bf.eye_x.Assign(info.eye_x);
|
||||
bf.face_makeup.Assign(info.face_makeup);
|
||||
|
||||
bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
|
||||
bf.eyebrow_scale.Assign(info.eyebrow_scale);
|
||||
bf.eyebrow_y.Assign(info.eyebrow_y);
|
||||
bf.eyebrow_x.Assign(info.eyebrow_x);
|
||||
bf.mouth_scale.Assign(info.mouth_scale);
|
||||
bf.nose_scale.Assign(info.nose_scale);
|
||||
bf.mole_scale.Assign(info.mole_scale);
|
||||
bf.mustache_scale.Assign(info.mustache_scale);
|
||||
|
||||
std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Source source) {
|
||||
if (static_cast<std::size_t>(source) >= SOURCE_NAMES.size()) {
|
||||
return os << "[UNKNOWN SOURCE]";
|
||||
}
|
||||
|
||||
os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
|
||||
return os;
|
||||
}
|
||||
|
||||
std::u16string MiiInfo::Name() const {
|
||||
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
|
||||
}
|
||||
|
||||
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
|
||||
return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
std::u16string MiiStoreData::Name() const {
|
||||
return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
|
||||
}
|
||||
|
||||
MiiManager::MiiManager() = default;
|
||||
|
||||
MiiManager::~MiiManager() = default;
|
||||
|
||||
MiiInfo MiiManager::CreateRandom(RandomParameters params) {
|
||||
LOG_WARNING(Service_Mii,
|
||||
"(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
|
||||
params.unknown_1, params.unknown_2, params.unknown_3);
|
||||
|
||||
return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::CreateDefault(u32 index) {
|
||||
const auto new_mii = CreateMiiWithUniqueUUID();
|
||||
|
||||
database.miis.at(index) = new_mii;
|
||||
|
||||
EnsureDatabasePartition();
|
||||
return ConvertStoreDataToInfo(new_mii);
|
||||
}
|
||||
|
||||
bool MiiManager::CheckUpdatedFlag() const {
|
||||
return updated_flag;
|
||||
}
|
||||
|
||||
void MiiManager::ResetUpdatedFlag() {
|
||||
updated_flag = false;
|
||||
}
|
||||
|
||||
bool MiiManager::IsTestModeEnabled() const {
|
||||
return is_test_mode_enabled;
|
||||
}
|
||||
|
||||
bool MiiManager::Empty() const {
|
||||
return Size() == 0;
|
||||
}
|
||||
|
||||
bool MiiManager::Full() const {
|
||||
return Size() == MAX_MIIS;
|
||||
}
|
||||
|
||||
void MiiManager::Clear() {
|
||||
updated_flag = true;
|
||||
std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
|
||||
}
|
||||
|
||||
u32 MiiManager::Size() const {
|
||||
return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
|
||||
[](const MiiStoreData& elem) { return elem.uuid; }));
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::GetInfo(u32 index) const {
|
||||
return ConvertStoreDataToInfo(GetStoreData(index));
|
||||
}
|
||||
|
||||
MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
|
||||
return {GetInfo(index), Source::Database};
|
||||
}
|
||||
|
||||
MiiStoreData MiiManager::GetStoreData(u32 index) const {
|
||||
return database.miis.at(index);
|
||||
}
|
||||
|
||||
MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
|
||||
return {GetStoreData(index), Source::Database};
|
||||
}
|
||||
|
||||
bool MiiManager::Remove(Common::UUID uuid) {
|
||||
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
|
||||
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return false;
|
||||
|
||||
updated_flag = true;
|
||||
*iter = MiiStoreData{};
|
||||
EnsureDatabasePartition();
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 MiiManager::IndexOf(Common::UUID uuid) const {
|
||||
const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
|
||||
[uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return INVALID_INDEX;
|
||||
|
||||
return static_cast<u32>(std::distance(database.miis.begin(), iter));
|
||||
}
|
||||
|
||||
u32 MiiManager::IndexOf(const MiiInfo& info) const {
|
||||
const auto iter =
|
||||
std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
|
||||
return ConvertStoreDataToInfo(elem) == info;
|
||||
});
|
||||
|
||||
if (iter == database.miis.end())
|
||||
return INVALID_INDEX;
|
||||
|
||||
return static_cast<u32>(std::distance(database.miis.begin(), iter));
|
||||
}
|
||||
|
||||
bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
|
||||
const auto index = IndexOf(uuid);
|
||||
|
||||
if (index == INVALID_INDEX || new_index >= MAX_MIIS)
|
||||
return false;
|
||||
|
||||
updated_flag = true;
|
||||
const auto moving = database.miis[index];
|
||||
const auto replacing = database.miis[new_index];
|
||||
if (replacing.uuid) {
|
||||
database.miis[index] = replacing;
|
||||
database.miis[new_index] = moving;
|
||||
} else {
|
||||
database.miis[index] = MiiStoreData{};
|
||||
database.miis[new_index] = moving;
|
||||
}
|
||||
|
||||
EnsureDatabasePartition();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MiiManager::AddOrReplace(const MiiStoreData& data) {
|
||||
const auto index = IndexOf(data.uuid);
|
||||
|
||||
updated_flag = true;
|
||||
if (index == INVALID_INDEX) {
|
||||
const auto size = Size();
|
||||
if (size == MAX_MIIS)
|
||||
return false;
|
||||
database.miis[size] = data;
|
||||
} else {
|
||||
database.miis[index] = data;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MiiManager::DestroyFile() {
|
||||
database = DEFAULT_MII_DATABASE;
|
||||
updated_flag = false;
|
||||
return DeleteFile();
|
||||
}
|
||||
|
||||
bool MiiManager::DeleteFile() {
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
|
||||
return FileUtil::Exists(path) && FileUtil::Delete(path);
|
||||
}
|
||||
|
||||
void MiiManager::WriteToFile() {
|
||||
const auto raw_path =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
|
||||
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
|
||||
FileUtil::Delete(raw_path);
|
||||
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
|
||||
|
||||
if (!FileUtil::CreateFullPath(path)) {
|
||||
LOG_WARNING(Service_Mii,
|
||||
"Failed to create full path of MiiDatabase.dat. Create the directory "
|
||||
"nand/system/save/8000000000000030 to mitigate this "
|
||||
"issue.");
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtil::IOFile save(path, "wb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
|
||||
"made in current session will be saved.");
|
||||
return;
|
||||
}
|
||||
|
||||
save.Resize(sizeof(MiiDatabase));
|
||||
if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
|
||||
LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
|
||||
"and/or regenerated on next run.");
|
||||
save.Resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MiiManager::ReadFromFile() {
|
||||
FileUtil::IOFile save(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
|
||||
"blank Mii database with no Miis.");
|
||||
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
|
||||
return;
|
||||
}
|
||||
|
||||
if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
|
||||
LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
|
||||
"Mii database with no Miis.");
|
||||
std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureDatabasePartition();
|
||||
}
|
||||
|
||||
MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
|
||||
auto new_mii = DEFAULT_MII;
|
||||
|
||||
do {
|
||||
new_mii.uuid = Common::UUID::Generate();
|
||||
} while (IndexOf(new_mii.uuid) != INVALID_INDEX);
|
||||
|
||||
return new_mii;
|
||||
}
|
||||
|
||||
void MiiManager::EnsureDatabasePartition() {
|
||||
std::stable_partition(database.miis.begin(), database.miis.end(),
|
||||
[](const MiiStoreData& elem) { return elem.uuid; });
|
||||
}
|
||||
|
||||
} // namespace Service::Mii
|
||||
@@ -1,273 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
constexpr std::size_t MAX_MIIS{100};
|
||||
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
|
||||
|
||||
struct RandomParameters {
|
||||
u32 unknown_1{};
|
||||
u32 unknown_2{};
|
||||
u32 unknown_3{};
|
||||
};
|
||||
static_assert(sizeof(RandomParameters) == 0xC, "RandomParameters has incorrect size.");
|
||||
|
||||
enum class Source : u32 {
|
||||
Database = 0,
|
||||
Default = 1,
|
||||
Account = 2,
|
||||
Friend = 3,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Source source);
|
||||
|
||||
struct MiiInfo {
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
std::array<char16_t, 11> name{};
|
||||
u8 font_region{};
|
||||
u8 favorite_color{};
|
||||
u8 gender{};
|
||||
u8 height{};
|
||||
u8 weight{};
|
||||
u8 mii_type{};
|
||||
u8 mii_region{};
|
||||
u8 face_type{};
|
||||
u8 face_color{};
|
||||
u8 face_wrinkle{};
|
||||
u8 face_makeup{};
|
||||
u8 hair_type{};
|
||||
u8 hair_color{};
|
||||
bool hair_flip{};
|
||||
u8 eye_type{};
|
||||
u8 eye_color{};
|
||||
u8 eye_scale{};
|
||||
u8 eye_aspect_ratio{};
|
||||
u8 eye_rotate{};
|
||||
u8 eye_x{};
|
||||
u8 eye_y{};
|
||||
u8 eyebrow_type{};
|
||||
u8 eyebrow_color{};
|
||||
u8 eyebrow_scale{};
|
||||
u8 eyebrow_aspect_ratio{};
|
||||
u8 eyebrow_rotate{};
|
||||
u8 eyebrow_x{};
|
||||
u8 eyebrow_y{};
|
||||
u8 nose_type{};
|
||||
u8 nose_scale{};
|
||||
u8 nose_y{};
|
||||
u8 mouth_type{};
|
||||
u8 mouth_color{};
|
||||
u8 mouth_scale{};
|
||||
u8 mouth_aspect_ratio{};
|
||||
u8 mouth_y{};
|
||||
u8 facial_hair_color{};
|
||||
u8 beard_type{};
|
||||
u8 mustache_type{};
|
||||
u8 mustache_scale{};
|
||||
u8 mustache_y{};
|
||||
u8 glasses_type{};
|
||||
u8 glasses_color{};
|
||||
u8 glasses_scale{};
|
||||
u8 glasses_y{};
|
||||
u8 mole_type{};
|
||||
u8 mole_scale{};
|
||||
u8 mole_x{};
|
||||
u8 mole_y{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
|
||||
static_assert(std::has_unique_object_representations_v<MiiInfo>,
|
||||
"All bits of MiiInfo must contribute to its value.");
|
||||
|
||||
bool operator==(const MiiInfo& lhs, const MiiInfo& rhs);
|
||||
bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs);
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct MiiInfoElement {
|
||||
MiiInfo info{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiInfoElement) == 0x5C, "MiiInfoElement has incorrect size.");
|
||||
|
||||
struct MiiStoreBitFields {
|
||||
union {
|
||||
u32 word_0{};
|
||||
|
||||
BitField<24, 8, u32> hair_type;
|
||||
BitField<23, 1, u32> mole_type;
|
||||
BitField<16, 7, u32> height;
|
||||
BitField<15, 1, u32> hair_flip;
|
||||
BitField<8, 7, u32> weight;
|
||||
BitField<0, 7, u32> hair_color;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_1{};
|
||||
|
||||
BitField<31, 1, u32> gender;
|
||||
BitField<24, 7, u32> eye_color;
|
||||
BitField<16, 7, u32> eyebrow_color;
|
||||
BitField<8, 7, u32> mouth_color;
|
||||
BitField<0, 7, u32> facial_hair_color;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_2{};
|
||||
|
||||
BitField<31, 1, u32> mii_type;
|
||||
BitField<24, 7, u32> glasses_color;
|
||||
BitField<22, 2, u32> font_region;
|
||||
BitField<16, 6, u32> eye_type;
|
||||
BitField<14, 2, u32> mii_region;
|
||||
BitField<8, 6, u32> mouth_type;
|
||||
BitField<5, 3, u32> glasses_scale;
|
||||
BitField<0, 5, u32> eye_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_3{};
|
||||
|
||||
BitField<29, 3, u32> mustache_type;
|
||||
BitField<24, 5, u32> eyebrow_type;
|
||||
BitField<21, 3, u32> beard_type;
|
||||
BitField<16, 5, u32> nose_type;
|
||||
BitField<13, 3, u32> mouth_aspect;
|
||||
BitField<8, 5, u32> nose_y;
|
||||
BitField<5, 3, u32> eyebrow_aspect;
|
||||
BitField<0, 5, u32> mouth_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_4{};
|
||||
|
||||
BitField<29, 3, u32> eye_rotate;
|
||||
BitField<24, 5, u32> mustache_y;
|
||||
BitField<21, 3, u32> eye_aspect;
|
||||
BitField<16, 5, u32> glasses_y;
|
||||
BitField<13, 3, u32> eye_scale;
|
||||
BitField<8, 5, u32> mole_x;
|
||||
BitField<0, 5, u32> mole_y;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_5{};
|
||||
|
||||
BitField<24, 5, u32> glasses_type;
|
||||
BitField<20, 4, u32> face_type;
|
||||
BitField<16, 4, u32> favorite_color;
|
||||
BitField<12, 4, u32> face_wrinkle;
|
||||
BitField<8, 4, u32> face_color;
|
||||
BitField<4, 4, u32> eye_x;
|
||||
BitField<0, 4, u32> face_makeup;
|
||||
};
|
||||
|
||||
union {
|
||||
u32 word_6{};
|
||||
|
||||
BitField<28, 4, u32> eyebrow_rotate;
|
||||
BitField<24, 4, u32> eyebrow_scale;
|
||||
BitField<20, 4, u32> eyebrow_y;
|
||||
BitField<16, 4, u32> eyebrow_x;
|
||||
BitField<12, 4, u32> mouth_scale;
|
||||
BitField<8, 4, u32> nose_scale;
|
||||
BitField<4, 4, u32> mole_scale;
|
||||
BitField<0, 4, u32> mustache_scale;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
|
||||
"MiiStoreBitFields is not trivially copyable.");
|
||||
|
||||
struct MiiStoreData {
|
||||
// This corresponds to the above structure MiiStoreBitFields. I did it like this because the
|
||||
// BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
|
||||
// not suitable for our uses.
|
||||
std::array<u8, 0x1C> data{};
|
||||
static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
|
||||
|
||||
std::array<char16_t, 10> name{};
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
u16 crc_1{};
|
||||
u16 crc_2{};
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
|
||||
|
||||
struct MiiStoreDataElement {
|
||||
MiiStoreData data{};
|
||||
Source source{};
|
||||
};
|
||||
static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
|
||||
|
||||
struct MiiDatabase {
|
||||
u32 magic{}; // 'NFDB'
|
||||
std::array<MiiStoreData, MAX_MIIS> miis{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 count{};
|
||||
u16 crc{};
|
||||
};
|
||||
static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
|
||||
#pragma pack(pop)
|
||||
|
||||
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
|
||||
// with providing an easy interface for HLE emulation of the mii service.
|
||||
class MiiManager {
|
||||
public:
|
||||
MiiManager();
|
||||
~MiiManager();
|
||||
|
||||
MiiInfo CreateRandom(RandomParameters params);
|
||||
MiiInfo CreateDefault(u32 index);
|
||||
|
||||
bool CheckUpdatedFlag() const;
|
||||
void ResetUpdatedFlag();
|
||||
|
||||
bool IsTestModeEnabled() const;
|
||||
|
||||
bool Empty() const;
|
||||
bool Full() const;
|
||||
|
||||
void Clear();
|
||||
|
||||
u32 Size() const;
|
||||
|
||||
MiiInfo GetInfo(u32 index) const;
|
||||
MiiInfoElement GetInfoElement(u32 index) const;
|
||||
MiiStoreData GetStoreData(u32 index) const;
|
||||
MiiStoreDataElement GetStoreDataElement(u32 index) const;
|
||||
|
||||
bool Remove(Common::UUID uuid);
|
||||
u32 IndexOf(Common::UUID uuid) const;
|
||||
u32 IndexOf(const MiiInfo& info) const;
|
||||
|
||||
bool Move(Common::UUID uuid, u32 new_index);
|
||||
bool AddOrReplace(const MiiStoreData& data);
|
||||
|
||||
bool DestroyFile();
|
||||
bool DeleteFile();
|
||||
|
||||
private:
|
||||
void WriteToFile();
|
||||
void ReadFromFile();
|
||||
|
||||
MiiStoreData CreateMiiWithUniqueUUID() const;
|
||||
|
||||
void EnsureDatabasePartition();
|
||||
|
||||
MiiDatabase database;
|
||||
bool updated_flag{};
|
||||
bool is_test_mode_enabled{};
|
||||
};
|
||||
|
||||
}; // namespace Service::Mii
|
||||
2261
src/core/hle/service/mii/raw_data.cpp
Normal file
2261
src/core/hle/service/mii/raw_data.cpp
Normal file
File diff suppressed because it is too large
Load Diff
27
src/core/hle/service/mii/raw_data.h
Normal file
27
src/core/hle/service/mii/raw_data.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::Mii::RawData {
|
||||
|
||||
extern const std::array<u8, 1728> DefaultMii;
|
||||
extern const std::array<u8, 3672> RandomMiiFaceline;
|
||||
extern const std::array<u8, 1200> RandomMiiFacelineColor;
|
||||
extern const std::array<u8, 3672> RandomMiiFacelineWrinkle;
|
||||
extern const std::array<u8, 3672> RandomMiiFacelineMakeup;
|
||||
extern const std::array<u8, 3672> RandomMiiHairType;
|
||||
extern const std::array<u8, 1800> RandomMiiHairColor;
|
||||
extern const std::array<u8, 3672> RandomMiiEyeType;
|
||||
extern const std::array<u8, 588> RandomMiiEyeColor;
|
||||
extern const std::array<u8, 3672> RandomMiiEyebrowType;
|
||||
extern const std::array<u8, 3672> RandomMiiNoseType;
|
||||
extern const std::array<u8, 3672> RandomMiiMouthType;
|
||||
extern const std::array<u8, 588> RandomMiiGlassType;
|
||||
|
||||
} // namespace Service::Mii::RawData
|
||||
67
src/core/hle/service/mii/types.h
Normal file
67
src/core/hle/service/mii/types.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2020 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::Mii {
|
||||
|
||||
enum class Age : u32 {
|
||||
Young,
|
||||
Normal,
|
||||
Old,
|
||||
All,
|
||||
};
|
||||
|
||||
enum class BeardType : u32 {
|
||||
None,
|
||||
Beard1,
|
||||
Beard2,
|
||||
Beard3,
|
||||
Beard4,
|
||||
Beard5,
|
||||
};
|
||||
|
||||
enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache };
|
||||
DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
|
||||
|
||||
enum class FontRegion : u32 {
|
||||
Standard,
|
||||
China,
|
||||
Korea,
|
||||
Taiwan,
|
||||
};
|
||||
|
||||
enum class Gender : u32 {
|
||||
Male,
|
||||
Female,
|
||||
All,
|
||||
Maximum = Female,
|
||||
};
|
||||
|
||||
enum class HairFlip : u32 {
|
||||
Left,
|
||||
Right,
|
||||
Maximum = Right,
|
||||
};
|
||||
|
||||
enum class MustacheType : u32 {
|
||||
None,
|
||||
Mustache1,
|
||||
Mustache2,
|
||||
Mustache3,
|
||||
Mustache4,
|
||||
Mustache5,
|
||||
};
|
||||
|
||||
enum class Race : u32 {
|
||||
Black,
|
||||
White,
|
||||
Asian,
|
||||
All,
|
||||
};
|
||||
|
||||
} // namespace Service::Mii
|
||||
@@ -121,11 +121,83 @@ public:
|
||||
{39, nullptr, "PrepareShutdown"},
|
||||
{40, nullptr, "ListApplyDeltaTask"},
|
||||
{41, nullptr, "ClearNotEnoughSpaceStateOfApplyDeltaTask"},
|
||||
{42, nullptr, "Unknown1"},
|
||||
{43, nullptr, "Unknown2"},
|
||||
{44, nullptr, "Unknown3"},
|
||||
{45, nullptr, "Unknown4"},
|
||||
{46, nullptr, "Unknown5"},
|
||||
{42, nullptr, "Unknown42"},
|
||||
{43, nullptr, "Unknown43"},
|
||||
{44, nullptr, "Unknown44"},
|
||||
{45, nullptr, "Unknown45"},
|
||||
{46, nullptr, "Unknown46"},
|
||||
{47, nullptr, "Unknown47"},
|
||||
{48, nullptr, "Unknown48"},
|
||||
{49, nullptr, "Unknown49"},
|
||||
{50, nullptr, "Unknown50"},
|
||||
{51, nullptr, "Unknown51"},
|
||||
{52, nullptr, "Unknown52"},
|
||||
{53, nullptr, "Unknown53"},
|
||||
{54, nullptr, "Unknown54"},
|
||||
{55, nullptr, "Unknown55"},
|
||||
{56, nullptr, "Unknown56"},
|
||||
{57, nullptr, "Unknown57"},
|
||||
{58, nullptr, "Unknown58"},
|
||||
{59, nullptr, "Unknown59"},
|
||||
{60, nullptr, "Unknown60"},
|
||||
{61, nullptr, "Unknown61"},
|
||||
{62, nullptr, "Unknown62"},
|
||||
{63, nullptr, "Unknown63"},
|
||||
{64, nullptr, "Unknown64"},
|
||||
{65, nullptr, "Unknown65"},
|
||||
{66, nullptr, "Unknown66"},
|
||||
{67, nullptr, "Unknown67"},
|
||||
{68, nullptr, "Unknown68"},
|
||||
{69, nullptr, "Unknown69"},
|
||||
{70, nullptr, "Unknown70"},
|
||||
{71, nullptr, "Unknown71"},
|
||||
{72, nullptr, "Unknown72"},
|
||||
{73, nullptr, "Unknown73"},
|
||||
{74, nullptr, "Unknown74"},
|
||||
{75, nullptr, "Unknown75"},
|
||||
{76, nullptr, "Unknown76"},
|
||||
{77, nullptr, "Unknown77"},
|
||||
{78, nullptr, "Unknown78"},
|
||||
{79, nullptr, "Unknown79"},
|
||||
{80, nullptr, "Unknown80"},
|
||||
{81, nullptr, "Unknown81"},
|
||||
{82, nullptr, "Unknown82"},
|
||||
{83, nullptr, "Unknown83"},
|
||||
{84, nullptr, "Unknown84"},
|
||||
{85, nullptr, "Unknown85"},
|
||||
{86, nullptr, "Unknown86"},
|
||||
{87, nullptr, "Unknown87"},
|
||||
{88, nullptr, "Unknown88"},
|
||||
{89, nullptr, "Unknown89"},
|
||||
{90, nullptr, "Unknown90"},
|
||||
{91, nullptr, "Unknown91"},
|
||||
{92, nullptr, "Unknown92"},
|
||||
{93, nullptr, "Unknown93"},
|
||||
{94, nullptr, "Unknown94"},
|
||||
{95, nullptr, "Unknown95"},
|
||||
{96, nullptr, "Unknown96"},
|
||||
{97, nullptr, "Unknown97"},
|
||||
{98, nullptr, "Unknown98"},
|
||||
{99, nullptr, "Unknown99"},
|
||||
{100, nullptr, "Unknown100"},
|
||||
{101, nullptr, "Unknown101"},
|
||||
{102, nullptr, "Unknown102"},
|
||||
{103, nullptr, "Unknown103"},
|
||||
{104, nullptr, "Unknown104"},
|
||||
{105, nullptr, "Unknown105"},
|
||||
{106, nullptr, "Unknown106"},
|
||||
{107, nullptr, "Unknown107"},
|
||||
{108, nullptr, "Unknown108"},
|
||||
{109, nullptr, "Unknown109"},
|
||||
{110, nullptr, "Unknown110"},
|
||||
{111, nullptr, "Unknown111"},
|
||||
{112, nullptr, "Unknown112"},
|
||||
{113, nullptr, "Unknown113"},
|
||||
{114, nullptr, "Unknown114"},
|
||||
{115, nullptr, "Unknown115"},
|
||||
{116, nullptr, "Unknown116"},
|
||||
{117, nullptr, "Unknown117"},
|
||||
{118, nullptr, "Unknown118"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -142,6 +214,7 @@ public:
|
||||
{1, nullptr, "RefreshDebugAvailability"},
|
||||
{2, nullptr, "ClearDebugResponse"},
|
||||
{3, nullptr, "RegisterDebugResponse"},
|
||||
{4, nullptr, "IsLargeResourceAvailable"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -164,6 +237,8 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestDeviceAuthenticationToken"},
|
||||
{1, nullptr, "RequestCachedDeviceAuthenticationToken"},
|
||||
{2, nullptr, "RequestEdgeToken"},
|
||||
{3, nullptr, "RequestCachedEdgeToken"},
|
||||
{100, nullptr, "RequestRegisterDeviceAccount"},
|
||||
{101, nullptr, "RequestUnregisterDeviceAccount"},
|
||||
{102, nullptr, "RequestDeviceAccountStatus"},
|
||||
@@ -181,7 +256,8 @@ public:
|
||||
{305, nullptr, "RequestCreateVirtualAccount"},
|
||||
{306, nullptr, "RequestDeviceLinkStatus"},
|
||||
{400, nullptr, "GetAccountByVirtualAccount"},
|
||||
{500, nullptr, "RequestSyncTicket"},
|
||||
{401, nullptr, "GetVirtualAccount"},
|
||||
{500, nullptr, "RequestSyncTicketLegacy"},
|
||||
{501, nullptr, "RequestDownloadTicket"},
|
||||
{502, nullptr, "RequestDownloadTicketForPrepurchasedContents"},
|
||||
{503, nullptr, "RequestSyncTicket"},
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
{23, nullptr, "DestroyToken"},
|
||||
{24, nullptr, "DestroyTokenWithApplicationId"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
{26, nullptr, "ListenToMyApplicationId"},
|
||||
{31, nullptr, "UploadTokenToBaaS"},
|
||||
{32, nullptr, "DestroyTokenForBaaS"},
|
||||
{33, nullptr, "CreateTokenForBaaS"},
|
||||
|
||||
@@ -104,7 +104,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
|
||||
{94, nullptr, "LaunchApplication"},
|
||||
{95, nullptr, "GetApplicationLaunchInfo"},
|
||||
{96, nullptr, "AcquireApplicationLaunchInfo"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndex2"},
|
||||
{97, nullptr, "GetMainApplicationProgramIndexByApplicationLaunchInfo"},
|
||||
{98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
|
||||
{99, nullptr, "LaunchDevMenu"},
|
||||
{100, nullptr, "ResetToFactorySettings"},
|
||||
@@ -254,7 +254,7 @@ IApplicationManagerInterface::IApplicationManagerInterface()
|
||||
{2170, nullptr, "GetRightsEnvironmentStatus"},
|
||||
{2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
|
||||
{2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
|
||||
{2181, nullptr, "GetResultOfExtendRightsInRightsEnvironment"},
|
||||
{2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
|
||||
{2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
|
||||
{2199, nullptr, "GetRightsEnvironmentCountForDebug"},
|
||||
@@ -366,7 +366,8 @@ ResultVal<u8> IApplicationManagerInterface::GetApplicationDesiredLanguage(
|
||||
LOG_DEBUG(Service_NS, "called with supported_languages={:08X}", supported_languages);
|
||||
|
||||
// Get language code from settings
|
||||
const auto language_code = Set::GetLanguageCodeFromIndex(Settings::values.language_index);
|
||||
const auto language_code =
|
||||
Set::GetLanguageCodeFromIndex(Settings::values.language_index.GetValue());
|
||||
|
||||
// Convert to application language, get priority list
|
||||
const auto application_language = ConvertToApplicationLanguage(language_code);
|
||||
@@ -445,8 +446,8 @@ IApplicationVersionInterface::IApplicationVersionInterface()
|
||||
|
||||
IApplicationVersionInterface::~IApplicationVersionInterface() = default;
|
||||
|
||||
IContentManagerInterface::IContentManagerInterface()
|
||||
: ServiceFramework{"IContentManagerInterface"} {
|
||||
IContentManagementInterface::IContentManagementInterface()
|
||||
: ServiceFramework{"IContentManagementInterface"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
@@ -463,7 +464,7 @@ IContentManagerInterface::IContentManagerInterface()
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IContentManagerInterface::~IContentManagerInterface() = default;
|
||||
IContentManagementInterface::~IContentManagementInterface() = default;
|
||||
|
||||
IDocumentInterface::IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
|
||||
// clang-format off
|
||||
@@ -545,7 +546,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
|
||||
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
|
||||
{7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
|
||||
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
|
||||
{7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
|
||||
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -572,9 +573,9 @@ public:
|
||||
{6, nullptr, "TerminateApplication"},
|
||||
{7, nullptr, "PrepareLaunchProgramFromHost"},
|
||||
{8, nullptr, "LaunchApplication"},
|
||||
{9, nullptr, "LaunchApplicationWithStorageId"},
|
||||
{10, nullptr, "TerminateApplication2"},
|
||||
{11, nullptr, "GetRunningApplicationProcessId"},
|
||||
{9, nullptr, "LaunchApplicationWithStorageIdForDevelop"},
|
||||
{10, nullptr, "IsSystemMemoryResourceLimitBoosted"},
|
||||
{11, nullptr, "GetRunningApplicationProcessIdForDevelop"},
|
||||
{12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
|
||||
{13, nullptr, "CreateApplicationResourceForDevelop"},
|
||||
{14, nullptr, "IsPreomiaForDevelop"},
|
||||
@@ -636,6 +637,10 @@ public:
|
||||
{9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
|
||||
{10, nullptr, "NotifySystemUpdateForContentDelivery"},
|
||||
{11, nullptr, "PrepareShutdown"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
{13, nullptr, "Unknown13"},
|
||||
{14, nullptr, "Unknown14"},
|
||||
{15, nullptr, "Unknown15"},
|
||||
{16, nullptr, "DestroySystemUpdateTask"},
|
||||
{17, nullptr, "RequestSendSystemUpdate"},
|
||||
{18, nullptr, "GetSendSystemUpdateProgress"},
|
||||
|
||||
@@ -40,10 +40,10 @@ public:
|
||||
~IApplicationVersionInterface() override;
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
class IContentManagementInterface final : public ServiceFramework<IContentManagementInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface();
|
||||
~IContentManagerInterface() override;
|
||||
explicit IContentManagementInterface();
|
||||
~IContentManagementInterface() override;
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
|
||||
@@ -163,7 +163,7 @@ PL_U::PL_U(Core::System& system)
|
||||
{5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
|
||||
{6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
|
||||
{100, nullptr, "RequestApplicationFunctionAuthorization"},
|
||||
{101, nullptr, "RequestApplicationFunctionAuthorizationForSystem"},
|
||||
{101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
|
||||
{102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
|
||||
{1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
|
||||
{1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},
|
||||
|
||||
@@ -144,7 +144,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
|
||||
void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
pid = rp.Pop<u64>();
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
|
||||
@@ -154,7 +154,7 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
|
||||
void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -187,13 +187,14 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
|
||||
{4, &NVDRV::QueryEvent, "QueryEvent"},
|
||||
{5, nullptr, "MapSharedMem"},
|
||||
{6, &NVDRV::GetStatus, "GetStatus"},
|
||||
{7, nullptr, "ForceSetClientPID"},
|
||||
{8, &NVDRV::SetClientPID, "SetClientPID"},
|
||||
{7, nullptr, "SetAruidForTest"},
|
||||
{8, &NVDRV::SetAruid, "SetAruid"},
|
||||
{9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
|
||||
{10, nullptr, "InitializeDevtools"},
|
||||
{11, &NVDRV::Ioctl2, "Ioctl2"},
|
||||
{12, &NVDRV::Ioctl3, "Ioctl3"},
|
||||
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
|
||||
{13, &NVDRV::SetGraphicsFirmwareMemoryMarginEnabled,
|
||||
"SetGraphicsFirmwareMemoryMarginEnabled"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ private:
|
||||
void Close(Kernel::HLERequestContext& ctx);
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void QueryEvent(Kernel::HLERequestContext& ctx);
|
||||
void SetClientPID(Kernel::HLERequestContext& ctx);
|
||||
void FinishInitialize(Kernel::HLERequestContext& ctx);
|
||||
void SetAruid(Kernel::HLERequestContext& ctx);
|
||||
void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
|
||||
void GetStatus(Kernel::HLERequestContext& ctx);
|
||||
void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
|
||||
void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
|
||||
|
||||
@@ -10,19 +10,19 @@ namespace Service::Nvidia {
|
||||
|
||||
NVMEMP::NVMEMP() : ServiceFramework("nvmemp") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &NVMEMP::Cmd0, "Cmd0"},
|
||||
{1, &NVMEMP::Cmd1, "Cmd1"},
|
||||
{0, &NVMEMP::Open, "Open"},
|
||||
{1, &NVMEMP::GetAruid, "GetAruid"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
NVMEMP::~NVMEMP() = default;
|
||||
|
||||
void NVMEMP::Cmd0(Kernel::HLERequestContext& ctx) {
|
||||
void NVMEMP::Open(Kernel::HLERequestContext& ctx) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void NVMEMP::Cmd1(Kernel::HLERequestContext& ctx) {
|
||||
void NVMEMP::GetAruid(Kernel::HLERequestContext& ctx) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ public:
|
||||
~NVMEMP() override;
|
||||
|
||||
private:
|
||||
void Cmd0(Kernel::HLERequestContext& ctx);
|
||||
void Cmd1(Kernel::HLERequestContext& ctx);
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
void GetAruid(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -36,6 +36,9 @@ public:
|
||||
{18, nullptr, "ReleaseIrq"},
|
||||
{19, nullptr, "SetIrqEnable"},
|
||||
{20, nullptr, "SetAspmEnable"},
|
||||
{21, nullptr, "SetResetUponResumeEnable"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ public:
|
||||
{24, nullptr, "GetModuleStateTable"},
|
||||
{25, nullptr, "GetPowerDomainStateTable"},
|
||||
{26, nullptr, "GetFuseInfo"},
|
||||
{27, nullptr, "GetDramId"},
|
||||
{28, nullptr, "IsPoweredOn"},
|
||||
{29, nullptr, "GetVoltage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -78,13 +78,13 @@ public:
|
||||
: ServiceFramework{"pm:dmnt"}, kernel(kernel) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetDebugProcesses"},
|
||||
{1, nullptr, "StartDebugProcess"},
|
||||
{2, &DebugMonitor::GetTitlePid, "GetTitlePid"},
|
||||
{3, nullptr, "EnableDebugForTitleId"},
|
||||
{4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"},
|
||||
{5, nullptr, "EnableDebugForApplication"},
|
||||
{6, nullptr, "DisableDebug"},
|
||||
{0, nullptr, "GetJitDebugProcessIdList"},
|
||||
{1, nullptr, "StartProcess"},
|
||||
{2, &DebugMonitor::GetProcessId, "GetProcessId"},
|
||||
{3, nullptr, "HookToCreateProcess"},
|
||||
{4, &DebugMonitor::GetApplicationProcessId, "GetApplicationProcessId"},
|
||||
{5, nullptr, "HookToCreateApplicationProgress"},
|
||||
{6, nullptr, "ClearHook"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetTitlePid(Kernel::HLERequestContext& ctx) {
|
||||
void GetProcessId(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
rb.Push((*process)->GetProcessID());
|
||||
}
|
||||
|
||||
void GetApplicationPid(Kernel::HLERequestContext& ctx) {
|
||||
void GetApplicationProcessId(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PM, "called");
|
||||
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
|
||||
}
|
||||
@@ -163,15 +163,15 @@ public:
|
||||
: ServiceFramework{"pm:shell"}, kernel(kernel) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "LaunchProcess"},
|
||||
{1, nullptr, "TerminateProcessByPid"},
|
||||
{2, nullptr, "TerminateProcessByTitleId"},
|
||||
{3, nullptr, "GetProcessEventWaiter"},
|
||||
{4, nullptr, "GetProcessEventType"},
|
||||
{0, nullptr, "LaunchProgram"},
|
||||
{1, nullptr, "TerminateProcess"},
|
||||
{2, nullptr, "TerminateProgram"},
|
||||
{3, nullptr, "GetProcessEventHandle"},
|
||||
{4, nullptr, "GetProcessEventInfo"},
|
||||
{5, nullptr, "NotifyBootFinished"},
|
||||
{6, &Shell::GetApplicationPid, "GetApplicationPid"},
|
||||
{6, &Shell::GetApplicationProcessIdForShell, "GetApplicationProcessIdForShell"},
|
||||
{7, nullptr, "BoostSystemMemoryResourceLimit"},
|
||||
{8, nullptr, "EnableAdditionalSystemThreads"},
|
||||
{8, nullptr, "BoostApplicationThreadResourceLimit"},
|
||||
{9, nullptr, "GetBootFinishedEventHandle"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -180,7 +180,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void GetApplicationPid(Kernel::HLERequestContext& ctx) {
|
||||
void GetApplicationProcessIdForShell(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_PM, "called");
|
||||
GetApplicationPidGeneric(ctx, kernel.GetProcessList());
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ public:
|
||||
{40101, nullptr, "SetUserAgreementCheckEnabled"},
|
||||
{50100, nullptr, "ReadAllApplicationReportFiles"},
|
||||
{90100, nullptr, "ReadAllReportFiles"},
|
||||
{90101, nullptr, "Unknown90101"},
|
||||
{90102, nullptr, "Unknown90102"},
|
||||
{90200, nullptr, "GetStatistics"},
|
||||
{90201, nullptr, "GetThroughputHistory"},
|
||||
{90300, nullptr, "GetLastUploadError"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ public:
|
||||
{4, nullptr, "Cancel"},
|
||||
{5, nullptr, "PrintModuleInformation"},
|
||||
{6, nullptr, "GetModuleInformation"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
{15, nullptr, "GetBatteryAgePercentage"},
|
||||
{16, nullptr, "GetBatteryChargeInfoEvent"},
|
||||
{17, nullptr, "GetBatteryChargeInfoFields"},
|
||||
{18, nullptr, "GetBatteryChargeCalibratedEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ void GetAvailableLanguageCodesImpl(Kernel::HLERequestContext& ctx, std::size_t m
|
||||
}
|
||||
|
||||
void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
|
||||
const auto language_code = available_language_codes[Settings::values.language_index];
|
||||
const auto language_code = available_language_codes[Settings::values.language_index.GetValue()];
|
||||
const auto key_code =
|
||||
std::find_if(language_to_layout.cbegin(), language_to_layout.cend(),
|
||||
[=](const auto& element) { return element.first == language_code; });
|
||||
@@ -99,7 +99,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
|
||||
if (key_code == language_to_layout.cend()) {
|
||||
LOG_ERROR(Service_SET,
|
||||
"Could not find keyboard layout for language index {}, defaulting to English us",
|
||||
Settings::values.language_index);
|
||||
Settings::values.language_index.GetValue());
|
||||
} else {
|
||||
layout = key_code->second;
|
||||
}
|
||||
@@ -163,11 +163,11 @@ void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
|
||||
LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum(available_language_codes[Settings::values.language_index]);
|
||||
rb.PushEnum(available_language_codes[Settings::values.language_index.GetValue()]);
|
||||
}
|
||||
|
||||
void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -175,7 +175,7 @@ void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(Settings::values.region_index);
|
||||
rb.Push(Settings::values.region_index.GetValue());
|
||||
}
|
||||
|
||||
void SET::GetKeyCodeMap(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace Service::SM {
|
||||
|
||||
void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
|
||||
void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain");
|
||||
LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
|
||||
ctx.Session()->ConvertToDomain();
|
||||
@@ -22,7 +22,7 @@ void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(1); // Converted sessions start with 1 request handler
|
||||
}
|
||||
|
||||
void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
|
||||
// and that we probably want to actually make an entirely new Session, but we still need to
|
||||
// verify this on hardware.
|
||||
@@ -33,10 +33,10 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushMoveObjects(ctx.Session()->GetParent()->Client());
|
||||
}
|
||||
|
||||
void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession");
|
||||
void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called, using CloneCurrentObject");
|
||||
|
||||
DuplicateSession(ctx);
|
||||
CloneCurrentObject(ctx);
|
||||
}
|
||||
|
||||
void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
@@ -47,13 +47,14 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u16>(0x1000);
|
||||
}
|
||||
|
||||
// https://switchbrew.org/wiki/IPC_Marshalling
|
||||
Controller::Controller() : ServiceFramework("IpcController") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &Controller::ConvertSessionToDomain, "ConvertSessionToDomain"},
|
||||
{0x00000001, nullptr, "ConvertDomainToSession"},
|
||||
{0x00000002, &Controller::DuplicateSession, "DuplicateSession"},
|
||||
{0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
|
||||
{0x00000004, &Controller::DuplicateSessionEx, "DuplicateSessionEx"},
|
||||
{0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"},
|
||||
{1, nullptr, "CopyFromCurrentDomain"},
|
||||
{2, &Controller::CloneCurrentObject, "CloneCurrentObject"},
|
||||
{3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"},
|
||||
{4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ public:
|
||||
~Controller() override;
|
||||
|
||||
private:
|
||||
void ConvertSessionToDomain(Kernel::HLERequestContext& ctx);
|
||||
void DuplicateSession(Kernel::HLERequestContext& ctx);
|
||||
void DuplicateSessionEx(Kernel::HLERequestContext& ctx);
|
||||
void ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx);
|
||||
void CloneCurrentObject(Kernel::HLERequestContext& ctx);
|
||||
void CloneCurrentObjectEx(Kernel::HLERequestContext& ctx);
|
||||
void QueryPointerBufferSize(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
|
||||
{12, nullptr, "GetDeviceId"},
|
||||
{13, nullptr, "DeleteSettings"},
|
||||
{14, nullptr, "ImportSettings"},
|
||||
{15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
|
||||
{20, nullptr, "Resolve"},
|
||||
{21, nullptr, "ResolveEx"},
|
||||
{30, nullptr, "GetNasServiceSetting"},
|
||||
@@ -28,6 +29,11 @@ NSD::NSD(const char* name) : ServiceFramework(name) {
|
||||
{60, nullptr, "ReadSaveDataFromFsForTest"},
|
||||
{61, nullptr, "WriteSaveDataToFsForTest"},
|
||||
{62, nullptr, "DeleteSaveDataOfFsForTest"},
|
||||
{63, nullptr, "IsChangeEnvironmentIdentifierDisabled"},
|
||||
{64, nullptr, "SetWithoutDomainExchangeFqdns"},
|
||||
{100, nullptr, "GetApplicationServerEnvironmentType"},
|
||||
{101, nullptr, "SetApplicationServerEnvironmentType"},
|
||||
{102, nullptr, "DeleteApplicationServerEnvironmentType"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace Service::Sockets {
|
||||
|
||||
void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
|
||||
void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u8 use_nsd_resolve;
|
||||
u32 unknown;
|
||||
@@ -29,15 +29,20 @@ SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "SetDnsAddressesPrivate"},
|
||||
{1, nullptr, "GetDnsAddressPrivate"},
|
||||
{2, nullptr, "GetHostByName"},
|
||||
{3, nullptr, "GetHostByAddr"},
|
||||
{4, nullptr, "GetHostStringError"},
|
||||
{5, nullptr, "GetGaiStringError"},
|
||||
{6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"},
|
||||
{7, nullptr, "GetNameInfo"},
|
||||
{8, nullptr, "RequestCancelHandle"},
|
||||
{9, nullptr, "CancelSocketCall"},
|
||||
{11, nullptr, "ClearDnsIpServerAddressArray"},
|
||||
{2, nullptr, "GetHostByNameRequest"},
|
||||
{3, nullptr, "GetHostByAddrRequest"},
|
||||
{4, nullptr, "GetHostStringErrorRequest"},
|
||||
{5, nullptr, "GetGaiStringErrorRequest"},
|
||||
{6, &SFDNSRES::GetAddrInfoRequest, "GetAddrInfoRequest"},
|
||||
{7, nullptr, "GetNameInfoRequest"},
|
||||
{8, nullptr, "RequestCancelHandleRequest"},
|
||||
{9, nullptr, "CancelRequest"},
|
||||
{10, nullptr, "GetHostByNameRequestWithOptions"},
|
||||
{11, nullptr, "GetHostByAddrRequestWithOptions"},
|
||||
{12, nullptr, "GetAddrInfoRequestWithOptions"},
|
||||
{13, nullptr, "GetNameInfoRequestWithOptions"},
|
||||
{14, nullptr, "ResolverSetOptionRequest"},
|
||||
{15, nullptr, "ResolverGetOptionRequest"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
~SFDNSRES() override;
|
||||
|
||||
private:
|
||||
void GetAddrInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Sockets
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Service::SPL {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
|
||||
rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
|
||||
@@ -9,35 +9,36 @@ namespace Service::SPL {
|
||||
SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetConfig"},
|
||||
{1, nullptr, "UserExpMod"},
|
||||
{1, nullptr, "ModularExponentiate"},
|
||||
{2, nullptr, "GenerateAesKek"},
|
||||
{3, nullptr, "LoadAesKey"},
|
||||
{4, nullptr, "GenerateAesKey"},
|
||||
{5, nullptr, "SetConfig"},
|
||||
{7, &SPL::GetRandomBytes, "GetRandomBytes"},
|
||||
{9, nullptr, "LoadSecureExpModKey"},
|
||||
{10, nullptr, "SecureExpMod"},
|
||||
{9, nullptr, "ImportLotusKey"},
|
||||
{10, nullptr, "DecryptLotusMessage"},
|
||||
{11, nullptr, "IsDevelopment"},
|
||||
{12, nullptr, "GenerateSpecificAesKey"},
|
||||
{13, nullptr, "DecryptPrivk"},
|
||||
{13, nullptr, "DecryptDeviceUniqueData"},
|
||||
{14, nullptr, "DecryptAesKey"},
|
||||
{15, nullptr, "DecryptAesCtr"},
|
||||
{15, nullptr, "CryptAesCtr"},
|
||||
{16, nullptr, "ComputeCmac"},
|
||||
{17, nullptr, "LoadRsaOaepKey"},
|
||||
{18, nullptr, "UnwrapRsaOaepWrappedTitleKey"},
|
||||
{17, nullptr, "ImportEsKey"},
|
||||
{18, nullptr, "UnwrapTitleKey"},
|
||||
{19, nullptr, "LoadTitleKey"},
|
||||
{20, nullptr, "UnwrapAesWrappedTitleKey"},
|
||||
{21, nullptr, "LockAesEngine"},
|
||||
{22, nullptr, "UnlockAesEngine"},
|
||||
{23, nullptr, "GetSplWaitEvent"},
|
||||
{24, nullptr, "SetSharedData"},
|
||||
{25, nullptr, "GetSharedData"},
|
||||
{26, nullptr, "ImportSslRsaKey"},
|
||||
{27, nullptr, "SecureExpModWithSslKey"},
|
||||
{28, nullptr, "ImportEsRsaKey"},
|
||||
{29, nullptr, "SecureExpModWithEsKey"},
|
||||
{30, nullptr, "EncryptManuRsaKeyForImport"},
|
||||
{31, nullptr, "GetPackage2Hash"},
|
||||
{20, nullptr, "PrepareEsCommonKey"},
|
||||
{21, nullptr, "AllocateAesKeyslot"},
|
||||
{22, nullptr, "DeallocateAesKeySlot"},
|
||||
{23, nullptr, "GetAesKeyslotAvailableEvent"},
|
||||
{24, nullptr, "SetBootReason"},
|
||||
{25, nullptr, "GetBootReason"},
|
||||
{26, nullptr, "DecryptAndStoreSslClientCertKey"},
|
||||
{27, nullptr, "ModularExponentiateWithSslClientCertKey"},
|
||||
{28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
|
||||
{29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
|
||||
{30, nullptr, "ReencryptDeviceUniqueData "},
|
||||
{31, nullptr, "PrepareEsArchiveKey"}, // This is also GetPackage2Hash?
|
||||
{32, nullptr, "LoadPreparedAesKey"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -90,6 +90,13 @@ public:
|
||||
: ServiceFramework("ISteadyClock"), clock_core{clock_core}, system{system} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
|
||||
{2, nullptr, "GetTestOffset"},
|
||||
{3, nullptr, "SetTestOffset"},
|
||||
{100, nullptr, "GetRtcValue"},
|
||||
{101, nullptr, "IsRtcResetDetected"},
|
||||
{102, nullptr, "GetSetupResultValue"},
|
||||
{200, nullptr, "GetInternalOffset"},
|
||||
{201, nullptr, "SetInternalOffset"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetDsEndpoint"},
|
||||
{1, nullptr, "GetSetupEvent"},
|
||||
{2, nullptr, "Unknown"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "EnableInterface"},
|
||||
{4, nullptr, "DisableInterface"},
|
||||
{5, nullptr, "CtrlInPostBufferAsync"},
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
{9, nullptr, "SetBinaryObjectStore"},
|
||||
{10, nullptr, "Enable"},
|
||||
{11, nullptr, "Disable"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -69,13 +70,13 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Open"},
|
||||
{1, nullptr, "Close"},
|
||||
{2, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Populate"},
|
||||
{4, nullptr, "PostBufferAsync"},
|
||||
{5, nullptr, "GetXferReport"},
|
||||
{6, nullptr, "PostBufferMultiAsync"},
|
||||
{7, nullptr, "Unknown3"},
|
||||
{8, nullptr, "Unknown4"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -88,13 +89,13 @@ public:
|
||||
explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "SetInterface"},
|
||||
{2, nullptr, "GetInterface"},
|
||||
{3, nullptr, "GetAlternateInterface"},
|
||||
{4, nullptr, "GetCurrentFrame"},
|
||||
{5, nullptr, "CtrlXferAsync"},
|
||||
{6, nullptr, "Unknown2"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "GetCtrlXferReport"},
|
||||
{8, nullptr, "ResetDevice"},
|
||||
{9, nullptr, "OpenUsbEp"},
|
||||
@@ -118,7 +119,7 @@ public:
|
||||
{5, nullptr, "DestroyInterfaceAvailableEvent"},
|
||||
{6, nullptr, "GetInterfaceStateChangeEvent"},
|
||||
{7, nullptr, "AcquireUsbIf"},
|
||||
{8, nullptr, "Unknown1"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -179,8 +180,8 @@ public:
|
||||
{4, nullptr, "GetFwRevision"},
|
||||
{5, nullptr, "GetManufacturerId"},
|
||||
{6, nullptr, "GetDeviceId"},
|
||||
{7, nullptr, "Unknown1"},
|
||||
{8, nullptr, "Unknown2"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -215,12 +216,12 @@ public:
|
||||
explicit USB_PM() : ServiceFramework{"usb:pm"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -519,9 +519,9 @@ private:
|
||||
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
|
||||
IGBPConnectResponseParcel response{
|
||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
|
||||
Settings::values.resolution_factor),
|
||||
Settings::values.resolution_factor.GetValue()),
|
||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
|
||||
Settings::values.resolution_factor)};
|
||||
Settings::values.resolution_factor.GetValue())};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
break;
|
||||
}
|
||||
@@ -700,6 +700,7 @@ public:
|
||||
{3215, nullptr, "SetDisplayGamma"},
|
||||
{3216, nullptr, "GetDisplayCmuLuma"},
|
||||
{3217, nullptr, "SetDisplayCmuLuma"},
|
||||
{6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
|
||||
{8225, nullptr, "GetSharedBufferMemoryHandleId"},
|
||||
{8250, nullptr, "OpenSharedLayer"},
|
||||
{8251, nullptr, "CloseSharedLayer"},
|
||||
@@ -748,14 +749,14 @@ private:
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
|
||||
rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
|
||||
@@ -785,6 +786,7 @@ public:
|
||||
{2300, nullptr, "AcquireLayerTexturePresentingEvent"},
|
||||
{2301, nullptr, "ReleaseLayerTexturePresentingEvent"},
|
||||
{2302, nullptr, "GetDisplayHotplugEvent"},
|
||||
{2303, nullptr, "GetDisplayModeChangedEvent"},
|
||||
{2402, nullptr, "GetDisplayHotplugState"},
|
||||
{2501, nullptr, "GetCompositorErrorInfo"},
|
||||
{2601, nullptr, "GetDisplayErrorEvent"},
|
||||
@@ -1029,9 +1031,9 @@ private:
|
||||
// between docked and undocked dimensions. We take the liberty of applying
|
||||
// the resolution scaling factor here.
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
|
||||
}
|
||||
|
||||
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -1064,8 +1066,8 @@ private:
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
DisplayInfo display_info;
|
||||
display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
display_info.width *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
|
||||
display_info.height *= static_cast<u64>(Settings::values.resolution_factor.GetValue());
|
||||
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -12,6 +12,7 @@ VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
|
||||
: ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &VI_U::GetDisplayService, "GetDisplayService"},
|
||||
{1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -15,34 +15,37 @@ public:
|
||||
explicit WLANInfra() : ServiceFramework{"wlan:inf"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{0, nullptr, "OpenMode"},
|
||||
{1, nullptr, "CloseMode"},
|
||||
{2, nullptr, "GetMacAddress"},
|
||||
{3, nullptr, "StartScan"},
|
||||
{4, nullptr, "StopScan"},
|
||||
{5, nullptr, "Connect"},
|
||||
{6, nullptr, "CancelConnect"},
|
||||
{7, nullptr, "Disconnect"},
|
||||
{8, nullptr, "Unknown3"},
|
||||
{9, nullptr, "Unknown4"},
|
||||
{8, nullptr, "GetConnectionEvent"},
|
||||
{9, nullptr, "GetConnectionStatus"},
|
||||
{10, nullptr, "GetState"},
|
||||
{11, nullptr, "GetScanResult"},
|
||||
{12, nullptr, "GetRssi"},
|
||||
{13, nullptr, "ChangeRxAntenna"},
|
||||
{14, nullptr, "Unknown5"},
|
||||
{15, nullptr, "Unknown6"},
|
||||
{14, nullptr, "GetFwVersion"},
|
||||
{15, nullptr, "RequestSleep"},
|
||||
{16, nullptr, "RequestWakeUp"},
|
||||
{17, nullptr, "RequestIfUpDown"},
|
||||
{18, nullptr, "Unknown7"},
|
||||
{19, nullptr, "Unknown8"},
|
||||
{20, nullptr, "Unknown9"},
|
||||
{21, nullptr, "Unknown10"},
|
||||
{22, nullptr, "Unknown11"},
|
||||
{23, nullptr, "Unknown12"},
|
||||
{24, nullptr, "Unknown13"},
|
||||
{25, nullptr, "Unknown14"},
|
||||
{26, nullptr, "Unknown15"},
|
||||
{27, nullptr, "Unknown16"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{25, nullptr, "Unknown25"},
|
||||
{26, nullptr, "Unknown26"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "Unknown28"},
|
||||
{29, nullptr, "Unknown29"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -55,12 +58,12 @@ public:
|
||||
explicit WLANLocal() : ServiceFramework{"wlan:lcl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "GetMacAddress"},
|
||||
{7, nullptr, "CreateBss"},
|
||||
{8, nullptr, "DestroyBss"},
|
||||
@@ -72,38 +75,42 @@ public:
|
||||
{14, nullptr, "CancelJoin"},
|
||||
{15, nullptr, "Disconnect"},
|
||||
{16, nullptr, "SetBeaconLostCount"},
|
||||
{17, nullptr, "Unknown7"},
|
||||
{18, nullptr, "Unknown8"},
|
||||
{19, nullptr, "Unknown9"},
|
||||
{17, nullptr, "Unknown17"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "GetBssIndicationEvent"},
|
||||
{21, nullptr, "GetBssIndicationInfo"},
|
||||
{22, nullptr, "GetState"},
|
||||
{23, nullptr, "GetAllowedChannels"},
|
||||
{24, nullptr, "AddIe"},
|
||||
{25, nullptr, "DeleteIe"},
|
||||
{26, nullptr, "Unknown10"},
|
||||
{27, nullptr, "Unknown11"},
|
||||
{26, nullptr, "Unknown26"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "CreateRxEntry"},
|
||||
{29, nullptr, "DeleteRxEntry"},
|
||||
{30, nullptr, "Unknown12"},
|
||||
{31, nullptr, "Unknown13"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{32, nullptr, "AddMatchingDataToRxEntry"},
|
||||
{33, nullptr, "RemoveMatchingDataFromRxEntry"},
|
||||
{34, nullptr, "GetScanResult"},
|
||||
{35, nullptr, "Unknown14"},
|
||||
{35, nullptr, "Unknown35"},
|
||||
{36, nullptr, "SetActionFrameWithBeacon"},
|
||||
{37, nullptr, "CancelActionFrameWithBeacon"},
|
||||
{38, nullptr, "CreateRxEntryForActionFrame"},
|
||||
{39, nullptr, "DeleteRxEntryForActionFrame"},
|
||||
{40, nullptr, "Unknown15"},
|
||||
{41, nullptr, "Unknown16"},
|
||||
{40, nullptr, "Unknown40"},
|
||||
{41, nullptr, "Unknown41"},
|
||||
{42, nullptr, "CancelGetActionFrame"},
|
||||
{43, nullptr, "GetRssi"},
|
||||
{44, nullptr, "Unknown17"},
|
||||
{45, nullptr, "Unknown18"},
|
||||
{46, nullptr, "Unknown19"},
|
||||
{47, nullptr, "Unknown20"},
|
||||
{48, nullptr, "Unknown21"},
|
||||
{44, nullptr, "Unknown44"},
|
||||
{45, nullptr, "Unknown45"},
|
||||
{46, nullptr, "Unknown46"},
|
||||
{47, nullptr, "Unknown47"},
|
||||
{48, nullptr, "Unknown48"},
|
||||
{49, nullptr, "Unknown49"},
|
||||
{50, nullptr, "Unknown50"},
|
||||
{51, nullptr, "Unknown51"},
|
||||
{52, nullptr, "Unknown52"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -142,18 +149,19 @@ public:
|
||||
explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown1"},
|
||||
{1, nullptr, "Unknown2"},
|
||||
{2, nullptr, "Unknown3"},
|
||||
{3, nullptr, "Unknown4"},
|
||||
{4, nullptr, "Unknown5"},
|
||||
{5, nullptr, "Unknown6"},
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "GetMacAddress"},
|
||||
{7, nullptr, "SwitchTsfTimerFunction"},
|
||||
{8, nullptr, "Unknown7"},
|
||||
{9, nullptr, "Unknown8"},
|
||||
{10, nullptr, "Unknown9"},
|
||||
{11, nullptr, "Unknown10"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{11, nullptr, "Unknown11"},
|
||||
{12, nullptr, "Unknown12"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -548,9 +548,9 @@ struct Memory::Impl {
|
||||
// longer exist, and we should just leave the pagetable entry blank.
|
||||
page_type = Common::PageType::Unmapped;
|
||||
} else {
|
||||
page_type = Common::PageType::Memory;
|
||||
current_page_table->pointers[vaddr >> PAGE_BITS] =
|
||||
pointer - (vaddr & ~PAGE_MASK);
|
||||
page_type = Common::PageType::Memory;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -591,9 +591,12 @@ struct Memory::Impl {
|
||||
base + page_table.pointers.size());
|
||||
|
||||
if (!target) {
|
||||
ASSERT_MSG(type != Common::PageType::Memory,
|
||||
"Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
|
||||
|
||||
while (base != end) {
|
||||
page_table.pointers[base] = nullptr;
|
||||
page_table.attributes[base] = type;
|
||||
page_table.pointers[base] = nullptr;
|
||||
page_table.backing_addr[base] = 0;
|
||||
|
||||
base += 1;
|
||||
|
||||
@@ -119,13 +119,14 @@ double PerfStats::GetLastFrameTimeScale() {
|
||||
}
|
||||
|
||||
void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
|
||||
if (!Settings::values.use_frame_limit || Settings::values.use_multi_core) {
|
||||
if (!Settings::values.use_frame_limit.GetValue() ||
|
||||
Settings::values.use_multi_core.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = Clock::now();
|
||||
|
||||
const double sleep_scale = Settings::values.frame_limit / 100.0;
|
||||
const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0;
|
||||
|
||||
// Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current
|
||||
// speed percent or it will clamp too much and prevent this from properly limiting to that
|
||||
|
||||
@@ -62,6 +62,7 @@ const std::array<const char*, NumMouseButtons> mapping = {{
|
||||
}
|
||||
|
||||
Values values = {};
|
||||
bool configuring_global = true;
|
||||
|
||||
std::string GetTimeZoneString() {
|
||||
static constexpr std::array<const char*, 46> timezones{{
|
||||
@@ -73,9 +74,9 @@ std::string GetTimeZoneString() {
|
||||
"UCT", "Universal", "UTC", "W-SU", "WET", "Zulu",
|
||||
}};
|
||||
|
||||
ASSERT(Settings::values.time_zone_index < timezones.size());
|
||||
ASSERT(Settings::values.time_zone_index.GetValue() < timezones.size());
|
||||
|
||||
return timezones[Settings::values.time_zone_index];
|
||||
return timezones[Settings::values.time_zone_index.GetValue()];
|
||||
}
|
||||
|
||||
void Apply() {
|
||||
@@ -97,25 +98,25 @@ void LogSetting(const std::string& name, const T& value) {
|
||||
|
||||
void LogSettings() {
|
||||
LOG_INFO(Config, "yuzu Configuration:");
|
||||
LogSetting("System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
|
||||
LogSetting("Controls_UseDockedMode", Settings::values.use_docked_mode);
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.GetValue().value_or(0));
|
||||
LogSetting("System_CurrentUser", Settings::values.current_user);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index);
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index);
|
||||
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index);
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index.GetValue());
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index.GetValue());
|
||||
LogSetting("System_TimeZoneIndex", Settings::values.time_zone_index.GetValue());
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor.GetValue());
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
||||
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue());
|
||||
LogSetting("Renderer_GPUAccuracyLevel", Settings::values.gpu_accuracy.GetValue());
|
||||
LogSetting("Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
|
||||
LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
|
||||
LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy);
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
LogSetting("Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
LogSetting("Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders.GetValue());
|
||||
LogSetting("Renderer_AnisotropicFilteringLevel", Settings::values.max_anisotropy.GetValue());
|
||||
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching.GetValue());
|
||||
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
|
||||
LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
|
||||
LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
@@ -131,15 +132,61 @@ float Volume() {
|
||||
if (values.audio_muted) {
|
||||
return 0.0f;
|
||||
}
|
||||
return values.volume;
|
||||
return values.volume.GetValue();
|
||||
}
|
||||
|
||||
bool IsGPULevelExtreme() {
|
||||
return values.gpu_accuracy == GPUAccuracy::Extreme;
|
||||
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme;
|
||||
}
|
||||
|
||||
bool IsGPULevelHigh() {
|
||||
return values.gpu_accuracy == GPUAccuracy::Extreme || values.gpu_accuracy == GPUAccuracy::High;
|
||||
return values.gpu_accuracy.GetValue() == GPUAccuracy::Extreme ||
|
||||
values.gpu_accuracy.GetValue() == GPUAccuracy::High;
|
||||
}
|
||||
|
||||
void RestoreGlobalState() {
|
||||
// If a game is running, DO NOT restore the global settings state
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Audio
|
||||
values.enable_audio_stretching.SetGlobal(true);
|
||||
values.volume.SetGlobal(true);
|
||||
|
||||
// Core
|
||||
values.use_multi_core.SetGlobal(true);
|
||||
|
||||
// Renderer
|
||||
values.renderer_backend.SetGlobal(true);
|
||||
values.vulkan_device.SetGlobal(true);
|
||||
values.aspect_ratio.SetGlobal(true);
|
||||
values.max_anisotropy.SetGlobal(true);
|
||||
values.use_frame_limit.SetGlobal(true);
|
||||
values.frame_limit.SetGlobal(true);
|
||||
values.use_disk_shader_cache.SetGlobal(true);
|
||||
values.gpu_accuracy.SetGlobal(true);
|
||||
values.use_asynchronous_gpu_emulation.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.use_assembly_shaders.SetGlobal(true);
|
||||
values.use_fast_gpu_time.SetGlobal(true);
|
||||
values.force_30fps_mode.SetGlobal(true);
|
||||
values.bg_red.SetGlobal(true);
|
||||
values.bg_green.SetGlobal(true);
|
||||
values.bg_blue.SetGlobal(true);
|
||||
|
||||
// System
|
||||
values.language_index.SetGlobal(true);
|
||||
values.region_index.SetGlobal(true);
|
||||
values.time_zone_index.SetGlobal(true);
|
||||
values.rng_seed.SetGlobal(true);
|
||||
values.custom_rtc.SetGlobal(true);
|
||||
values.sound_index.SetGlobal(true);
|
||||
}
|
||||
|
||||
void Sanitize() {
|
||||
values.use_asynchronous_gpu_emulation.SetValue(
|
||||
values.use_asynchronous_gpu_emulation.GetValue() || values.use_multi_core.GetValue());
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -346,31 +346,6 @@ struct TouchscreenInput {
|
||||
u32 rotation_angle;
|
||||
};
|
||||
|
||||
enum class NANDTotalSize : u64 {
|
||||
S29_1GB = 0x747C00000ULL,
|
||||
};
|
||||
|
||||
enum class NANDUserSize : u64 {
|
||||
S26GB = 0x680000000ULL,
|
||||
};
|
||||
|
||||
enum class NANDSystemSize : u64 {
|
||||
S2_5GB = 0xA0000000,
|
||||
};
|
||||
|
||||
enum class SDMCSize : u64 {
|
||||
S1GB = 0x40000000,
|
||||
S2GB = 0x80000000,
|
||||
S4GB = 0x100000000ULL,
|
||||
S8GB = 0x200000000ULL,
|
||||
S16GB = 0x400000000ULL,
|
||||
S32GB = 0x800000000ULL,
|
||||
S64GB = 0x1000000000ULL,
|
||||
S128GB = 0x2000000000ULL,
|
||||
S256GB = 0x4000000000ULL,
|
||||
S1TB = 0x10000000000ULL,
|
||||
};
|
||||
|
||||
enum class RendererBackend {
|
||||
OpenGL = 0,
|
||||
Vulkan = 1,
|
||||
@@ -382,20 +357,102 @@ enum class GPUAccuracy : u32 {
|
||||
Extreme = 2,
|
||||
};
|
||||
|
||||
enum class CPUAccuracy {
|
||||
Accurate = 0,
|
||||
DebugMode = 1,
|
||||
};
|
||||
|
||||
extern bool configuring_global;
|
||||
|
||||
template <typename Type>
|
||||
class Setting final {
|
||||
public:
|
||||
Setting() = default;
|
||||
explicit Setting(Type val) : global{val} {}
|
||||
~Setting() = default;
|
||||
void SetGlobal(bool to_global) {
|
||||
use_global = to_global;
|
||||
}
|
||||
bool UsingGlobal() const {
|
||||
return use_global;
|
||||
}
|
||||
Type GetValue(bool need_global = false) const {
|
||||
if (use_global || need_global) {
|
||||
return global;
|
||||
}
|
||||
return local;
|
||||
}
|
||||
void SetValue(const Type& value) {
|
||||
if (use_global) {
|
||||
global = value;
|
||||
} else {
|
||||
local = value;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool use_global = true;
|
||||
Type global{};
|
||||
Type local{};
|
||||
};
|
||||
|
||||
struct Values {
|
||||
// Audio
|
||||
std::string audio_device_id;
|
||||
std::string sink_id;
|
||||
bool audio_muted;
|
||||
Setting<bool> enable_audio_stretching;
|
||||
Setting<float> volume;
|
||||
|
||||
// Core
|
||||
Setting<bool> use_multi_core;
|
||||
|
||||
// Cpu
|
||||
CPUAccuracy cpu_accuracy;
|
||||
|
||||
bool cpuopt_page_tables;
|
||||
bool cpuopt_block_linking;
|
||||
bool cpuopt_return_stack_buffer;
|
||||
bool cpuopt_fast_dispatcher;
|
||||
bool cpuopt_context_elimination;
|
||||
bool cpuopt_const_prop;
|
||||
bool cpuopt_misc_ir;
|
||||
bool cpuopt_reduce_misalign_checks;
|
||||
|
||||
// Renderer
|
||||
Setting<RendererBackend> renderer_backend;
|
||||
bool renderer_debug;
|
||||
Setting<int> vulkan_device;
|
||||
|
||||
Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
|
||||
Setting<int> aspect_ratio;
|
||||
Setting<int> max_anisotropy;
|
||||
Setting<bool> use_frame_limit;
|
||||
Setting<u16> frame_limit;
|
||||
Setting<bool> use_disk_shader_cache;
|
||||
Setting<GPUAccuracy> gpu_accuracy;
|
||||
Setting<bool> use_asynchronous_gpu_emulation;
|
||||
Setting<bool> use_vsync;
|
||||
Setting<bool> use_assembly_shaders;
|
||||
Setting<bool> force_30fps_mode;
|
||||
Setting<bool> use_fast_gpu_time;
|
||||
|
||||
Setting<float> bg_red;
|
||||
Setting<float> bg_green;
|
||||
Setting<float> bg_blue;
|
||||
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
std::optional<u32> rng_seed;
|
||||
Setting<std::optional<u32>> rng_seed;
|
||||
// Measured in seconds since epoch
|
||||
std::optional<std::chrono::seconds> custom_rtc;
|
||||
Setting<std::optional<std::chrono::seconds>> custom_rtc;
|
||||
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
|
||||
std::chrono::seconds custom_rtc_differential;
|
||||
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
s32 region_index;
|
||||
s32 time_zone_index;
|
||||
s32 sound_index;
|
||||
Setting<s32> language_index;
|
||||
Setting<s32> region_index;
|
||||
Setting<s32> time_zone_index;
|
||||
Setting<s32> sound_index;
|
||||
|
||||
// Controls
|
||||
std::array<PlayerInput, 10> players;
|
||||
@@ -419,51 +476,13 @@ struct Values {
|
||||
u16 udp_input_port;
|
||||
u8 udp_pad_index;
|
||||
|
||||
// Core
|
||||
bool use_multi_core;
|
||||
bool use_docked_mode;
|
||||
|
||||
// Data Storage
|
||||
bool use_virtual_sd;
|
||||
bool gamecard_inserted;
|
||||
bool gamecard_current_game;
|
||||
std::string gamecard_path;
|
||||
NANDTotalSize nand_total_size;
|
||||
NANDSystemSize nand_system_size;
|
||||
NANDUserSize nand_user_size;
|
||||
SDMCSize sdmc_size;
|
||||
|
||||
// Renderer
|
||||
RendererBackend renderer_backend;
|
||||
bool renderer_debug;
|
||||
int vulkan_device;
|
||||
|
||||
u16 resolution_factor{1};
|
||||
int aspect_ratio;
|
||||
int max_anisotropy;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_disk_shader_cache;
|
||||
GPUAccuracy gpu_accuracy;
|
||||
bool use_asynchronous_gpu_emulation;
|
||||
bool use_vsync;
|
||||
bool use_assembly_shaders;
|
||||
bool force_30fps_mode;
|
||||
bool use_fast_gpu_time;
|
||||
|
||||
float bg_red;
|
||||
float bg_green;
|
||||
float bg_blue;
|
||||
|
||||
std::string log_filter;
|
||||
|
||||
bool use_dev_keys;
|
||||
|
||||
// Audio
|
||||
bool audio_muted;
|
||||
std::string sink_id;
|
||||
bool enable_audio_stretching;
|
||||
std::string audio_device_id;
|
||||
float volume;
|
||||
|
||||
// Debugging
|
||||
bool record_frame_times;
|
||||
@@ -474,10 +493,13 @@ struct Values {
|
||||
bool dump_nso;
|
||||
bool reporting_services;
|
||||
bool quest_flag;
|
||||
bool disable_cpu_opt;
|
||||
bool disable_macro_jit;
|
||||
|
||||
// BCAT
|
||||
// Misceallaneous
|
||||
std::string log_filter;
|
||||
bool use_dev_keys;
|
||||
|
||||
// Services
|
||||
std::string bcat_backend;
|
||||
bool bcat_boxcat_local;
|
||||
|
||||
@@ -501,4 +523,10 @@ std::string GetTimeZoneString();
|
||||
void Apply();
|
||||
void LogSettings();
|
||||
|
||||
// Restore the global state of all applicable settings in the Values struct
|
||||
void RestoreGlobalState();
|
||||
|
||||
// Fixes settings that are known to cause issues with the emulator
|
||||
void Sanitize();
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -189,19 +189,24 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
// Log user configuration information
|
||||
constexpr auto field_type = Telemetry::FieldType::UserConfig;
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
|
||||
AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend));
|
||||
AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
AddField(field_type, "Audio_EnableAudioStretching",
|
||||
Settings::values.enable_audio_stretching.GetValue());
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
|
||||
AddField(field_type, "Renderer_Backend",
|
||||
TranslateRenderer(Settings::values.renderer_backend.GetValue()));
|
||||
AddField(field_type, "Renderer_ResolutionFactor",
|
||||
Settings::values.resolution_factor.GetValue());
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue());
|
||||
AddField(field_type, "Renderer_UseDiskShaderCache",
|
||||
Settings::values.use_disk_shader_cache.GetValue());
|
||||
AddField(field_type, "Renderer_GPUAccuracyLevel",
|
||||
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy));
|
||||
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
|
||||
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
|
||||
Settings::values.use_asynchronous_gpu_emulation);
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
|
||||
AddField(field_type, "Renderer_UseAssemblyShaders", Settings::values.use_assembly_shaders);
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue());
|
||||
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
|
||||
AddField(field_type, "Renderer_UseAssemblyShaders",
|
||||
Settings::values.use_assembly_shaders.GetValue());
|
||||
AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ if(SDL2_FOUND)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||
endif()
|
||||
|
||||
target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES})
|
||||
target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <libusb.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
|
||||
@@ -24,20 +25,20 @@ Adapter::Adapter() {
|
||||
LOG_INFO(Input, "GC Adapter Initialization started");
|
||||
|
||||
current_status = NO_ADAPTER_DETECTED;
|
||||
libusb_init(&libusb_ctx);
|
||||
get_origin.fill(true);
|
||||
|
||||
StartScanThread();
|
||||
const int init_res = libusb_init(&libusb_ctx);
|
||||
if (init_res == LIBUSB_SUCCESS) {
|
||||
StartScanThread();
|
||||
} else {
|
||||
LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
|
||||
}
|
||||
}
|
||||
|
||||
GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
|
||||
GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
|
||||
GCPadStatus pad = {};
|
||||
bool get_origin = false;
|
||||
|
||||
ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4);
|
||||
if (type != ControllerTypes::None) {
|
||||
get_origin = true;
|
||||
}
|
||||
|
||||
adapter_controllers_status[port] = type;
|
||||
|
||||
static constexpr std::array<PadButton, 8> b1_buttons{
|
||||
@@ -53,6 +54,11 @@ GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_pa
|
||||
PadButton::PAD_TRIGGER_L,
|
||||
};
|
||||
|
||||
if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) {
|
||||
// Controller may have been disconnected, recalibrate if reconnected.
|
||||
get_origin[port] = true;
|
||||
}
|
||||
|
||||
if (adapter_controllers_status[port] != ControllerTypes::None) {
|
||||
const u8 b1 = adapter_payload[1 + (9 * port) + 1];
|
||||
const u8 b2 = adapter_payload[1 + (9 * port) + 2];
|
||||
@@ -69,16 +75,22 @@ GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_pa
|
||||
}
|
||||
}
|
||||
|
||||
if (get_origin) {
|
||||
pad.button |= PAD_GET_ORIGIN;
|
||||
}
|
||||
|
||||
pad.stick_x = adapter_payload[1 + (9 * port) + 3];
|
||||
pad.stick_y = adapter_payload[1 + (9 * port) + 4];
|
||||
pad.substick_x = adapter_payload[1 + (9 * port) + 5];
|
||||
pad.substick_y = adapter_payload[1 + (9 * port) + 6];
|
||||
pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
|
||||
pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
|
||||
|
||||
if (get_origin[port]) {
|
||||
origin_status[port].stick_x = pad.stick_x;
|
||||
origin_status[port].stick_y = pad.stick_y;
|
||||
origin_status[port].substick_x = pad.substick_x;
|
||||
origin_status[port].substick_y = pad.substick_y;
|
||||
origin_status[port].trigger_left = pad.trigger_left;
|
||||
origin_status[port].trigger_right = pad.trigger_right;
|
||||
get_origin[port] = false;
|
||||
}
|
||||
}
|
||||
return pad;
|
||||
}
|
||||
@@ -127,31 +139,31 @@ void Adapter::Read() {
|
||||
for (std::size_t port = 0; port < pads.size(); ++port) {
|
||||
pads[port] = GetPadStatus(port, adapter_payload_copy);
|
||||
if (DeviceConnected(port) && configuring) {
|
||||
if (pads[port].button != PAD_GET_ORIGIN) {
|
||||
if (pads[port].button != 0) {
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
|
||||
// Accounting for a threshold here because of some controller variance
|
||||
if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD ||
|
||||
pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) {
|
||||
if (pads[port].stick_x > origin_status[port].stick_x + pads[port].THRESHOLD ||
|
||||
pads[port].stick_x < origin_status[port].stick_x - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::StickX;
|
||||
pads[port].axis_value = pads[port].stick_x;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD ||
|
||||
pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) {
|
||||
if (pads[port].stick_y > origin_status[port].stick_y + pads[port].THRESHOLD ||
|
||||
pads[port].stick_y < origin_status[port].stick_y - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::StickY;
|
||||
pads[port].axis_value = pads[port].stick_y;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD ||
|
||||
pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) {
|
||||
if (pads[port].substick_x > origin_status[port].substick_x + pads[port].THRESHOLD ||
|
||||
pads[port].substick_x < origin_status[port].substick_x - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::SubstickX;
|
||||
pads[port].axis_value = pads[port].substick_x;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
}
|
||||
if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD ||
|
||||
pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) {
|
||||
if (pads[port].substick_y > origin_status[port].substick_y + pads[port].THRESHOLD ||
|
||||
pads[port].substick_y < origin_status[port].substick_y - pads[port].THRESHOLD) {
|
||||
pads[port].axis = GCAdapter::PadAxes::SubstickY;
|
||||
pads[port].axis_value = pads[port].substick_y;
|
||||
pad_queue[port].Push(pads[port]);
|
||||
@@ -194,7 +206,7 @@ void Adapter::StartScanThread() {
|
||||
}
|
||||
|
||||
detect_thread_running = true;
|
||||
detect_thread = std::thread([=] { ScanThreadFunc(); });
|
||||
detect_thread = std::thread(&Adapter::ScanThreadFunc, this);
|
||||
}
|
||||
|
||||
void Adapter::StopScanThread() {
|
||||
@@ -211,18 +223,30 @@ void Adapter::Setup() {
|
||||
adapter_controllers_status.fill(ControllerTypes::None);
|
||||
|
||||
// pointer to list of connected usb devices
|
||||
libusb_device** devices;
|
||||
libusb_device** devices{};
|
||||
|
||||
// populate the list of devices, get the count
|
||||
const std::size_t device_count = libusb_get_device_list(libusb_ctx, &devices);
|
||||
|
||||
for (std::size_t index = 0; index < device_count; ++index) {
|
||||
if (CheckDeviceAccess(devices[index])) {
|
||||
// GC Adapter found and accessible, registering it
|
||||
GetGCEndpoint(devices[index]);
|
||||
break;
|
||||
}
|
||||
const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
|
||||
if (device_count < 0) {
|
||||
LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
|
||||
detect_thread_running = false; // Stop the loop constantly checking for gc adapter
|
||||
// TODO: For hotplug+gc adapter checkbox implementation, revert this.
|
||||
return;
|
||||
}
|
||||
|
||||
if (devices != nullptr) {
|
||||
for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) {
|
||||
if (CheckDeviceAccess(devices[index])) {
|
||||
// GC Adapter found and accessible, registering it
|
||||
GetGCEndpoint(devices[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
libusb_free_device_list(devices, 1);
|
||||
}
|
||||
// Break out of the ScanThreadFunc() loop that is constantly looking for the device
|
||||
// Assumes user has GC adapter plugged in before launch to use the adapter
|
||||
detect_thread_running = false;
|
||||
}
|
||||
|
||||
bool Adapter::CheckDeviceAccess(libusb_device* device) {
|
||||
@@ -279,7 +303,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
|
||||
|
||||
void Adapter::GetGCEndpoint(libusb_device* device) {
|
||||
libusb_config_descriptor* config = nullptr;
|
||||
libusb_get_config_descriptor(device, 0, &config);
|
||||
const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
|
||||
if (config_descriptor_return != LIBUSB_SUCCESS) {
|
||||
LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
|
||||
config_descriptor_return);
|
||||
return;
|
||||
}
|
||||
|
||||
for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
|
||||
const libusb_interface* interfaceContainer = &config->interface[ic];
|
||||
for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
|
||||
@@ -325,6 +355,7 @@ void Adapter::Reset() {
|
||||
adapter_input_thread.join();
|
||||
|
||||
adapter_controllers_status.fill(ControllerTypes::None);
|
||||
get_origin.fill(true);
|
||||
current_status = NO_ADAPTER_DETECTED;
|
||||
|
||||
if (usb_adapter_handle) {
|
||||
@@ -338,15 +369,16 @@ void Adapter::Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Adapter::DeviceConnected(int port) {
|
||||
bool Adapter::DeviceConnected(std::size_t port) {
|
||||
return adapter_controllers_status[port] != ControllerTypes::None;
|
||||
}
|
||||
|
||||
void Adapter::ResetDeviceType(int port) {
|
||||
void Adapter::ResetDeviceType(std::size_t port) {
|
||||
adapter_controllers_status[port] = ControllerTypes::None;
|
||||
}
|
||||
|
||||
void Adapter::BeginConfiguration() {
|
||||
get_origin.fill(true);
|
||||
for (auto& pq : pad_queue) {
|
||||
pq.Clear();
|
||||
}
|
||||
@@ -376,4 +408,25 @@ const std::array<GCState, 4>& Adapter::GetPadState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
int Adapter::GetOriginValue(int port, int axis) const {
|
||||
const auto& status = origin_status[port];
|
||||
|
||||
switch (static_cast<PadAxes>(axis)) {
|
||||
case PadAxes::StickX:
|
||||
return status.stick_x;
|
||||
case PadAxes::StickY:
|
||||
return status.stick_y;
|
||||
case PadAxes::SubstickX:
|
||||
return status.substick_x;
|
||||
case PadAxes::SubstickY:
|
||||
return status.substick_y;
|
||||
case PadAxes::TriggerLeft:
|
||||
return status.trigger_left;
|
||||
case PadAxes::TriggerRight:
|
||||
return status.trigger_right;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace GCAdapter
|
||||
|
||||
@@ -7,17 +7,15 @@
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <libusb.h>
|
||||
#include <unordered_map>
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
namespace GCAdapter {
|
||||
struct libusb_context;
|
||||
struct libusb_device;
|
||||
struct libusb_device_handle;
|
||||
|
||||
enum {
|
||||
PAD_USE_ORIGIN = 0x0080,
|
||||
PAD_GET_ORIGIN = 0x2000,
|
||||
PAD_ERR_STATUS = 0x8000,
|
||||
};
|
||||
namespace GCAdapter {
|
||||
|
||||
enum class PadButton {
|
||||
PAD_BUTTON_LEFT = 0x0001,
|
||||
@@ -96,14 +94,19 @@ public:
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
/// Returns true if there is a device connected to port
|
||||
bool DeviceConnected(std::size_t port);
|
||||
|
||||
std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
|
||||
const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
|
||||
|
||||
std::array<GCState, 4>& GetPadState();
|
||||
const std::array<GCState, 4>& GetPadState() const;
|
||||
|
||||
int GetOriginValue(int port, int axis) const;
|
||||
|
||||
private:
|
||||
GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload);
|
||||
GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
|
||||
|
||||
void PadToState(const GCPadStatus& pad, GCState& state);
|
||||
|
||||
@@ -115,11 +118,8 @@ private:
|
||||
/// Stop scanning for the adapter
|
||||
void StopScanThread();
|
||||
|
||||
/// Returns true if there is a device connected to port
|
||||
bool DeviceConnected(int port);
|
||||
|
||||
/// Resets status of device connected to port
|
||||
void ResetDeviceType(int port);
|
||||
void ResetDeviceType(std::size_t port);
|
||||
|
||||
/// Returns true if we successfully gain access to GC Adapter
|
||||
bool CheckDeviceAccess(libusb_device* device);
|
||||
@@ -155,6 +155,8 @@ private:
|
||||
|
||||
std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
|
||||
std::array<GCState, 4> state;
|
||||
std::array<bool, 4> get_origin;
|
||||
std::array<GCPadStatus, 4> origin_status;
|
||||
};
|
||||
|
||||
} // namespace GCAdapter
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
@@ -20,7 +21,10 @@ public:
|
||||
~GCButton() override;
|
||||
|
||||
bool GetStatus() const override {
|
||||
return gcadapter->GetPadState()[port].buttons.at(button);
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
return gcadapter->GetPadState()[port].buttons.at(button);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -34,22 +38,20 @@ public:
|
||||
explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
|
||||
GCAdapter::Adapter* adapter)
|
||||
: port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
|
||||
gcadapter(adapter) {
|
||||
// L/R triggers range is only in positive direction beginning near 0
|
||||
// 0.0 threshold equates to near half trigger press, but threshold accounts for variability.
|
||||
if (axis > 3) {
|
||||
threshold *= -0.5;
|
||||
}
|
||||
}
|
||||
gcadapter(adapter), origin_value(adapter->GetOriginValue(port_, axis_)) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f;
|
||||
if (trigger_if_greater) {
|
||||
// TODO: Might be worthwile to set a slider for the trigger threshold. It is currently
|
||||
// always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
|
||||
return axis_value > threshold;
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis);
|
||||
const float axis_value = (current_axis_value - origin_value) / 128.0f;
|
||||
if (trigger_if_greater) {
|
||||
// TODO: Might be worthwile to set a slider for the trigger threshold. It is
|
||||
// currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
|
||||
return axis_value > threshold;
|
||||
}
|
||||
return axis_value < -threshold;
|
||||
}
|
||||
return axis_value < -threshold;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -58,6 +60,7 @@ private:
|
||||
float threshold;
|
||||
bool trigger_if_greater;
|
||||
GCAdapter::Adapter* gcadapter;
|
||||
const float origin_value;
|
||||
};
|
||||
|
||||
GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
|
||||
@@ -94,9 +97,12 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param
|
||||
return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater,
|
||||
adapter.get());
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::ParamPackage GCButtonFactory::GetNextInput() {
|
||||
Common::ParamPackage GCButtonFactory::GetNextInput() const {
|
||||
Common::ParamPackage params;
|
||||
GCAdapter::GCPadStatus pad;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
@@ -144,14 +150,20 @@ void GCButtonFactory::EndConfiguration() {
|
||||
class GCAnalog final : public Input::AnalogDevice {
|
||||
public:
|
||||
GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter)
|
||||
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {}
|
||||
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
|
||||
origin_value_x(adapter->GetOriginValue(port_, axis_x_)),
|
||||
origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {}
|
||||
|
||||
float GetAxis(int axis) const {
|
||||
std::lock_guard lock{mutex};
|
||||
// division is not by a perfect 128 to account for some variance in center location
|
||||
// e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
|
||||
// [20-230]
|
||||
return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f;
|
||||
if (gcadapter->DeviceConnected(port)) {
|
||||
std::lock_guard lock{mutex};
|
||||
const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
|
||||
// division is not by a perfect 128 to account for some variance in center location
|
||||
// e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range
|
||||
// [20-230]
|
||||
return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
|
||||
@@ -201,8 +213,10 @@ private:
|
||||
const int axis_x;
|
||||
const int axis_y;
|
||||
const float deadzone;
|
||||
mutable std::mutex mutex;
|
||||
GCAdapter::Adapter* gcadapter;
|
||||
const float origin_value_x;
|
||||
const float origin_value_y;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from GC Adapter
|
||||
@@ -249,7 +263,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
|
||||
const u8 axis = static_cast<u8>(pad.axis);
|
||||
if (analog_x_axis == -1) {
|
||||
analog_x_axis = axis;
|
||||
controller_number = port;
|
||||
controller_number = static_cast<int>(port);
|
||||
} else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) {
|
||||
analog_y_axis = axis;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
*/
|
||||
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
Common::ParamPackage GetNextInput();
|
||||
Common::ParamPackage GetNextInput() const;
|
||||
|
||||
/// For device input configuration/polling
|
||||
void BeginConfiguration();
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <libusb.h>
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/analog_from_button.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
|
||||
@@ -234,7 +234,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
std::function<void(Status)> status_callback,
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=] {
|
||||
std::thread([=, this] {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
u16 min_x{UINT16_MAX};
|
||||
|
||||
@@ -96,7 +96,8 @@ public:
|
||||
}
|
||||
if (is_written) {
|
||||
map->MarkAsModified(true, GetModifiedTicks());
|
||||
if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::IsGPULevelHigh() &&
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
MarkForAsyncFlush(map);
|
||||
}
|
||||
if (!map->is_written) {
|
||||
@@ -369,7 +370,8 @@ private:
|
||||
}
|
||||
if (modified_inheritance) {
|
||||
map->MarkAsModified(true, GetModifiedTicks());
|
||||
if (Settings::IsGPULevelHigh() && Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::IsGPULevelHigh() &&
|
||||
Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
MarkForAsyncFlush(map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,50 +14,45 @@
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
using namespace Texture;
|
||||
|
||||
MaxwellDMA::MaxwellDMA(Core::System& system, MemoryManager& memory_manager)
|
||||
: system{system}, memory_manager{memory_manager} {}
|
||||
|
||||
void MaxwellDMA::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid MaxwellDMA register, increase the size of the Regs structure");
|
||||
ASSERT_MSG(method < NUM_REGS, "Invalid MaxwellDMA register");
|
||||
|
||||
regs.reg_array[method] = method_argument;
|
||||
|
||||
#define MAXWELLDMA_REG_INDEX(field_name) \
|
||||
(offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
|
||||
|
||||
switch (method) {
|
||||
case MAXWELLDMA_REG_INDEX(exec): {
|
||||
HandleCopy();
|
||||
break;
|
||||
if (method == offsetof(Regs, launch_dma) / sizeof(u32)) {
|
||||
Launch();
|
||||
}
|
||||
}
|
||||
|
||||
#undef MAXWELLDMA_REG_INDEX
|
||||
}
|
||||
|
||||
void MaxwellDMA::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) {
|
||||
for (std::size_t i = 0; i < amount; i++) {
|
||||
for (size_t i = 0; i < amount; ++i) {
|
||||
CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::HandleCopy() {
|
||||
LOG_TRACE(HW_GPU, "Requested a DMA copy");
|
||||
|
||||
const GPUVAddr source = regs.src_address.Address();
|
||||
const GPUVAddr dest = regs.dst_address.Address();
|
||||
void MaxwellDMA::Launch() {
|
||||
LOG_TRACE(Render_OpenGL, "DMA copy 0x{:x} -> 0x{:x}", static_cast<GPUVAddr>(regs.offset_in),
|
||||
static_cast<GPUVAddr>(regs.offset_out));
|
||||
|
||||
// TODO(Subv): Perform more research and implement all features of this engine.
|
||||
ASSERT(regs.exec.enable_swizzle == 0);
|
||||
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
|
||||
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
|
||||
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
|
||||
ASSERT(regs.dst_params.pos_x == 0);
|
||||
ASSERT(regs.dst_params.pos_y == 0);
|
||||
const LaunchDMA& launch = regs.launch_dma;
|
||||
ASSERT(launch.remap_enable == 0);
|
||||
ASSERT(launch.semaphore_type == LaunchDMA::SemaphoreType::NONE);
|
||||
ASSERT(launch.interrupt_type == LaunchDMA::InterruptType::NONE);
|
||||
ASSERT(launch.data_transfer_type == LaunchDMA::DataTransferType::NON_PIPELINED);
|
||||
ASSERT(regs.dst_params.origin.x == 0);
|
||||
ASSERT(regs.dst_params.origin.y == 0);
|
||||
|
||||
if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
const bool is_src_pitch = launch.src_memory_layout == LaunchDMA::MemoryLayout::PITCH;
|
||||
const bool is_dst_pitch = launch.dst_memory_layout == LaunchDMA::MemoryLayout::PITCH;
|
||||
|
||||
if (!is_src_pitch && !is_dst_pitch) {
|
||||
// If both the source and the destination are in block layout, assert.
|
||||
UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
|
||||
return;
|
||||
@@ -66,144 +61,161 @@ void MaxwellDMA::HandleCopy() {
|
||||
// All copies here update the main memory, so mark all rasterizer states as invalid.
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
|
||||
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
|
||||
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
|
||||
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
|
||||
// y_count).
|
||||
if (!regs.exec.enable_2d) {
|
||||
memory_manager.CopyBlock(dest, source, regs.x_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// If both the source and the destination are in linear layout, perform a line-by-line
|
||||
// copy. We're going to take a subrect of size (x_count, y_count) from the source
|
||||
// rectangle. There is no need to manually flush/invalidate the regions because
|
||||
// CopyBlock does that for us.
|
||||
for (u32 line = 0; line < regs.y_count; ++line) {
|
||||
const GPUVAddr source_line = source + line * regs.src_pitch;
|
||||
const GPUVAddr dest_line = dest + line * regs.dst_pitch;
|
||||
memory_manager.CopyBlock(dest_line, source_line, regs.x_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(regs.exec.enable_2d == 1);
|
||||
|
||||
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
|
||||
|
||||
ASSERT(regs.src_params.BlockDepth() == 0);
|
||||
// Optimized path for micro copies.
|
||||
if (regs.dst_pitch * regs.y_count < Texture::GetGOBSize() && regs.dst_pitch <= 64) {
|
||||
const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
|
||||
const std::size_t src_size = Texture::GetGOBSize();
|
||||
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
|
||||
u32 pos_x = regs.src_params.pos_x;
|
||||
u32 pos_y = regs.src_params.pos_y;
|
||||
const u64 offset =
|
||||
Texture::GetGOBOffset(regs.src_params.size_x, regs.src_params.size_y, pos_x, pos_y,
|
||||
regs.src_params.BlockDepth(), bytes_per_pixel);
|
||||
const u32 x_in_gob = 64 / bytes_per_pixel;
|
||||
pos_x = pos_x % x_in_gob;
|
||||
pos_y = pos_y % 8;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
|
||||
regs.src_params.size_x, bytes_per_pixel, read_buffer.data(),
|
||||
write_buffer.data(), regs.src_params.BlockHeight(), pos_x,
|
||||
pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
|
||||
return;
|
||||
}
|
||||
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
|
||||
const u32 bytes_per_pixel = regs.dst_pitch / regs.x_count;
|
||||
const std::size_t src_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y,
|
||||
regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
|
||||
|
||||
const std::size_t src_layer_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y, 1,
|
||||
regs.src_params.BlockHeight(), regs.src_params.BlockDepth());
|
||||
|
||||
const std::size_t dst_size = regs.dst_pitch * regs.y_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
Texture::UnswizzleSubrect(
|
||||
regs.x_count, regs.y_count, regs.dst_pitch, regs.src_params.size_x, bytes_per_pixel,
|
||||
read_buffer.data() + src_layer_size * regs.src_params.pos_z, write_buffer.data(),
|
||||
regs.src_params.BlockHeight(), regs.src_params.pos_x, regs.src_params.pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
if (is_src_pitch && is_dst_pitch) {
|
||||
CopyPitchToPitch();
|
||||
} else {
|
||||
ASSERT(regs.dst_params.BlockDepth() == 0);
|
||||
ASSERT(launch.multi_line_enable == 1);
|
||||
|
||||
const u32 bytes_per_pixel = regs.src_pitch / regs.x_count;
|
||||
|
||||
const std::size_t dst_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y,
|
||||
regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
|
||||
|
||||
const std::size_t dst_layer_size = Texture::CalculateSize(
|
||||
true, bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1,
|
||||
regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
|
||||
|
||||
const std::size_t src_size = regs.src_pitch * regs.y_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(dest, write_buffer.data(), dst_size);
|
||||
if (!is_src_pitch && is_dst_pitch) {
|
||||
CopyBlockLinearToPitch();
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(source, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(dest, write_buffer.data(), dst_size);
|
||||
CopyPitchToBlockLinear();
|
||||
}
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
Texture::SwizzleSubrect(
|
||||
regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, bytes_per_pixel,
|
||||
write_buffer.data() + dst_layer_size * regs.dst_params.pos_z, read_buffer.data(),
|
||||
regs.dst_params.BlockHeight(), regs.dst_params.pos_x, regs.dst_params.pos_y);
|
||||
|
||||
memory_manager.WriteBlock(dest, write_buffer.data(), dst_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyPitchToPitch() {
|
||||
// When `multi_line_enable` bit is disabled the copy is performed as if we were copying a 1D
|
||||
// buffer of length `line_length_in`.
|
||||
// Otherwise we copy a 2D image of dimensions (line_length_in, line_count).
|
||||
if (!regs.launch_dma.multi_line_enable) {
|
||||
memory_manager.CopyBlock(regs.offset_out, regs.offset_in, regs.line_length_in);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform a line-by-line copy.
|
||||
// We're going to take a subrect of size (line_length_in, line_count) from the source rectangle.
|
||||
// There is no need to manually flush/invalidate the regions because CopyBlock does that for us.
|
||||
for (u32 line = 0; line < regs.line_count; ++line) {
|
||||
const GPUVAddr source_line = regs.offset_in + static_cast<size_t>(line) * regs.pitch_in;
|
||||
const GPUVAddr dest_line = regs.offset_out + static_cast<size_t>(line) * regs.pitch_out;
|
||||
memory_manager.CopyBlock(dest_line, source_line, regs.line_length_in);
|
||||
}
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyBlockLinearToPitch() {
|
||||
ASSERT(regs.src_params.block_size.depth == 0);
|
||||
|
||||
// Optimized path for micro copies.
|
||||
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
|
||||
if (dst_size < GOB_SIZE && regs.pitch_out <= GOB_SIZE_X) {
|
||||
FastCopyBlockLinearToPitch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Deswizzle the input and copy it over.
|
||||
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
|
||||
const Parameters& src_params = regs.src_params;
|
||||
const u32 width = src_params.width;
|
||||
const u32 height = src_params.height;
|
||||
const u32 depth = src_params.depth;
|
||||
const u32 block_height = src_params.block_size.height;
|
||||
const u32 block_depth = src_params.block_size.depth;
|
||||
const size_t src_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
|
||||
const size_t src_layer_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel,
|
||||
read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(),
|
||||
block_height, src_params.origin.x, src_params.origin.y);
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::CopyPitchToBlockLinear() {
|
||||
const auto& dst_params = regs.dst_params;
|
||||
const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in;
|
||||
const u32 width = dst_params.width;
|
||||
const u32 height = dst_params.height;
|
||||
const u32 depth = dst_params.depth;
|
||||
const u32 block_height = dst_params.block_size.height;
|
||||
const u32 block_depth = dst_params.block_size.depth;
|
||||
const size_t dst_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
|
||||
const size_t dst_layer_size =
|
||||
CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
|
||||
|
||||
const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
// If the input is linear and the output is tiled, swizzle the input and copy it over.
|
||||
if (regs.dst_params.block_size.depth > 0) {
|
||||
ASSERT(dst_params.layer == 0);
|
||||
SwizzleSliceToVoxel(regs.line_length_in, regs.line_count, regs.pitch_in, width, height,
|
||||
bytes_per_pixel, block_height, block_depth, dst_params.origin.x,
|
||||
dst_params.origin.y, write_buffer.data(), read_buffer.data());
|
||||
} else {
|
||||
SwizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_in, width, bytes_per_pixel,
|
||||
write_buffer.data() + dst_layer_size * dst_params.layer, read_buffer.data(),
|
||||
block_height, dst_params.origin.x, dst_params.origin.y);
|
||||
}
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
void MaxwellDMA::FastCopyBlockLinearToPitch() {
|
||||
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
|
||||
const size_t src_size = GOB_SIZE;
|
||||
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
|
||||
u32 pos_x = regs.src_params.origin.x;
|
||||
u32 pos_y = regs.src_params.origin.y;
|
||||
const u64 offset = GetGOBOffset(regs.src_params.width, regs.src_params.height, pos_x, pos_y,
|
||||
regs.src_params.block_size.height, bytes_per_pixel);
|
||||
const u32 x_in_gob = 64 / bytes_per_pixel;
|
||||
pos_x = pos_x % x_in_gob;
|
||||
pos_y = pos_y % 8;
|
||||
|
||||
if (read_buffer.size() < src_size) {
|
||||
read_buffer.resize(src_size);
|
||||
}
|
||||
|
||||
if (write_buffer.size() < dst_size) {
|
||||
write_buffer.resize(dst_size);
|
||||
}
|
||||
|
||||
if (Settings::IsGPULevelExtreme()) {
|
||||
memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
} else {
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(), src_size);
|
||||
memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width,
|
||||
bytes_per_pixel, read_buffer.data(), write_buffer.data(),
|
||||
regs.src_params.block_size.height, pos_x, pos_y);
|
||||
|
||||
memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size);
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -24,12 +24,167 @@ class MemoryManager;
|
||||
namespace Tegra::Engines {
|
||||
|
||||
/**
|
||||
* This Engine is known as GK104_Copy. Documentation can be found in:
|
||||
* This engine is known as gk104_copy. Documentation can be found in:
|
||||
* https://github.com/NVIDIA/open-gpu-doc/blob/master/classes/dma-copy/clb0b5.h
|
||||
* https://github.com/envytools/envytools/blob/master/rnndb/fifo/gk104_copy.xml
|
||||
*/
|
||||
|
||||
class MaxwellDMA final : public EngineInterface {
|
||||
public:
|
||||
struct PackedGPUVAddr {
|
||||
u32 upper;
|
||||
u32 lower;
|
||||
|
||||
constexpr operator GPUVAddr() const noexcept {
|
||||
return (static_cast<GPUVAddr>(upper & 0xff) << 32) | lower;
|
||||
}
|
||||
};
|
||||
|
||||
union BlockSize {
|
||||
BitField<0, 4, u32> width;
|
||||
BitField<4, 4, u32> height;
|
||||
BitField<8, 4, u32> depth;
|
||||
BitField<12, 4, u32> gob_height;
|
||||
};
|
||||
static_assert(sizeof(BlockSize) == 4);
|
||||
|
||||
union Origin {
|
||||
BitField<0, 16, u32> x;
|
||||
BitField<16, 16, u32> y;
|
||||
};
|
||||
static_assert(sizeof(Origin) == 4);
|
||||
|
||||
struct Parameters {
|
||||
BlockSize block_size;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
u32 layer;
|
||||
Origin origin;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 24);
|
||||
|
||||
struct Semaphore {
|
||||
PackedGPUVAddr address;
|
||||
u32 payload;
|
||||
};
|
||||
static_assert(sizeof(Semaphore) == 12);
|
||||
|
||||
struct RenderEnable {
|
||||
enum class Mode : u32 {
|
||||
FALSE = 0,
|
||||
TRUE = 1,
|
||||
CONDITIONAL = 2,
|
||||
RENDER_IF_EQUAL = 3,
|
||||
RENDER_IF_NOT_EQUAL = 4,
|
||||
};
|
||||
|
||||
PackedGPUVAddr address;
|
||||
BitField<0, 3, Mode> mode;
|
||||
};
|
||||
static_assert(sizeof(RenderEnable) == 12);
|
||||
|
||||
enum class PhysModeTarget : u32 {
|
||||
LOCAL_FB = 0,
|
||||
COHERENT_SYSMEM = 1,
|
||||
NONCOHERENT_SYSMEM = 2,
|
||||
};
|
||||
using PhysMode = BitField<0, 2, PhysModeTarget>;
|
||||
|
||||
union LaunchDMA {
|
||||
enum class DataTransferType : u32 {
|
||||
NONE = 0,
|
||||
PIPELINED = 1,
|
||||
NON_PIPELINED = 2,
|
||||
};
|
||||
|
||||
enum class SemaphoreType : u32 {
|
||||
NONE = 0,
|
||||
RELEASE_ONE_WORD_SEMAPHORE = 1,
|
||||
RELEASE_FOUR_WORD_SEMAPHORE = 2,
|
||||
};
|
||||
|
||||
enum class InterruptType : u32 {
|
||||
NONE = 0,
|
||||
BLOCKING = 1,
|
||||
NON_BLOCKING = 2,
|
||||
};
|
||||
|
||||
enum class MemoryLayout : u32 {
|
||||
BLOCKLINEAR = 0,
|
||||
PITCH = 1,
|
||||
};
|
||||
|
||||
enum class Type : u32 {
|
||||
VIRTUAL = 0,
|
||||
PHYSICAL = 1,
|
||||
};
|
||||
|
||||
enum class SemaphoreReduction : u32 {
|
||||
IMIN = 0,
|
||||
IMAX = 1,
|
||||
IXOR = 2,
|
||||
IAND = 3,
|
||||
IOR = 4,
|
||||
IADD = 5,
|
||||
INC = 6,
|
||||
DEC = 7,
|
||||
FADD = 0xA,
|
||||
};
|
||||
|
||||
enum class SemaphoreReductionSign : u32 {
|
||||
SIGNED = 0,
|
||||
UNSIGNED = 1,
|
||||
};
|
||||
|
||||
enum class BypassL2 : u32 {
|
||||
USE_PTE_SETTING = 0,
|
||||
FORCE_VOLATILE = 1,
|
||||
};
|
||||
|
||||
BitField<0, 2, DataTransferType> data_transfer_type;
|
||||
BitField<2, 1, u32> flush_enable;
|
||||
BitField<3, 2, SemaphoreType> semaphore_type;
|
||||
BitField<5, 2, InterruptType> interrupt_type;
|
||||
BitField<7, 1, MemoryLayout> src_memory_layout;
|
||||
BitField<8, 1, MemoryLayout> dst_memory_layout;
|
||||
BitField<9, 1, u32> multi_line_enable;
|
||||
BitField<10, 1, u32> remap_enable;
|
||||
BitField<11, 1, u32> rmwdisable;
|
||||
BitField<12, 1, Type> src_type;
|
||||
BitField<13, 1, Type> dst_type;
|
||||
BitField<14, 4, SemaphoreReduction> semaphore_reduction;
|
||||
BitField<18, 1, SemaphoreReductionSign> semaphore_reduction_sign;
|
||||
BitField<19, 1, u32> reduction_enable;
|
||||
BitField<20, 1, BypassL2> bypass_l2;
|
||||
};
|
||||
static_assert(sizeof(LaunchDMA) == 4);
|
||||
|
||||
struct RemapConst {
|
||||
enum Swizzle : u32 {
|
||||
SRC_X = 0,
|
||||
SRC_Y = 1,
|
||||
SRC_Z = 2,
|
||||
SRC_W = 3,
|
||||
CONST_A = 4,
|
||||
CONST_B = 5,
|
||||
NO_WRITE = 6,
|
||||
};
|
||||
|
||||
PackedGPUVAddr address;
|
||||
|
||||
union {
|
||||
BitField<0, 3, Swizzle> dst_x;
|
||||
BitField<4, 3, Swizzle> dst_y;
|
||||
BitField<8, 3, Swizzle> dst_z;
|
||||
BitField<12, 3, Swizzle> dst_w;
|
||||
BitField<16, 2, u32> component_size_minus_one;
|
||||
BitField<20, 2, u32> num_src_components_minus_one;
|
||||
BitField<24, 2, u32> num_dst_components_minus_one;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(RemapConst) == 12);
|
||||
|
||||
explicit MaxwellDMA(Core::System& system, MemoryManager& memory_manager);
|
||||
~MaxwellDMA() = default;
|
||||
|
||||
@@ -40,144 +195,19 @@ public:
|
||||
void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
|
||||
u32 methods_pending) override;
|
||||
|
||||
struct Regs {
|
||||
static constexpr std::size_t NUM_REGS = 0x1D6;
|
||||
|
||||
struct Parameters {
|
||||
union {
|
||||
BitField<0, 4, u32> block_depth;
|
||||
BitField<4, 4, u32> block_height;
|
||||
BitField<8, 4, u32> block_width;
|
||||
};
|
||||
u32 size_x;
|
||||
u32 size_y;
|
||||
u32 size_z;
|
||||
u32 pos_z;
|
||||
union {
|
||||
BitField<0, 16, u32> pos_x;
|
||||
BitField<16, 16, u32> pos_y;
|
||||
};
|
||||
|
||||
u32 BlockHeight() const {
|
||||
return block_height.Value();
|
||||
}
|
||||
|
||||
u32 BlockDepth() const {
|
||||
return block_depth.Value();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
|
||||
|
||||
enum class ComponentMode : u32 {
|
||||
Src0 = 0,
|
||||
Src1 = 1,
|
||||
Src2 = 2,
|
||||
Src3 = 3,
|
||||
Const0 = 4,
|
||||
Const1 = 5,
|
||||
Zero = 6,
|
||||
};
|
||||
|
||||
enum class CopyMode : u32 {
|
||||
None = 0,
|
||||
Unk1 = 1,
|
||||
Unk2 = 2,
|
||||
};
|
||||
|
||||
enum class QueryMode : u32 {
|
||||
None = 0,
|
||||
Short = 1,
|
||||
Long = 2,
|
||||
};
|
||||
|
||||
enum class QueryIntr : u32 {
|
||||
None = 0,
|
||||
Block = 1,
|
||||
NonBlock = 2,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
INSERT_UNION_PADDING_WORDS(0xC0);
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 2, CopyMode> copy_mode;
|
||||
BitField<2, 1, u32> flush;
|
||||
|
||||
BitField<3, 2, QueryMode> query_mode;
|
||||
BitField<5, 2, QueryIntr> query_intr;
|
||||
|
||||
BitField<7, 1, u32> is_src_linear;
|
||||
BitField<8, 1, u32> is_dst_linear;
|
||||
|
||||
BitField<9, 1, u32> enable_2d;
|
||||
BitField<10, 1, u32> enable_swizzle;
|
||||
};
|
||||
} exec;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x3F);
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} src_address;
|
||||
|
||||
struct {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
|
||||
GPUVAddr Address() const {
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} dst_address;
|
||||
|
||||
u32 src_pitch;
|
||||
u32 dst_pitch;
|
||||
u32 x_count;
|
||||
u32 y_count;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xB8);
|
||||
|
||||
u32 const0;
|
||||
u32 const1;
|
||||
union {
|
||||
BitField<0, 4, ComponentMode> component0;
|
||||
BitField<4, 4, ComponentMode> component1;
|
||||
BitField<8, 4, ComponentMode> component2;
|
||||
BitField<12, 4, ComponentMode> component3;
|
||||
BitField<16, 2, u32> component_size;
|
||||
BitField<20, 3, u32> src_num_components;
|
||||
BitField<24, 3, u32> dst_num_components;
|
||||
|
||||
u32 SrcBytePerPixel() const {
|
||||
return src_num_components.Value() * component_size.Value();
|
||||
}
|
||||
u32 DstBytePerPixel() const {
|
||||
return dst_num_components.Value() * component_size.Value();
|
||||
}
|
||||
} swizzle_config;
|
||||
|
||||
Parameters dst_params;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
|
||||
Parameters src_params;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x13);
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
private:
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void Launch();
|
||||
|
||||
void CopyPitchToPitch();
|
||||
|
||||
void CopyBlockLinearToPitch();
|
||||
|
||||
void CopyPitchToBlockLinear();
|
||||
|
||||
void FastCopyBlockLinearToPitch();
|
||||
|
||||
Core::System& system;
|
||||
|
||||
MemoryManager& memory_manager;
|
||||
@@ -185,28 +215,58 @@ private:
|
||||
std::vector<u8> read_buffer;
|
||||
std::vector<u8> write_buffer;
|
||||
|
||||
/// Performs the copy from the source buffer to the destination buffer as configured in the
|
||||
/// registers.
|
||||
void HandleCopy();
|
||||
};
|
||||
static constexpr std::size_t NUM_REGS = 0x800;
|
||||
struct Regs {
|
||||
union {
|
||||
struct {
|
||||
u32 reserved[0x40];
|
||||
u32 nop;
|
||||
u32 reserved01[0xf];
|
||||
u32 pm_trigger;
|
||||
u32 reserved02[0x3f];
|
||||
Semaphore semaphore;
|
||||
u32 reserved03[0x2];
|
||||
RenderEnable render_enable;
|
||||
PhysMode src_phys_mode;
|
||||
PhysMode dst_phys_mode;
|
||||
u32 reserved04[0x26];
|
||||
LaunchDMA launch_dma;
|
||||
u32 reserved05[0x3f];
|
||||
PackedGPUVAddr offset_in;
|
||||
PackedGPUVAddr offset_out;
|
||||
u32 pitch_in;
|
||||
u32 pitch_out;
|
||||
u32 line_length_in;
|
||||
u32 line_count;
|
||||
u32 reserved06[0xb8];
|
||||
RemapConst remap_const;
|
||||
Parameters dst_params;
|
||||
u32 reserved07[0x1];
|
||||
Parameters src_params;
|
||||
u32 reserved08[0x275];
|
||||
u32 pm_trigger_end;
|
||||
u32 reserved09[0x3ba];
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(exec, 0xC0);
|
||||
ASSERT_REG_POSITION(src_address, 0x100);
|
||||
ASSERT_REG_POSITION(dst_address, 0x102);
|
||||
ASSERT_REG_POSITION(src_pitch, 0x104);
|
||||
ASSERT_REG_POSITION(dst_pitch, 0x105);
|
||||
ASSERT_REG_POSITION(x_count, 0x106);
|
||||
ASSERT_REG_POSITION(y_count, 0x107);
|
||||
ASSERT_REG_POSITION(const0, 0x1C0);
|
||||
ASSERT_REG_POSITION(const1, 0x1C1);
|
||||
ASSERT_REG_POSITION(swizzle_config, 0x1C2);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
ASSERT_REG_POSITION(launch_dma, 0xC0);
|
||||
ASSERT_REG_POSITION(offset_in, 0x100);
|
||||
ASSERT_REG_POSITION(offset_out, 0x102);
|
||||
ASSERT_REG_POSITION(pitch_in, 0x104);
|
||||
ASSERT_REG_POSITION(pitch_out, 0x105);
|
||||
ASSERT_REG_POSITION(line_length_in, 0x106);
|
||||
ASSERT_REG_POSITION(line_count, 0x107);
|
||||
ASSERT_REG_POSITION(remap_const, 0x1C0);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
};
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -157,7 +157,7 @@ u64 GPU::GetTicks() const {
|
||||
constexpr u64 gpu_ticks_den = 625;
|
||||
|
||||
u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count();
|
||||
if (Settings::values.use_fast_gpu_time) {
|
||||
if (Settings::values.use_fast_gpu_time.GetValue()) {
|
||||
nanoseconds /= 256;
|
||||
}
|
||||
const u64 nanoseconds_num = nanoseconds / gpu_ticks_den;
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
}
|
||||
|
||||
query->BindCounter(Stream(type).Current(), timestamp);
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
|
||||
AsyncFlushQuery(cpu_addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ RendererBase::~RendererBase() = default;
|
||||
void RendererBase::RefreshBaseSettings() {
|
||||
UpdateCurrentFramebufferLayout();
|
||||
|
||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit;
|
||||
renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue();
|
||||
renderer_settings.set_background_color = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -229,15 +229,15 @@ Device::Device()
|
||||
// uniform buffers as "push constants"
|
||||
has_fast_buffer_sub_data = is_nvidia && !disable_fast_buffer_sub_data;
|
||||
|
||||
use_assembly_shaders = Settings::values.use_assembly_shaders && GLAD_GL_NV_gpu_program5 &&
|
||||
GLAD_GL_NV_compute_program5 && GLAD_GL_NV_transform_feedback &&
|
||||
GLAD_GL_NV_transform_feedback2;
|
||||
use_assembly_shaders = Settings::values.use_assembly_shaders.GetValue() &&
|
||||
GLAD_GL_NV_gpu_program5 && GLAD_GL_NV_compute_program5 &&
|
||||
GLAD_GL_NV_transform_feedback && GLAD_GL_NV_transform_feedback2;
|
||||
|
||||
LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_ComponentIndexingBug: {}", has_component_indexing_bug);
|
||||
LOG_INFO(Render_OpenGL, "Renderer_PreciseBug: {}", has_precise_bug);
|
||||
|
||||
if (Settings::values.use_assembly_shaders && !use_assembly_shaders) {
|
||||
if (Settings::values.use_assembly_shaders.GetValue() && !use_assembly_shaders) {
|
||||
LOG_ERROR(Render_OpenGL, "Assembly shaders enabled but not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
|
||||
std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
|
||||
// Skip games without title id
|
||||
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
|
||||
if (!Settings::values.use_disk_shader_cache || !has_title_id) {
|
||||
if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -455,8 +455,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
||||
void RendererOpenGL::InitOpenGLObjects() {
|
||||
frame_mailbox = std::make_unique<FrameMailbox>();
|
||||
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue(), 0.0f);
|
||||
|
||||
// Create shader programs
|
||||
OGLShader vertex_shader;
|
||||
@@ -561,8 +561,8 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
|
||||
Settings::values.bg_blue.GetValue(), 0.0f);
|
||||
}
|
||||
|
||||
// Set projection matrix
|
||||
|
||||
@@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept {
|
||||
raw = 0;
|
||||
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
|
||||
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
|
||||
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
|
||||
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
|
||||
if (regs.stencil_two_side_enable) {
|
||||
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
|
||||
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
|
||||
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
|
||||
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
|
||||
} else {
|
||||
back.action_stencil_fail.Assign(front.action_stencil_fail);
|
||||
back.action_depth_fail.Assign(front.action_depth_fail);
|
||||
back.action_depth_pass.Assign(front.action_depth_pass);
|
||||
back.test_func.Assign(front.test_func);
|
||||
}
|
||||
depth_test_enable.Assign(regs.depth_test_enable);
|
||||
depth_write_enable.Assign(regs.depth_write_enabled);
|
||||
depth_bounds_enable.Assign(regs.depth_bounds_enable);
|
||||
stencil_enable.Assign(regs.stencil_enable);
|
||||
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
|
||||
}
|
||||
|
||||
void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
|
||||
void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
|
||||
const auto& clip = regs.view_volume_clip_control;
|
||||
const std::array enabled_lut = {regs.polygon_offset_point_enable,
|
||||
regs.polygon_offset_line_enable,
|
||||
regs.polygon_offset_fill_enable};
|
||||
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
|
||||
|
||||
u32 packed_front_face = PackFrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
// Flip front face
|
||||
packed_front_face = 1 - packed_front_face;
|
||||
}
|
||||
|
||||
raw = 0;
|
||||
topology.Assign(topology_index);
|
||||
primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
|
||||
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
|
||||
depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
|
||||
depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
|
||||
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
|
||||
cull_face.Assign(PackCullFace(regs.cull_face));
|
||||
front_face.Assign(packed_front_face);
|
||||
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
|
||||
patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
|
||||
tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
|
||||
@@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
|
||||
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
|
||||
logic_op.Assign(PackLogicOp(regs.logic_op.operation));
|
||||
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
|
||||
std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
|
||||
}
|
||||
|
||||
void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept {
|
||||
std::memcpy(&point_size, ®s.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
binding_divisors[index] =
|
||||
regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
||||
const auto& input = regs.vertex_attrib_format[index];
|
||||
auto& attribute = attributes[index];
|
||||
attribute.raw = 0;
|
||||
attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
|
||||
attribute.buffer.Assign(input.buffer);
|
||||
attribute.offset.Assign(input.offset);
|
||||
attribute.type.Assign(static_cast<u32>(input.type.Value()));
|
||||
attribute.size.Assign(static_cast<u32>(input.size.Value()));
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < std::size(attachments); ++index) {
|
||||
attachments[index].Fill(regs, index);
|
||||
}
|
||||
}
|
||||
|
||||
void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept {
|
||||
const auto& transform = regs.viewport_transform;
|
||||
std::transform(transform.begin(), transform.end(), swizzles.begin(),
|
||||
std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
|
||||
[](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
|
||||
|
||||
if (!has_extended_dynamic_state) {
|
||||
no_extended_dynamic_state.Assign(1);
|
||||
dynamic_state.Fill(regs);
|
||||
}
|
||||
}
|
||||
|
||||
void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
|
||||
@@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
|
||||
enable.Assign(1);
|
||||
}
|
||||
|
||||
void FixedPipelineState::Fill(const Maxwell& regs) {
|
||||
rasterizer.Fill(regs);
|
||||
depth_stencil.Fill(regs);
|
||||
color_blending.Fill(regs);
|
||||
viewport_swizzles.Fill(regs);
|
||||
void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
|
||||
const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
|
||||
u32 packed_front_face = PackFrontFace(regs.front_face);
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
// Flip front face
|
||||
packed_front_face = 1 - packed_front_face;
|
||||
}
|
||||
|
||||
raw1 = 0;
|
||||
raw2 = 0;
|
||||
front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
|
||||
front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
|
||||
front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
|
||||
front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
|
||||
if (regs.stencil_two_side_enable) {
|
||||
back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
|
||||
back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
|
||||
back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
|
||||
back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
|
||||
} else {
|
||||
back.action_stencil_fail.Assign(front.action_stencil_fail);
|
||||
back.action_depth_fail.Assign(front.action_depth_fail);
|
||||
back.action_depth_pass.Assign(front.action_depth_pass);
|
||||
back.test_func.Assign(front.test_func);
|
||||
}
|
||||
stencil_enable.Assign(regs.stencil_enable);
|
||||
depth_write_enable.Assign(regs.depth_write_enabled);
|
||||
depth_bounds_enable.Assign(regs.depth_bounds_enable);
|
||||
depth_test_enable.Assign(regs.depth_test_enable);
|
||||
front_face.Assign(packed_front_face);
|
||||
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
|
||||
topology.Assign(topology_index);
|
||||
cull_face.Assign(PackCullFace(regs.cull_face));
|
||||
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
|
||||
const auto& input = regs.vertex_array[index];
|
||||
VertexBinding& binding = vertex_bindings[index];
|
||||
binding.raw = 0;
|
||||
binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
|
||||
binding.stride.Assign(static_cast<u16>(input.stride.Value()));
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t FixedPipelineState::Hash() const noexcept {
|
||||
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
|
||||
const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
|
||||
return static_cast<std::size_t>(hash);
|
||||
}
|
||||
|
||||
bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
|
||||
return std::memcmp(this, &rhs, sizeof *this) == 0;
|
||||
return std::memcmp(this, &rhs, Size()) == 0;
|
||||
}
|
||||
|
||||
u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {
|
||||
|
||||
@@ -60,14 +60,6 @@ struct FixedPipelineState {
|
||||
|
||||
void Fill(const Maxwell& regs, std::size_t index);
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
bool operator==(const BlendingAttachment& rhs) const noexcept;
|
||||
|
||||
bool operator!=(const BlendingAttachment& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
constexpr std::array<bool, 4> Mask() const noexcept {
|
||||
return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
|
||||
}
|
||||
@@ -97,83 +89,77 @@ struct FixedPipelineState {
|
||||
}
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
union Binding {
|
||||
u16 raw;
|
||||
BitField<0, 1, u16> enabled;
|
||||
BitField<1, 12, u16> stride;
|
||||
};
|
||||
union VertexAttribute {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> enabled;
|
||||
BitField<1, 5, u32> buffer;
|
||||
BitField<6, 14, u32> offset;
|
||||
BitField<20, 3, u32> type;
|
||||
BitField<23, 6, u32> size;
|
||||
|
||||
union Attribute {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> enabled;
|
||||
BitField<1, 5, u32> buffer;
|
||||
BitField<6, 14, u32> offset;
|
||||
BitField<20, 3, u32> type;
|
||||
BitField<23, 6, u32> size;
|
||||
|
||||
constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
|
||||
}
|
||||
|
||||
constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
|
||||
}
|
||||
};
|
||||
|
||||
std::array<Binding, Maxwell::NumVertexArrays> bindings;
|
||||
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
|
||||
std::array<Attribute, Maxwell::NumVertexAttributes> attributes;
|
||||
|
||||
void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept {
|
||||
auto& binding = bindings[index];
|
||||
binding.raw = 0;
|
||||
binding.enabled.Assign(enabled ? 1 : 0);
|
||||
binding.stride.Assign(static_cast<u16>(stride));
|
||||
binding_divisors[index] = divisor;
|
||||
constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
|
||||
}
|
||||
|
||||
void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset,
|
||||
Maxwell::VertexAttribute::Type type,
|
||||
Maxwell::VertexAttribute::Size size) noexcept {
|
||||
auto& attribute = attributes[index];
|
||||
attribute.raw = 0;
|
||||
attribute.enabled.Assign(enabled ? 1 : 0);
|
||||
attribute.buffer.Assign(buffer);
|
||||
attribute.offset.Assign(offset);
|
||||
attribute.type.Assign(static_cast<u32>(type));
|
||||
attribute.size.Assign(static_cast<u32>(size));
|
||||
constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
|
||||
return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct Rasterizer {
|
||||
template <std::size_t Position>
|
||||
union StencilFace {
|
||||
BitField<Position + 0, 3, u32> action_stencil_fail;
|
||||
BitField<Position + 3, 3, u32> action_depth_fail;
|
||||
BitField<Position + 6, 3, u32> action_depth_pass;
|
||||
BitField<Position + 9, 3, u32> test_func;
|
||||
|
||||
Maxwell::StencilOp ActionStencilFail() const noexcept {
|
||||
return UnpackStencilOp(action_stencil_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthFail() const noexcept {
|
||||
return UnpackStencilOp(action_depth_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthPass() const noexcept {
|
||||
return UnpackStencilOp(action_depth_pass);
|
||||
}
|
||||
|
||||
Maxwell::ComparisonOp TestFunc() const noexcept {
|
||||
return UnpackComparisonOp(test_func);
|
||||
}
|
||||
};
|
||||
|
||||
union VertexBinding {
|
||||
u16 raw;
|
||||
BitField<0, 12, u16> stride;
|
||||
BitField<12, 1, u16> enabled;
|
||||
};
|
||||
|
||||
struct DynamicState {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 4, u32> topology;
|
||||
BitField<4, 1, u32> primitive_restart_enable;
|
||||
BitField<5, 1, u32> cull_enable;
|
||||
BitField<6, 1, u32> depth_bias_enable;
|
||||
BitField<7, 1, u32> depth_clamp_disabled;
|
||||
BitField<8, 1, u32> ndc_minus_one_to_one;
|
||||
BitField<9, 2, u32> cull_face;
|
||||
BitField<11, 1, u32> front_face;
|
||||
BitField<12, 2, u32> polygon_mode;
|
||||
BitField<14, 5, u32> patch_control_points_minus_one;
|
||||
BitField<19, 2, u32> tessellation_primitive;
|
||||
BitField<21, 2, u32> tessellation_spacing;
|
||||
BitField<23, 1, u32> tessellation_clockwise;
|
||||
BitField<24, 1, u32> logic_op_enable;
|
||||
BitField<25, 4, u32> logic_op;
|
||||
BitField<29, 1, u32> rasterize_enable;
|
||||
u32 raw1;
|
||||
StencilFace<0> front;
|
||||
StencilFace<12> back;
|
||||
BitField<24, 1, u32> stencil_enable;
|
||||
BitField<25, 1, u32> depth_write_enable;
|
||||
BitField<26, 1, u32> depth_bounds_enable;
|
||||
BitField<27, 1, u32> depth_test_enable;
|
||||
BitField<28, 1, u32> front_face;
|
||||
BitField<29, 3, u32> depth_test_func;
|
||||
};
|
||||
union {
|
||||
u32 raw2;
|
||||
BitField<0, 4, u32> topology;
|
||||
BitField<4, 2, u32> cull_face;
|
||||
BitField<6, 1, u32> cull_enable;
|
||||
};
|
||||
std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
|
||||
|
||||
// TODO(Rodrigo): Move this to push constants
|
||||
u32 point_size;
|
||||
void Fill(const Maxwell& regs);
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
|
||||
constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
|
||||
return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
|
||||
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
|
||||
return UnpackComparisonOp(depth_test_func);
|
||||
}
|
||||
|
||||
Maxwell::CullFace CullFace() const noexcept {
|
||||
@@ -183,70 +169,36 @@ struct FixedPipelineState {
|
||||
Maxwell::FrontFace FrontFace() const noexcept {
|
||||
return UnpackFrontFace(front_face.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthStencil {
|
||||
template <std::size_t Position>
|
||||
union StencilFace {
|
||||
BitField<Position + 0, 3, u32> action_stencil_fail;
|
||||
BitField<Position + 3, 3, u32> action_depth_fail;
|
||||
BitField<Position + 6, 3, u32> action_depth_pass;
|
||||
BitField<Position + 9, 3, u32> test_func;
|
||||
|
||||
Maxwell::StencilOp ActionStencilFail() const noexcept {
|
||||
return UnpackStencilOp(action_stencil_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthFail() const noexcept {
|
||||
return UnpackStencilOp(action_depth_fail);
|
||||
}
|
||||
|
||||
Maxwell::StencilOp ActionDepthPass() const noexcept {
|
||||
return UnpackStencilOp(action_depth_pass);
|
||||
}
|
||||
|
||||
Maxwell::ComparisonOp TestFunc() const noexcept {
|
||||
return UnpackComparisonOp(test_func);
|
||||
}
|
||||
};
|
||||
|
||||
union {
|
||||
u32 raw;
|
||||
StencilFace<0> front;
|
||||
StencilFace<12> back;
|
||||
BitField<24, 1, u32> depth_test_enable;
|
||||
BitField<25, 1, u32> depth_write_enable;
|
||||
BitField<26, 1, u32> depth_bounds_enable;
|
||||
BitField<27, 1, u32> stencil_enable;
|
||||
BitField<28, 3, u32> depth_test_func;
|
||||
};
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
|
||||
Maxwell::ComparisonOp DepthTestFunc() const noexcept {
|
||||
return UnpackComparisonOp(depth_test_func);
|
||||
constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
|
||||
return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
|
||||
}
|
||||
};
|
||||
|
||||
struct ColorBlending {
|
||||
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> no_extended_dynamic_state;
|
||||
BitField<2, 1, u32> primitive_restart_enable;
|
||||
BitField<3, 1, u32> depth_bias_enable;
|
||||
BitField<4, 1, u32> depth_clamp_disabled;
|
||||
BitField<5, 1, u32> ndc_minus_one_to_one;
|
||||
BitField<6, 2, u32> polygon_mode;
|
||||
BitField<8, 5, u32> patch_control_points_minus_one;
|
||||
BitField<13, 2, u32> tessellation_primitive;
|
||||
BitField<15, 2, u32> tessellation_spacing;
|
||||
BitField<17, 1, u32> tessellation_clockwise;
|
||||
BitField<18, 1, u32> logic_op_enable;
|
||||
BitField<19, 4, u32> logic_op;
|
||||
BitField<23, 1, u32> rasterize_enable;
|
||||
};
|
||||
u32 point_size;
|
||||
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
|
||||
std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
|
||||
std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
|
||||
std::array<u16, Maxwell::NumViewports> viewport_swizzles;
|
||||
DynamicState dynamic_state;
|
||||
|
||||
struct ViewportSwizzles {
|
||||
std::array<u16, Maxwell::NumViewports> swizzles;
|
||||
|
||||
void Fill(const Maxwell& regs) noexcept;
|
||||
};
|
||||
|
||||
VertexInput vertex_input;
|
||||
Rasterizer rasterizer;
|
||||
DepthStencil depth_stencil;
|
||||
ColorBlending color_blending;
|
||||
ViewportSwizzles viewport_swizzles;
|
||||
|
||||
void Fill(const Maxwell& regs);
|
||||
void Fill(const Maxwell& regs, bool has_extended_dynamic_state);
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
@@ -255,6 +207,11 @@ struct FixedPipelineState {
|
||||
bool operator!=(const FixedPipelineState& rhs) const noexcept {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
std::size_t Size() const noexcept {
|
||||
const std::size_t total_size = sizeof *this;
|
||||
return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
|
||||
}
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
|
||||
static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user