diff options
Diffstat (limited to 'src')
144 files changed, 2173 insertions, 1790 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e977e8a8..71efbb40d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -62,6 +62,12 @@ else() | |||
| 62 | -Wno-unused-parameter | 62 | -Wno-unused-parameter |
| 63 | ) | 63 | ) |
| 64 | 64 | ||
| 65 | # TODO: Remove when we update to a GCC compiler that enables this | ||
| 66 | # by default (i.e. GCC 10 or newer). | ||
| 67 | if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) | ||
| 68 | add_compile_options(-fconcepts) | ||
| 69 | endif() | ||
| 70 | |||
| 65 | if (ARCHITECTURE_x86_64) | 71 | if (ARCHITECTURE_x86_64) |
| 66 | add_compile_options("-mcx16") | 72 | add_compile_options("-mcx16") |
| 67 | endif() | 73 | endif() |
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 41bf5cd4d..c27df946c 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp | |||
| @@ -78,7 +78,7 @@ public: | |||
| 78 | const s16 surround_left{samples[i + 4]}; | 78 | const s16 surround_left{samples[i + 4]}; |
| 79 | const s16 surround_right{samples[i + 5]}; | 79 | const s16 surround_right{samples[i + 5]}; |
| 80 | // Not used in the ATSC reference implementation | 80 | // Not used in the ATSC reference implementation |
| 81 | [[maybe_unused]] const s16 low_frequency_effects { samples[i + 3] }; | 81 | [[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]}; |
| 82 | 82 | ||
| 83 | constexpr s32 clev{707}; // center mixing level coefficient | 83 | constexpr s32 clev{707}; // center mixing level coefficient |
| 84 | constexpr s32 slev{707}; // surround mixing level coefficient | 84 | constexpr s32 slev{707}; // surround mixing level coefficient |
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index f80ab92e4..7be5d5087 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp | |||
| @@ -36,9 +36,10 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo | |||
| 36 | ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_) | 36 | ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_) |
| 37 | : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, | 37 | : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, |
| 38 | sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { | 38 | sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { |
| 39 | 39 | release_event = | |
| 40 | release_event = Core::Timing::CreateEvent( | 40 | Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { |
| 41 | name, [this](u64, std::chrono::nanoseconds ns_late) { ReleaseActiveBuffer(ns_late); }); | 41 | ReleaseActiveBuffer(ns_late); |
| 42 | }); | ||
| 42 | } | 43 | } |
| 43 | 44 | ||
| 44 | void Stream::Play() { | 45 | void Stream::Play() { |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d120c8d3d..78c3bfb3b 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -110,6 +110,7 @@ add_library(common STATIC | |||
| 110 | common_funcs.h | 110 | common_funcs.h |
| 111 | common_paths.h | 111 | common_paths.h |
| 112 | common_types.h | 112 | common_types.h |
| 113 | concepts.h | ||
| 113 | dynamic_library.cpp | 114 | dynamic_library.cpp |
| 114 | dynamic_library.h | 115 | dynamic_library.h |
| 115 | fiber.cpp | 116 | fiber.cpp |
diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp index 1098e21ff..1612d0e67 100644 --- a/src/common/atomic_ops.cpp +++ b/src/common/atomic_ops.cpp | |||
| @@ -14,50 +14,55 @@ namespace Common { | |||
| 14 | 14 | ||
| 15 | #if _MSC_VER | 15 | #if _MSC_VER |
| 16 | 16 | ||
| 17 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { | 17 | bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { |
| 18 | u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected); | 18 | const u8 result = |
| 19 | _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected); | ||
| 19 | return result == expected; | 20 | return result == expected; |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { | 23 | bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { |
| 23 | u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected); | 24 | const u16 result = |
| 25 | _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected); | ||
| 24 | return result == expected; | 26 | return result == expected; |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { | 29 | bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) { |
| 28 | u32 result = _InterlockedCompareExchange((long*)pointer, value, expected); | 30 | const u32 result = |
| 31 | _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected); | ||
| 29 | return result == expected; | 32 | return result == expected; |
| 30 | } | 33 | } |
| 31 | 34 | ||
| 32 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { | 35 | bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) { |
| 33 | u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected); | 36 | const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), |
| 37 | value, expected); | ||
| 34 | return result == expected; | 38 | return result == expected; |
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { | 41 | bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) { |
| 38 | return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], | 42 | return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1], |
| 39 | (__int64*)expected.data()) != 0; | 43 | value[0], |
| 44 | reinterpret_cast<__int64*>(expected.data())) != 0; | ||
| 40 | } | 45 | } |
| 41 | 46 | ||
| 42 | #else | 47 | #else |
| 43 | 48 | ||
| 44 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { | 49 | bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) { |
| 45 | return __sync_bool_compare_and_swap(pointer, expected, value); | 50 | return __sync_bool_compare_and_swap(pointer, expected, value); |
| 46 | } | 51 | } |
| 47 | 52 | ||
| 48 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { | 53 | bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) { |
| 49 | return __sync_bool_compare_and_swap(pointer, expected, value); | 54 | return __sync_bool_compare_and_swap(pointer, expected, value); |
| 50 | } | 55 | } |
| 51 | 56 | ||
| 52 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { | 57 | bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) { |
| 53 | return __sync_bool_compare_and_swap(pointer, expected, value); | 58 | return __sync_bool_compare_and_swap(pointer, expected, value); |
| 54 | } | 59 | } |
| 55 | 60 | ||
| 56 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { | 61 | bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) { |
| 57 | return __sync_bool_compare_and_swap(pointer, expected, value); | 62 | return __sync_bool_compare_and_swap(pointer, expected, value); |
| 58 | } | 63 | } |
| 59 | 64 | ||
| 60 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { | 65 | bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) { |
| 61 | unsigned __int128 value_a; | 66 | unsigned __int128 value_a; |
| 62 | unsigned __int128 expected_a; | 67 | unsigned __int128 expected_a; |
| 63 | std::memcpy(&value_a, value.data(), sizeof(u128)); | 68 | std::memcpy(&value_a, value.data(), sizeof(u128)); |
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h index e6181d521..8d6b73c00 100644 --- a/src/common/atomic_ops.h +++ b/src/common/atomic_ops.h | |||
| @@ -8,10 +8,10 @@ | |||
| 8 | 8 | ||
| 9 | namespace Common { | 9 | namespace Common { |
| 10 | 10 | ||
| 11 | bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected); | 11 | bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected); |
| 12 | bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected); | 12 | bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected); |
| 13 | bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected); | 13 | bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected); |
| 14 | bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected); | 14 | bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected); |
| 15 | bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected); | 15 | bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected); |
| 16 | 16 | ||
| 17 | } // namespace Common | 17 | } // namespace Common |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 076752d3b..3c593d5f6 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #define KEYS_DIR "keys" | 35 | #define KEYS_DIR "keys" |
| 36 | #define LOAD_DIR "load" | 36 | #define LOAD_DIR "load" |
| 37 | #define DUMP_DIR "dump" | 37 | #define DUMP_DIR "dump" |
| 38 | #define SCREENSHOTS_DIR "screenshots" | ||
| 38 | #define SHADER_DIR "shader" | 39 | #define SHADER_DIR "shader" |
| 39 | #define LOG_DIR "log" | 40 | #define LOG_DIR "log" |
| 40 | 41 | ||
diff --git a/src/common/concepts.h b/src/common/concepts.h new file mode 100644 index 000000000..54252e778 --- /dev/null +++ b/src/common/concepts.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace Common { | ||
| 8 | |||
| 9 | #include <type_traits> | ||
| 10 | |||
| 11 | // Check if type is like an STL container | ||
| 12 | template <typename T> | ||
| 13 | concept IsSTLContainer = requires(T t) { | ||
| 14 | typename T::value_type; | ||
| 15 | typename T::iterator; | ||
| 16 | typename T::const_iterator; | ||
| 17 | // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it. | ||
| 18 | t.begin(); | ||
| 19 | t.end(); | ||
| 20 | t.cbegin(); | ||
| 21 | t.cend(); | ||
| 22 | t.data(); | ||
| 23 | t.size(); | ||
| 24 | }; | ||
| 25 | |||
| 26 | // TODO: Replace with std::derived_from when the <concepts> header | ||
| 27 | // is available on all supported platforms. | ||
| 28 | template <typename Derived, typename Base> | ||
| 29 | concept DerivedFrom = requires { | ||
| 30 | std::is_base_of_v<Base, Derived>; | ||
| 31 | std::is_convertible_v<const volatile Derived*, const volatile Base*>; | ||
| 32 | }; | ||
| 33 | |||
| 34 | } // namespace Common | ||
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp index f268d6021..f2b4939df 100644 --- a/src/common/detached_tasks.cpp +++ b/src/common/detached_tasks.cpp | |||
| @@ -34,8 +34,7 @@ void DetachedTasks::AddTask(std::function<void()> task) { | |||
| 34 | std::unique_lock lock{instance->mutex}; | 34 | std::unique_lock lock{instance->mutex}; |
| 35 | --instance->count; | 35 | --instance->count; |
| 36 | std::notify_all_at_thread_exit(instance->cv, std::move(lock)); | 36 | std::notify_all_at_thread_exit(instance->cv, std::move(lock)); |
| 37 | }) | 37 | }).detach(); |
| 38 | .detach(); | ||
| 39 | } | 38 | } |
| 40 | 39 | ||
| 41 | } // namespace Common | 40 | } // namespace Common |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 45b750e1e..4ede9f72c 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -695,6 +695,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { | |||
| 695 | paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); | 695 | paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); |
| 696 | paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); | 696 | paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); |
| 697 | paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP); | 697 | paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP); |
| 698 | paths.emplace(UserPath::ScreenshotsDir, user_path + SCREENSHOTS_DIR DIR_SEP); | ||
| 698 | paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP); | 699 | paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP); |
| 699 | paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); | 700 | paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); |
| 700 | paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); | 701 | paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); |
diff --git a/src/common/file_util.h b/src/common/file_util.h index f7a0c33fa..187b93161 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -32,6 +32,7 @@ enum class UserPath { | |||
| 32 | SDMCDir, | 32 | SDMCDir, |
| 33 | LoadDir, | 33 | LoadDir, |
| 34 | DumpDir, | 34 | DumpDir, |
| 35 | ScreenshotsDir, | ||
| 35 | ShaderDir, | 36 | ShaderDir, |
| 36 | SysDataDir, | 37 | SysDataDir, |
| 37 | UserDir, | 38 | UserDir, |
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp index c2f6cf0f6..74f52dd11 100644 --- a/src/common/hex_util.cpp +++ b/src/common/hex_util.cpp | |||
| @@ -3,21 +3,9 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/hex_util.h" | 5 | #include "common/hex_util.h" |
| 6 | #include "common/logging/log.h" | ||
| 7 | 6 | ||
| 8 | namespace Common { | 7 | namespace Common { |
| 9 | 8 | ||
| 10 | u8 ToHexNibble(char c1) { | ||
| 11 | if (c1 >= 65 && c1 <= 70) | ||
| 12 | return c1 - 55; | ||
| 13 | if (c1 >= 97 && c1 <= 102) | ||
| 14 | return c1 - 87; | ||
| 15 | if (c1 >= 48 && c1 <= 57) | ||
| 16 | return c1 - 48; | ||
| 17 | LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1); | ||
| 18 | return 0; | ||
| 19 | } | ||
| 20 | |||
| 21 | std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) { | 9 | std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) { |
| 22 | std::vector<u8> out(str.size() / 2); | 10 | std::vector<u8> out(str.size() / 2); |
| 23 | if (little_endian) { | 11 | if (little_endian) { |
| @@ -30,26 +18,4 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) { | |||
| 30 | return out; | 18 | return out; |
| 31 | } | 19 | } |
| 32 | 20 | ||
| 33 | std::array<u8, 16> operator""_array16(const char* str, std::size_t len) { | ||
| 34 | if (len != 32) { | ||
| 35 | LOG_ERROR(Common, | ||
| 36 | "Attempting to parse string to array that is not of correct size (expected=32, " | ||
| 37 | "actual={}).", | ||
| 38 | len); | ||
| 39 | return {}; | ||
| 40 | } | ||
| 41 | return HexStringToArray<16>(str); | ||
| 42 | } | ||
| 43 | |||
| 44 | std::array<u8, 32> operator""_array32(const char* str, std::size_t len) { | ||
| 45 | if (len != 64) { | ||
| 46 | LOG_ERROR(Common, | ||
| 47 | "Attempting to parse string to array that is not of correct size (expected=64, " | ||
| 48 | "actual={}).", | ||
| 49 | len); | ||
| 50 | return {}; | ||
| 51 | } | ||
| 52 | return HexStringToArray<32>(str); | ||
| 53 | } | ||
| 54 | |||
| 55 | } // namespace Common | 21 | } // namespace Common |
diff --git a/src/common/hex_util.h b/src/common/hex_util.h index bb4736f96..a0a0e78a4 100644 --- a/src/common/hex_util.h +++ b/src/common/hex_util.h | |||
| @@ -14,19 +14,31 @@ | |||
| 14 | 14 | ||
| 15 | namespace Common { | 15 | namespace Common { |
| 16 | 16 | ||
| 17 | u8 ToHexNibble(char c1); | 17 | constexpr u8 ToHexNibble(char c) { |
| 18 | if (c >= 65 && c <= 70) { | ||
| 19 | return c - 55; | ||
| 20 | } | ||
| 21 | |||
| 22 | if (c >= 97 && c <= 102) { | ||
| 23 | return c - 87; | ||
| 24 | } | ||
| 25 | |||
| 26 | return c - 48; | ||
| 27 | } | ||
| 18 | 28 | ||
| 19 | std::vector<u8> HexStringToVector(std::string_view str, bool little_endian); | 29 | std::vector<u8> HexStringToVector(std::string_view str, bool little_endian); |
| 20 | 30 | ||
| 21 | template <std::size_t Size, bool le = false> | 31 | template <std::size_t Size, bool le = false> |
| 22 | std::array<u8, Size> HexStringToArray(std::string_view str) { | 32 | constexpr std::array<u8, Size> HexStringToArray(std::string_view str) { |
| 23 | std::array<u8, Size> out{}; | 33 | std::array<u8, Size> out{}; |
| 24 | if constexpr (le) { | 34 | if constexpr (le) { |
| 25 | for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) | 35 | for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) { |
| 26 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); | 36 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); |
| 37 | } | ||
| 27 | } else { | 38 | } else { |
| 28 | for (std::size_t i = 0; i < 2 * Size; i += 2) | 39 | for (std::size_t i = 0; i < 2 * Size; i += 2) { |
| 29 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); | 40 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); |
| 41 | } | ||
| 30 | } | 42 | } |
| 31 | return out; | 43 | return out; |
| 32 | } | 44 | } |
| @@ -48,7 +60,12 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) { | |||
| 48 | return out; | 60 | return out; |
| 49 | } | 61 | } |
| 50 | 62 | ||
| 51 | std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len); | 63 | constexpr std::array<u8, 16> AsArray(const char (&data)[17]) { |
| 52 | std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len); | 64 | return HexStringToArray<16>(data); |
| 65 | } | ||
| 66 | |||
| 67 | constexpr std::array<u8, 32> AsArray(const char (&data)[65]) { | ||
| 68 | return HexStringToArray<32>(data); | ||
| 69 | } | ||
| 53 | 70 | ||
| 54 | } // namespace Common | 71 | } // namespace Common |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 04bc3128f..62cfde397 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -113,19 +113,19 @@ private: | |||
| 113 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, | 113 | Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, |
| 114 | const char* function, std::string message) const { | 114 | const char* function, std::string message) const { |
| 115 | using std::chrono::duration_cast; | 115 | using std::chrono::duration_cast; |
| 116 | using std::chrono::microseconds; | ||
| 116 | using std::chrono::steady_clock; | 117 | using std::chrono::steady_clock; |
| 117 | 118 | ||
| 118 | Entry entry; | 119 | return { |
| 119 | entry.timestamp = | 120 | .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin), |
| 120 | duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); | 121 | .log_class = log_class, |
| 121 | entry.log_class = log_class; | 122 | .log_level = log_level, |
| 122 | entry.log_level = log_level; | 123 | .filename = filename, |
| 123 | entry.filename = filename; | 124 | .line_num = line_nr, |
| 124 | entry.line_num = line_nr; | 125 | .function = function, |
| 125 | entry.function = function; | 126 | .message = std::move(message), |
| 126 | entry.message = std::move(message); | 127 | .final_entry = false, |
| 127 | 128 | }; | |
| 128 | return entry; | ||
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | std::mutex writing_mutex; | 131 | std::mutex writing_mutex; |
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index fc338c70d..e5d702568 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -21,19 +21,13 @@ class Filter; | |||
| 21 | */ | 21 | */ |
| 22 | struct Entry { | 22 | struct Entry { |
| 23 | std::chrono::microseconds timestamp; | 23 | std::chrono::microseconds timestamp; |
| 24 | Class log_class; | 24 | Class log_class{}; |
| 25 | Level log_level; | 25 | Level log_level{}; |
| 26 | const char* filename; | 26 | const char* filename = nullptr; |
| 27 | unsigned int line_num; | 27 | unsigned int line_num = 0; |
| 28 | std::string function; | 28 | std::string function; |
| 29 | std::string message; | 29 | std::string message; |
| 30 | bool final_entry = false; | 30 | bool final_entry = false; |
| 31 | |||
| 32 | Entry() = default; | ||
| 33 | Entry(Entry&& o) = default; | ||
| 34 | |||
| 35 | Entry& operator=(Entry&& o) = default; | ||
| 36 | Entry& operator=(const Entry& o) = default; | ||
| 37 | }; | 31 | }; |
| 38 | 32 | ||
| 39 | /** | 33 | /** |
diff --git a/src/common/math_util.h b/src/common/math_util.h index 83ef0201f..abca3177c 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h | |||
| @@ -54,6 +54,6 @@ struct Rectangle { | |||
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | template <typename T> | 56 | template <typename T> |
| 57 | Rectangle(T, T, T, T)->Rectangle<T>; | 57 | Rectangle(T, T, T, T) -> Rectangle<T>; |
| 58 | 58 | ||
| 59 | } // namespace Common | 59 | } // namespace Common |
diff --git a/src/common/string_util.h b/src/common/string_util.h index 583fd05e6..023dff5dc 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -74,16 +74,4 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t | |||
| 74 | std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, | 74 | std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, |
| 75 | std::size_t max_len); | 75 | std::size_t max_len); |
| 76 | 76 | ||
| 77 | /** | ||
| 78 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's | ||
| 79 | * intended to be used to strip a system-specific build directory from the `__FILE__` macro, | ||
| 80 | * leaving only the path relative to the sources root. | ||
| 81 | * | ||
| 82 | * @param path The input file path as a null-terminated string | ||
| 83 | * @param root The name of the root source directory as a null-terminated string. Path up to and | ||
| 84 | * including the last occurrence of this name will be stripped | ||
| 85 | * @return A pointer to the same string passed as `path`, but starting at the trimmed portion | ||
| 86 | */ | ||
| 87 | const char* TrimSourcePath(const char* path, const char* root = "src"); | ||
| 88 | |||
| 89 | } // namespace Common | 77 | } // namespace Common |
diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp index b426f4747..b009cb500 100644 --- a/src/common/virtual_buffer.cpp +++ b/src/common/virtual_buffer.cpp | |||
| @@ -5,16 +5,7 @@ | |||
| 5 | #ifdef _WIN32 | 5 | #ifdef _WIN32 |
| 6 | #include <windows.h> | 6 | #include <windows.h> |
| 7 | #else | 7 | #else |
| 8 | #include <stdio.h> | ||
| 9 | #include <sys/mman.h> | 8 | #include <sys/mman.h> |
| 10 | #include <sys/types.h> | ||
| 11 | #if defined __APPLE__ || defined __FreeBSD__ || defined __OpenBSD__ | ||
| 12 | #include <sys/sysctl.h> | ||
| 13 | #elif defined __HAIKU__ | ||
| 14 | #include <OS.h> | ||
| 15 | #else | ||
| 16 | #include <sys/sysinfo.h> | ||
| 17 | #endif | ||
| 18 | #endif | 9 | #endif |
| 19 | 10 | ||
| 20 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| @@ -38,7 +29,7 @@ void* AllocateMemoryPages(std::size_t size) { | |||
| 38 | return base; | 29 | return base; |
| 39 | } | 30 | } |
| 40 | 31 | ||
| 41 | void FreeMemoryPages(void* base, std::size_t size) { | 32 | void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) { |
| 42 | if (!base) { | 33 | if (!base) { |
| 43 | return; | 34 | return; |
| 44 | } | 35 | } |
diff --git a/src/core/core.cpp b/src/core/core.cpp index e598c0e2b..42277e2cd 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -146,7 +146,7 @@ struct System::Impl { | |||
| 146 | ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { | 146 | ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { |
| 147 | LOG_DEBUG(HW_Memory, "initialized OK"); | 147 | LOG_DEBUG(HW_Memory, "initialized OK"); |
| 148 | 148 | ||
| 149 | device_memory = std::make_unique<Core::DeviceMemory>(system); | 149 | device_memory = std::make_unique<Core::DeviceMemory>(); |
| 150 | 150 | ||
| 151 | is_multicore = Settings::values.use_multi_core.GetValue(); | 151 | is_multicore = Settings::values.use_multi_core.GetValue(); |
| 152 | is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); | 152 | is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index b5feb3f24..71af26ec5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -23,7 +23,7 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac | |||
| 23 | struct CoreTiming::Event { | 23 | struct CoreTiming::Event { |
| 24 | u64 time; | 24 | u64 time; |
| 25 | u64 fifo_order; | 25 | u64 fifo_order; |
| 26 | u64 userdata; | 26 | std::uintptr_t user_data; |
| 27 | std::weak_ptr<EventType> type; | 27 | std::weak_ptr<EventType> type; |
| 28 | 28 | ||
| 29 | // Sort by time, unless the times are the same, in which case sort by | 29 | // Sort by time, unless the times are the same, in which case sort by |
| @@ -58,7 +58,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { | |||
| 58 | event_fifo_id = 0; | 58 | event_fifo_id = 0; |
| 59 | shutting_down = false; | 59 | shutting_down = false; |
| 60 | ticks = 0; | 60 | ticks = 0; |
| 61 | const auto empty_timed_callback = [](u64, std::chrono::nanoseconds) {}; | 61 | const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; |
| 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); | 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); |
| 63 | if (is_multicore) { | 63 | if (is_multicore) { |
| 64 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); | 64 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); |
| @@ -107,22 +107,24 @@ bool CoreTiming::HasPendingEvents() const { | |||
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, | 109 | void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, |
| 110 | const std::shared_ptr<EventType>& event_type, u64 userdata) { | 110 | const std::shared_ptr<EventType>& event_type, |
| 111 | std::uintptr_t user_data) { | ||
| 111 | { | 112 | { |
| 112 | std::scoped_lock scope{basic_lock}; | 113 | std::scoped_lock scope{basic_lock}; |
| 113 | const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); | 114 | const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); |
| 114 | 115 | ||
| 115 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | 116 | event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); |
| 116 | 117 | ||
| 117 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 118 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 118 | } | 119 | } |
| 119 | event.Set(); | 120 | event.Set(); |
| 120 | } | 121 | } |
| 121 | 122 | ||
| 122 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { | 123 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, |
| 124 | std::uintptr_t user_data) { | ||
| 123 | std::scoped_lock scope{basic_lock}; | 125 | std::scoped_lock scope{basic_lock}; |
| 124 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 126 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 125 | return e.type.lock().get() == event_type.get() && e.userdata == userdata; | 127 | return e.type.lock().get() == event_type.get() && e.user_data == user_data; |
| 126 | }); | 128 | }); |
| 127 | 129 | ||
| 128 | // Removing random items breaks the invariant so we have to re-establish it. | 130 | // Removing random items breaks the invariant so we have to re-establish it. |
| @@ -197,7 +199,7 @@ std::optional<s64> CoreTiming::Advance() { | |||
| 197 | 199 | ||
| 198 | if (const auto event_type{evt.type.lock()}) { | 200 | if (const auto event_type{evt.type.lock()}) { |
| 199 | event_type->callback( | 201 | event_type->callback( |
| 200 | evt.userdata, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); | 202 | evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); |
| 201 | } | 203 | } |
| 202 | 204 | ||
| 203 | basic_lock.lock(); | 205 | basic_lock.lock(); |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 120c74e46..b0b6036e4 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -22,7 +22,8 @@ | |||
| 22 | namespace Core::Timing { | 22 | namespace Core::Timing { |
| 23 | 23 | ||
| 24 | /// A callback that may be scheduled for a particular core timing event. | 24 | /// A callback that may be scheduled for a particular core timing event. |
| 25 | using TimedCallback = std::function<void(u64 userdata, std::chrono::nanoseconds ns_late)>; | 25 | using TimedCallback = |
| 26 | std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; | ||
| 26 | 27 | ||
| 27 | /// Contains the characteristics of a particular event. | 28 | /// Contains the characteristics of a particular event. |
| 28 | struct EventType { | 29 | struct EventType { |
| @@ -94,9 +95,9 @@ public: | |||
| 94 | 95 | ||
| 95 | /// Schedules an event in core timing | 96 | /// Schedules an event in core timing |
| 96 | void ScheduleEvent(std::chrono::nanoseconds ns_into_future, | 97 | void ScheduleEvent(std::chrono::nanoseconds ns_into_future, |
| 97 | const std::shared_ptr<EventType>& event_type, u64 userdata = 0); | 98 | const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); |
| 98 | 99 | ||
| 99 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); | 100 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); |
| 100 | 101 | ||
| 101 | /// We only permit one event of each type in the queue at a time. | 102 | /// We only permit one event of each type in the queue at a time. |
| 102 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); | 103 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); |
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 32afcf3ae..358943429 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -52,15 +52,15 @@ void CpuManager::Shutdown() { | |||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { | 54 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { |
| 55 | return std::function<void(void*)>(GuestThreadFunction); | 55 | return GuestThreadFunction; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { | 58 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { |
| 59 | return std::function<void(void*)>(IdleThreadFunction); | 59 | return IdleThreadFunction; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { | 62 | std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { |
| 63 | return std::function<void(void*)>(SuspendThreadFunction); | 63 | return SuspendThreadFunction; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { | 66 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { |
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp index 4be76bb43..330996b24 100644 --- a/src/core/crypto/aes_util.cpp +++ b/src/core/crypto/aes_util.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 5 | #include <mbedtls/cipher.h> | 6 | #include <mbedtls/cipher.h> |
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| @@ -10,8 +11,10 @@ | |||
| 10 | 11 | ||
| 11 | namespace Core::Crypto { | 12 | namespace Core::Crypto { |
| 12 | namespace { | 13 | namespace { |
| 13 | std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) { | 14 | using NintendoTweak = std::array<u8, 16>; |
| 14 | std::vector<u8> out(0x10); | 15 | |
| 16 | NintendoTweak CalculateNintendoTweak(std::size_t sector_id) { | ||
| 17 | NintendoTweak out{}; | ||
| 15 | for (std::size_t i = 0xF; i <= 0xF; --i) { | 18 | for (std::size_t i = 0xF; i <= 0xF; --i) { |
| 16 | out[i] = sector_id & 0xFF; | 19 | out[i] = sector_id & 0xFF; |
| 17 | sector_id >>= 8; | 20 | sector_id >>= 8; |
| @@ -64,13 +67,6 @@ AESCipher<Key, KeySize>::~AESCipher() { | |||
| 64 | } | 67 | } |
| 65 | 68 | ||
| 66 | template <typename Key, std::size_t KeySize> | 69 | template <typename Key, std::size_t KeySize> |
| 67 | void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { | ||
| 68 | ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || | ||
| 69 | mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, | ||
| 70 | "Failed to set IV on mbedtls ciphers."); | ||
| 71 | } | ||
| 72 | |||
| 73 | template <typename Key, std::size_t KeySize> | ||
| 74 | void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const { | 70 | void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const { |
| 75 | auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; | 71 | auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; |
| 76 | 72 | ||
| @@ -124,6 +120,13 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8* | |||
| 124 | } | 120 | } |
| 125 | } | 121 | } |
| 126 | 122 | ||
| 123 | template <typename Key, std::size_t KeySize> | ||
| 124 | void AESCipher<Key, KeySize>::SetIVImpl(const u8* data, std::size_t size) { | ||
| 125 | ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, data, size) || | ||
| 126 | mbedtls_cipher_set_iv(&ctx->decryption_context, data, size)) == 0, | ||
| 127 | "Failed to set IV on mbedtls ciphers."); | ||
| 128 | } | ||
| 129 | |||
| 127 | template class AESCipher<Key128>; | 130 | template class AESCipher<Key128>; |
| 128 | template class AESCipher<Key256>; | 131 | template class AESCipher<Key256>; |
| 129 | } // namespace Core::Crypto | 132 | } // namespace Core::Crypto |
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h index edc4ab910..e2a304186 100644 --- a/src/core/crypto/aes_util.h +++ b/src/core/crypto/aes_util.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <type_traits> | 8 | #include <type_traits> |
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/vfs.h" | 10 | #include "core/file_sys/vfs.h" |
| 12 | 11 | ||
| @@ -32,10 +31,12 @@ class AESCipher { | |||
| 32 | 31 | ||
| 33 | public: | 32 | public: |
| 34 | AESCipher(Key key, Mode mode); | 33 | AESCipher(Key key, Mode mode); |
| 35 | |||
| 36 | ~AESCipher(); | 34 | ~AESCipher(); |
| 37 | 35 | ||
| 38 | void SetIV(std::vector<u8> iv); | 36 | template <typename ContiguousContainer> |
| 37 | void SetIV(const ContiguousContainer& container) { | ||
| 38 | SetIVImpl(std::data(container), std::size(container)); | ||
| 39 | } | ||
| 39 | 40 | ||
| 40 | template <typename Source, typename Dest> | 41 | template <typename Source, typename Dest> |
| 41 | void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { | 42 | void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { |
| @@ -59,6 +60,8 @@ public: | |||
| 59 | std::size_t sector_size, Op op); | 60 | std::size_t sector_size, Op op); |
| 60 | 61 | ||
| 61 | private: | 62 | private: |
| 63 | void SetIVImpl(const u8* data, std::size_t size); | ||
| 64 | |||
| 62 | std::unique_ptr<CipherContext> ctx; | 65 | std::unique_ptr<CipherContext> ctx; |
| 63 | }; | 66 | }; |
| 64 | } // namespace Core::Crypto | 67 | } // namespace Core::Crypto |
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp index 902841c77..5c84bb0a4 100644 --- a/src/core/crypto/ctr_encryption_layer.cpp +++ b/src/core/crypto/ctr_encryption_layer.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "core/crypto/ctr_encryption_layer.h" | 8 | #include "core/crypto/ctr_encryption_layer.h" |
| @@ -10,8 +11,7 @@ namespace Core::Crypto { | |||
| 10 | 11 | ||
| 11 | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, | 12 | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, |
| 12 | std::size_t base_offset) | 13 | std::size_t base_offset) |
| 13 | : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), | 14 | : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR) {} |
| 14 | iv(16, 0) {} | ||
| 15 | 15 | ||
| 16 | std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { | 16 | std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 17 | if (length == 0) | 17 | if (length == 0) |
| @@ -39,9 +39,8 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o | |||
| 39 | return read + Read(data + read, length - read, offset + read); | 39 | return read + Read(data + read, length - read, offset + read); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { | 42 | void CTREncryptionLayer::SetIV(const IVData& iv_) { |
| 43 | const auto length = std::min(iv_.size(), iv.size()); | 43 | iv = iv_; |
| 44 | iv.assign(iv_.cbegin(), iv_.cbegin() + length); | ||
| 45 | } | 44 | } |
| 46 | 45 | ||
| 47 | void CTREncryptionLayer::UpdateIV(std::size_t offset) const { | 46 | void CTREncryptionLayer::UpdateIV(std::size_t offset) const { |
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h index a7bf810f4..a2429f001 100644 --- a/src/core/crypto/ctr_encryption_layer.h +++ b/src/core/crypto/ctr_encryption_layer.h | |||
| @@ -4,7 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <vector> | 7 | #include <array> |
| 8 | |||
| 8 | #include "core/crypto/aes_util.h" | 9 | #include "core/crypto/aes_util.h" |
| 9 | #include "core/crypto/encryption_layer.h" | 10 | #include "core/crypto/encryption_layer.h" |
| 10 | #include "core/crypto/key_manager.h" | 11 | #include "core/crypto/key_manager.h" |
| @@ -14,18 +15,20 @@ namespace Core::Crypto { | |||
| 14 | // Sits on top of a VirtualFile and provides CTR-mode AES decription. | 15 | // Sits on top of a VirtualFile and provides CTR-mode AES decription. |
| 15 | class CTREncryptionLayer : public EncryptionLayer { | 16 | class CTREncryptionLayer : public EncryptionLayer { |
| 16 | public: | 17 | public: |
| 18 | using IVData = std::array<u8, 16>; | ||
| 19 | |||
| 17 | CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset); | 20 | CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset); |
| 18 | 21 | ||
| 19 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; | 22 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 20 | 23 | ||
| 21 | void SetIV(const std::vector<u8>& iv); | 24 | void SetIV(const IVData& iv); |
| 22 | 25 | ||
| 23 | private: | 26 | private: |
| 24 | std::size_t base_offset; | 27 | std::size_t base_offset; |
| 25 | 28 | ||
| 26 | // Must be mutable as operations modify cipher contexts. | 29 | // Must be mutable as operations modify cipher contexts. |
| 27 | mutable AESCipher<Key128> cipher; | 30 | mutable AESCipher<Key128> cipher; |
| 28 | mutable std::vector<u8> iv; | 31 | mutable IVData iv{}; |
| 29 | 32 | ||
| 30 | void UpdateIV(std::size_t offset) const; | 33 | void UpdateIV(std::size_t offset) const; |
| 31 | }; | 34 | }; |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index f87fe0abc..c09f7ad41 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -40,12 +40,14 @@ namespace Core::Crypto { | |||
| 40 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; | 40 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; |
| 41 | constexpr u64 FULL_TICKET_SIZE = 0x400; | 41 | constexpr u64 FULL_TICKET_SIZE = 0x400; |
| 42 | 42 | ||
| 43 | using namespace Common; | 43 | using Common::AsArray; |
| 44 | 44 | ||
| 45 | const std::array<SHA256Hash, 2> eticket_source_hashes{ | 45 | // clang-format off |
| 46 | "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source | 46 | constexpr std::array eticket_source_hashes{ |
| 47 | "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source | 47 | AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source |
| 48 | AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source | ||
| 48 | }; | 49 | }; |
| 50 | // clang-format on | ||
| 49 | 51 | ||
| 50 | const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ | 52 | const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ |
| 51 | {{S128KeyType::Master, 0}, "master_key_"}, | 53 | {{S128KeyType::Master, 0}, "master_key_"}, |
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 7ed71ac3a..3e96f7516 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp | |||
| @@ -27,7 +27,7 @@ | |||
| 27 | #include "core/file_sys/vfs_offset.h" | 27 | #include "core/file_sys/vfs_offset.h" |
| 28 | #include "core/file_sys/vfs_vector.h" | 28 | #include "core/file_sys/vfs_vector.h" |
| 29 | 29 | ||
| 30 | using namespace Common; | 30 | using Common::AsArray; |
| 31 | 31 | ||
| 32 | namespace Core::Crypto { | 32 | namespace Core::Crypto { |
| 33 | 33 | ||
| @@ -47,105 +47,123 @@ struct Package2Header { | |||
| 47 | }; | 47 | }; |
| 48 | static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); | 48 | static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); |
| 49 | 49 | ||
| 50 | const std::array<SHA256Hash, 0x10> source_hashes{ | 50 | // clang-format off |
| 51 | "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source | 51 | constexpr std::array source_hashes{ |
| 52 | "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source | 52 | AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source |
| 53 | "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source | 53 | AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source |
| 54 | "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source | 54 | AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source |
| 55 | "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source | 55 | AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source |
| 56 | "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source | 56 | AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source |
| 57 | "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source | 57 | AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source |
| 58 | "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source | 58 | AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source |
| 59 | "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source | 59 | AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source |
| 60 | "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source | 60 | AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source |
| 61 | "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source | 61 | AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source |
| 62 | "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source | 62 | AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source |
| 63 | "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source | 63 | AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source |
| 64 | "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source | 64 | AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source |
| 65 | "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3 | 65 | AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source |
| 66 | "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0 | 66 | AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3 |
| 67 | AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0 | ||
| 67 | }; | 68 | }; |
| 68 | 69 | // clang-format on | |
| 69 | const std::array<SHA256Hash, 0x20> keyblob_source_hashes{ | 70 | |
| 70 | "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00 | 71 | // clang-format off |
| 71 | "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01 | 72 | constexpr std::array keyblob_source_hashes{ |
| 72 | "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02 | 73 | AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00 |
| 73 | "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03 | 74 | AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01 |
| 74 | "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04 | 75 | AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02 |
| 75 | "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05 | 76 | AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03 |
| 76 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06 | 77 | AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04 |
| 77 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07 | 78 | AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05 |
| 78 | 79 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06 | |
| 79 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08 | 80 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07 |
| 80 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09 | 81 | |
| 81 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A | 82 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08 |
| 82 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B | 83 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09 |
| 83 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C | 84 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A |
| 84 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D | 85 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B |
| 85 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E | 86 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C |
| 86 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F | 87 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D |
| 87 | 88 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E | |
| 88 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10 | 89 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F |
| 89 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11 | 90 | |
| 90 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12 | 91 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10 |
| 91 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13 | 92 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11 |
| 92 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14 | 93 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12 |
| 93 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15 | 94 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13 |
| 94 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16 | 95 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14 |
| 95 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17 | 96 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15 |
| 96 | 97 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16 | |
| 97 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18 | 98 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17 |
| 98 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19 | 99 | |
| 99 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A | 100 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18 |
| 100 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B | 101 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19 |
| 101 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C | 102 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A |
| 102 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D | 103 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B |
| 103 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E | 104 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C |
| 104 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F | 105 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D |
| 106 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E | ||
| 107 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F | ||
| 105 | }; | 108 | }; |
| 106 | 109 | // clang-format on | |
| 107 | const std::array<SHA256Hash, 0x20> master_key_hashes{ | 110 | |
| 108 | "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00 | 111 | // clang-format off |
| 109 | "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01 | 112 | constexpr std::array master_key_hashes{ |
| 110 | "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02 | 113 | AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00 |
| 111 | "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03 | 114 | AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01 |
| 112 | "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04 | 115 | AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02 |
| 113 | "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05 | 116 | AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03 |
| 114 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06 | 117 | AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04 |
| 115 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07 | 118 | AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05 |
| 116 | 119 | AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06 | |
| 117 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08 | 120 | AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07 |
| 118 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09 | 121 | |
| 119 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A | 122 | AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08 |
| 120 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B | 123 | AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09 |
| 121 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C | 124 | AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A |
| 122 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D | 125 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B |
| 123 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E | 126 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C |
| 124 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F | 127 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D |
| 125 | 128 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E | |
| 126 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10 | 129 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F |
| 127 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11 | 130 | |
| 128 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12 | 131 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10 |
| 129 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13 | 132 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11 |
| 130 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14 | 133 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12 |
| 131 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15 | 134 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13 |
| 132 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16 | 135 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14 |
| 133 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17 | 136 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15 |
| 134 | 137 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16 | |
| 135 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18 | 138 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17 |
| 136 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19 | 139 | |
| 137 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A | 140 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18 |
| 138 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B | 141 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19 |
| 139 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C | 142 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A |
| 140 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D | 143 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B |
| 141 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E | 144 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C |
| 142 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F | 145 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D |
| 146 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E | ||
| 147 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F | ||
| 143 | }; | 148 | }; |
| 149 | // clang-format on | ||
| 150 | |||
| 151 | static constexpr u8 CalculateMaxKeyblobSourceHash() { | ||
| 152 | const auto is_zero = [](const auto& data) { | ||
| 153 | // TODO: Replace with std::all_of whenever mingw decides to update their | ||
| 154 | // libraries to include the constexpr variant of it. | ||
| 155 | for (const auto element : data) { | ||
| 156 | if (element != 0) { | ||
| 157 | return false; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | return true; | ||
| 161 | }; | ||
| 144 | 162 | ||
| 145 | static u8 CalculateMaxKeyblobSourceHash() { | ||
| 146 | for (s8 i = 0x1F; i >= 0; --i) { | 163 | for (s8 i = 0x1F; i >= 0; --i) { |
| 147 | if (keyblob_source_hashes[i] != SHA256Hash{}) | 164 | if (!is_zero(keyblob_source_hashes[i])) { |
| 148 | return static_cast<u8>(i + 1); | 165 | return static_cast<u8>(i + 1); |
| 166 | } | ||
| 149 | } | 167 | } |
| 150 | 168 | ||
| 151 | return 0; | 169 | return 0; |
| @@ -346,10 +364,9 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con | |||
| 346 | } | 364 | } |
| 347 | 365 | ||
| 348 | static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { | 366 | static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { |
| 349 | const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end()); | ||
| 350 | Package2Header temp = header; | 367 | Package2Header temp = header; |
| 351 | AESCipher<Key128> cipher(key, Mode::CTR); | 368 | AESCipher<Key128> cipher(key, Mode::CTR); |
| 352 | cipher.SetIV(iv); | 369 | cipher.SetIV(header.header_ctr); |
| 353 | cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, | 370 | cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, |
| 354 | Op::Decrypt); | 371 | Op::Decrypt); |
| 355 | if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { | 372 | if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { |
| @@ -388,7 +405,7 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa | |||
| 388 | auto c = a->ReadAllBytes(); | 405 | auto c = a->ReadAllBytes(); |
| 389 | 406 | ||
| 390 | AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); | 407 | AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); |
| 391 | cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()}); | 408 | cipher.SetIV(header.section_ctr[1]); |
| 392 | cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); | 409 | cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); |
| 393 | 410 | ||
| 394 | const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c); | 411 | const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c); |
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp index 51097ced3..0c4b440ed 100644 --- a/src/core/device_memory.cpp +++ b/src/core/device_memory.cpp | |||
| @@ -2,14 +2,11 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/device_memory.h" | 5 | #include "core/device_memory.h" |
| 7 | #include "core/memory.h" | ||
| 8 | 6 | ||
| 9 | namespace Core { | 7 | namespace Core { |
| 10 | 8 | ||
| 11 | DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {} | 9 | DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {} |
| 12 | |||
| 13 | DeviceMemory::~DeviceMemory() = default; | 10 | DeviceMemory::~DeviceMemory() = default; |
| 14 | 11 | ||
| 15 | } // namespace Core | 12 | } // namespace Core |
diff --git a/src/core/device_memory.h b/src/core/device_memory.h index 9efa088d0..5b1ae28f3 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h | |||
| @@ -4,14 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/virtual_buffer.h" | 8 | #include "common/virtual_buffer.h" |
| 10 | 9 | ||
| 11 | namespace Core { | 10 | namespace Core { |
| 12 | 11 | ||
| 13 | class System; | ||
| 14 | |||
| 15 | namespace DramMemoryMap { | 12 | namespace DramMemoryMap { |
| 16 | enum : u64 { | 13 | enum : u64 { |
| 17 | Base = 0x80000000ULL, | 14 | Base = 0x80000000ULL, |
| @@ -26,7 +23,7 @@ enum : u64 { | |||
| 26 | 23 | ||
| 27 | class DeviceMemory : NonCopyable { | 24 | class DeviceMemory : NonCopyable { |
| 28 | public: | 25 | public: |
| 29 | explicit DeviceMemory(Core::System& system); | 26 | explicit DeviceMemory(); |
| 30 | ~DeviceMemory(); | 27 | ~DeviceMemory(); |
| 31 | 28 | ||
| 32 | template <typename T> | 29 | template <typename T> |
| @@ -45,7 +42,6 @@ public: | |||
| 45 | 42 | ||
| 46 | private: | 43 | private: |
| 47 | Common::VirtualBuffer<u8> buffer; | 44 | Common::VirtualBuffer<u8> buffer; |
| 48 | Core::System& system; | ||
| 49 | }; | 45 | }; |
| 50 | 46 | ||
| 51 | } // namespace Core | 47 | } // namespace Core |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 473245d5a..5039341c7 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -495,9 +495,10 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s | |||
| 495 | 495 | ||
| 496 | auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key, | 496 | auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key, |
| 497 | starting_offset); | 497 | starting_offset); |
| 498 | std::vector<u8> iv(16); | 498 | Core::Crypto::CTREncryptionLayer::IVData iv{}; |
| 499 | for (u8 i = 0; i < 8; ++i) | 499 | for (std::size_t i = 0; i < 8; ++i) { |
| 500 | iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; | 500 | iv[i] = s_header.raw.section_ctr[8 - i - 1]; |
| 501 | } | ||
| 501 | out->SetIV(iv); | 502 | out->SetIV(iv); |
| 502 | return std::static_pointer_cast<VfsFile>(out); | 503 | return std::static_pointer_cast<VfsFile>(out); |
| 503 | } | 504 | } |
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h index c95205668..2b4f21073 100644 --- a/src/core/file_sys/mode.h +++ b/src/core/file_sys/mode.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | 9 | ||
| 9 | namespace FileSys { | 10 | namespace FileSys { |
| @@ -11,13 +12,11 @@ namespace FileSys { | |||
| 11 | enum class Mode : u32 { | 12 | enum class Mode : u32 { |
| 12 | Read = 1, | 13 | Read = 1, |
| 13 | Write = 2, | 14 | Write = 2, |
| 14 | ReadWrite = 3, | 15 | ReadWrite = Read | Write, |
| 15 | Append = 4, | 16 | Append = 4, |
| 16 | WriteAppend = 6, | 17 | WriteAppend = Write | Append, |
| 17 | }; | 18 | }; |
| 18 | 19 | ||
| 19 | inline u32 operator&(Mode lhs, Mode rhs) { | 20 | DECLARE_ENUM_FLAG_OPERATORS(Mode) |
| 20 | return static_cast<u32>(lhs) & static_cast<u32>(rhs); | ||
| 21 | } | ||
| 22 | 21 | ||
| 23 | } // namespace FileSys | 22 | } // namespace FileSys |
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index 0090cc6c4..fe7375e84 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | ||
| 6 | #include <cstddef> | 7 | #include <cstddef> |
| 7 | #include <cstring> | 8 | #include <cstring> |
| 8 | 9 | ||
| @@ -66,7 +67,7 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const { | |||
| 66 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR); | 67 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR); |
| 67 | 68 | ||
| 68 | // Calculate AES IV | 69 | // Calculate AES IV |
| 69 | std::vector<u8> iv(16); | 70 | std::array<u8, 16> iv{}; |
| 70 | auto subsection_ctr = subsection.ctr; | 71 | auto subsection_ctr = subsection.ctr; |
| 71 | auto offset_iv = section_offset + base_offset; | 72 | auto offset_iv = section_offset + base_offset; |
| 72 | for (std::size_t i = 0; i < section_ctr.size(); ++i) | 73 | for (std::size_t i = 0; i < section_ctr.size(); ++i) |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index c47ff863e..729dbb5f4 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -288,8 +288,8 @@ std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( | |||
| 288 | } | 288 | } |
| 289 | 289 | ||
| 290 | Core::Memory::TextCheatParser parser; | 290 | Core::Memory::TextCheatParser parser; |
| 291 | return parser.Parse( | 291 | return parser.Parse(system, |
| 292 | system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size())); | 292 | std::string_view(reinterpret_cast<const char*>(data.data()), data.size())); |
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | } // Anonymous namespace | 295 | } // Anonymous namespace |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 37351c561..f831487dd 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -344,15 +344,18 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { | |||
| 344 | 344 | ||
| 345 | static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id, | 345 | static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id, |
| 346 | ContentRecordType type) { | 346 | ContentRecordType type) { |
| 347 | if (map.find(title_id) == map.end()) | 347 | const auto cmnt_iter = map.find(title_id); |
| 348 | return {}; | 348 | if (cmnt_iter == map.cend()) { |
| 349 | 349 | return std::nullopt; | |
| 350 | const auto& cnmt = map.at(title_id); | 350 | } |
| 351 | 351 | ||
| 352 | const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(), | 352 | const auto& cnmt = cmnt_iter->second; |
| 353 | const auto& content_records = cnmt.GetContentRecords(); | ||
| 354 | const auto iter = std::find_if(content_records.cbegin(), content_records.cend(), | ||
| 353 | [type](const ContentRecord& rec) { return rec.type == type; }); | 355 | [type](const ContentRecord& rec) { return rec.type == type; }); |
| 354 | if (iter == cnmt.GetContentRecords().end()) | 356 | if (iter == content_records.cend()) { |
| 355 | return {}; | 357 | return std::nullopt; |
| 358 | } | ||
| 356 | 359 | ||
| 357 | return std::make_optional(iter->nca_id); | 360 | return std::make_optional(iter->nca_id); |
| 358 | } | 361 | } |
| @@ -467,14 +470,16 @@ VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType ty | |||
| 467 | 470 | ||
| 468 | std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { | 471 | std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { |
| 469 | const auto meta_iter = meta.find(title_id); | 472 | const auto meta_iter = meta.find(title_id); |
| 470 | if (meta_iter != meta.end()) | 473 | if (meta_iter != meta.cend()) { |
| 471 | return meta_iter->second.GetTitleVersion(); | 474 | return meta_iter->second.GetTitleVersion(); |
| 475 | } | ||
| 472 | 476 | ||
| 473 | const auto yuzu_meta_iter = yuzu_meta.find(title_id); | 477 | const auto yuzu_meta_iter = yuzu_meta.find(title_id); |
| 474 | if (yuzu_meta_iter != yuzu_meta.end()) | 478 | if (yuzu_meta_iter != yuzu_meta.cend()) { |
| 475 | return yuzu_meta_iter->second.GetTitleVersion(); | 479 | return yuzu_meta_iter->second.GetTitleVersion(); |
| 480 | } | ||
| 476 | 481 | ||
| 477 | return {}; | 482 | return std::nullopt; |
| 478 | } | 483 | } |
| 479 | 484 | ||
| 480 | VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { | 485 | VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { |
| @@ -547,56 +552,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex | |||
| 547 | return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); | 552 | return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); |
| 548 | } | 553 | } |
| 549 | 554 | ||
| 550 | bool RegisteredCache::RemoveExistingEntry(u64 title_id) { | ||
| 551 | const auto delete_nca = [this](const NcaID& id) { | ||
| 552 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 553 | |||
| 554 | if (dir->GetFileRelative(path) == nullptr) { | ||
| 555 | return false; | ||
| 556 | } | ||
| 557 | |||
| 558 | Core::Crypto::SHA256Hash hash{}; | ||
| 559 | mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0); | ||
| 560 | const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||
| 561 | |||
| 562 | const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||
| 563 | |||
| 564 | const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||
| 565 | |||
| 566 | return res; | ||
| 567 | }; | ||
| 568 | |||
| 569 | // If an entry exists in the registered cache, remove it | ||
| 570 | if (HasEntry(title_id, ContentRecordType::Meta)) { | ||
| 571 | LOG_INFO(Loader, | ||
| 572 | "Previously installed entry (v{}) for title_id={:016X} detected! " | ||
| 573 | "Attempting to remove...", | ||
| 574 | GetEntryVersion(title_id).value_or(0), title_id); | ||
| 575 | // Get all the ncas associated with the current CNMT and delete them | ||
| 576 | const auto meta_old_id = | ||
| 577 | GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); | ||
| 578 | const auto program_id = | ||
| 579 | GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); | ||
| 580 | const auto data_id = | ||
| 581 | GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); | ||
| 582 | const auto control_id = | ||
| 583 | GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); | ||
| 584 | const auto html_id = | ||
| 585 | GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); | ||
| 586 | const auto legal_id = | ||
| 587 | GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); | ||
| 588 | |||
| 589 | delete_nca(meta_old_id); | ||
| 590 | delete_nca(program_id); | ||
| 591 | delete_nca(data_id); | ||
| 592 | delete_nca(control_id); | ||
| 593 | delete_nca(html_id); | ||
| 594 | delete_nca(legal_id); | ||
| 595 | return true; | ||
| 596 | } | ||
| 597 | return false; | ||
| 598 | } | ||
| 599 | |||
| 600 | InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, | 555 | InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, |
| 601 | const VfsCopyFunction& copy) { | 556 | const VfsCopyFunction& copy) { |
| 602 | const auto ncas = nsp.GetNCAsCollapsed(); | 557 | const auto ncas = nsp.GetNCAsCollapsed(); |
| @@ -692,6 +647,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, | |||
| 692 | return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); | 647 | return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); |
| 693 | } | 648 | } |
| 694 | 649 | ||
| 650 | bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { | ||
| 651 | const auto delete_nca = [this](const NcaID& id) { | ||
| 652 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 653 | |||
| 654 | const bool isFile = dir->GetFileRelative(path) != nullptr; | ||
| 655 | const bool isDir = dir->GetDirectoryRelative(path) != nullptr; | ||
| 656 | |||
| 657 | if (isFile) { | ||
| 658 | return dir->DeleteFile(path); | ||
| 659 | } else if (isDir) { | ||
| 660 | return dir->DeleteSubdirectoryRecursive(path); | ||
| 661 | } | ||
| 662 | |||
| 663 | return false; | ||
| 664 | }; | ||
| 665 | |||
| 666 | // If an entry exists in the registered cache, remove it | ||
| 667 | if (HasEntry(title_id, ContentRecordType::Meta)) { | ||
| 668 | LOG_INFO(Loader, | ||
| 669 | "Previously installed entry (v{}) for title_id={:016X} detected! " | ||
| 670 | "Attempting to remove...", | ||
| 671 | GetEntryVersion(title_id).value_or(0), title_id); | ||
| 672 | |||
| 673 | // Get all the ncas associated with the current CNMT and delete them | ||
| 674 | const auto meta_old_id = | ||
| 675 | GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); | ||
| 676 | const auto program_id = | ||
| 677 | GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); | ||
| 678 | const auto data_id = | ||
| 679 | GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); | ||
| 680 | const auto control_id = | ||
| 681 | GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); | ||
| 682 | const auto html_id = | ||
| 683 | GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); | ||
| 684 | const auto legal_id = | ||
| 685 | GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); | ||
| 686 | |||
| 687 | const auto deleted_meta = delete_nca(meta_old_id); | ||
| 688 | const auto deleted_program = delete_nca(program_id); | ||
| 689 | const auto deleted_data = delete_nca(data_id); | ||
| 690 | const auto deleted_control = delete_nca(control_id); | ||
| 691 | const auto deleted_html = delete_nca(html_id); | ||
| 692 | const auto deleted_legal = delete_nca(legal_id); | ||
| 693 | |||
| 694 | return deleted_meta && (deleted_meta || deleted_program || deleted_data || | ||
| 695 | deleted_control || deleted_html || deleted_legal); | ||
| 696 | } | ||
| 697 | |||
| 698 | return false; | ||
| 699 | } | ||
| 700 | |||
| 695 | InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, | 701 | InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, |
| 696 | bool overwrite_if_exists, | 702 | bool overwrite_if_exists, |
| 697 | std::optional<NcaID> override_id) { | 703 | std::optional<NcaID> override_id) { |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 29cf0d40c..5b414b0f0 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -133,9 +133,9 @@ public: | |||
| 133 | // Parsing function defines the conversion from raw file to NCA. If there are other steps | 133 | // Parsing function defines the conversion from raw file to NCA. If there are other steps |
| 134 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom | 134 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom |
| 135 | // parsing function. | 135 | // parsing function. |
| 136 | explicit RegisteredCache(VirtualDir dir, | 136 | explicit RegisteredCache( |
| 137 | ContentProviderParsingFunction parsing_function = | 137 | VirtualDir dir, ContentProviderParsingFunction parsing_function = |
| 138 | [](const VirtualFile& file, const NcaID& id) { return file; }); | 138 | [](const VirtualFile& file, const NcaID& id) { return file; }); |
| 139 | ~RegisteredCache() override; | 139 | ~RegisteredCache() override; |
| 140 | 140 | ||
| 141 | void Refresh() override; | 141 | void Refresh() override; |
| @@ -155,9 +155,6 @@ public: | |||
| 155 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, | 155 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, |
| 156 | std::optional<u64> title_id = {}) const override; | 156 | std::optional<u64> title_id = {}) const override; |
| 157 | 157 | ||
| 158 | // Removes an existing entry based on title id | ||
| 159 | bool RemoveExistingEntry(u64 title_id); | ||
| 160 | |||
| 161 | // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure | 158 | // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure |
| 162 | // there is a meta NCA and all of them are accessible. | 159 | // there is a meta NCA and all of them are accessible. |
| 163 | InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, | 160 | InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, |
| @@ -172,6 +169,9 @@ public: | |||
| 172 | InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, | 169 | InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, |
| 173 | const VfsCopyFunction& copy = &VfsRawCopy); | 170 | const VfsCopyFunction& copy = &VfsRawCopy); |
| 174 | 171 | ||
| 172 | // Removes an existing entry based on title id | ||
| 173 | bool RemoveExistingEntry(u64 title_id) const; | ||
| 174 | |||
| 175 | private: | 175 | private: |
| 176 | template <typename T> | 176 | template <typename T> |
| 177 | void IterateAllMetadata(std::vector<T>& out, | 177 | void IterateAllMetadata(std::vector<T>& out, |
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index adfd2c1a4..ba4efee3a 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -17,23 +17,23 @@ constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; | |||
| 17 | 17 | ||
| 18 | namespace { | 18 | namespace { |
| 19 | 19 | ||
| 20 | void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { | 20 | void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) { |
| 21 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 21 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 22 | if (meta.zero_1 != 0) { | 22 | if (meta.zero_1 != 0) { |
| 23 | LOG_WARNING(Service_FS, | 23 | LOG_WARNING(Service_FS, |
| 24 | "Possibly incorrect SaveDataDescriptor, type is " | 24 | "Possibly incorrect SaveDataAttribute, type is " |
| 25 | "SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).", | 25 | "SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).", |
| 26 | meta.zero_1); | 26 | meta.zero_1); |
| 27 | } | 27 | } |
| 28 | if (meta.zero_2 != 0) { | 28 | if (meta.zero_2 != 0) { |
| 29 | LOG_WARNING(Service_FS, | 29 | LOG_WARNING(Service_FS, |
| 30 | "Possibly incorrect SaveDataDescriptor, type is " | 30 | "Possibly incorrect SaveDataAttribute, type is " |
| 31 | "SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).", | 31 | "SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).", |
| 32 | meta.zero_2); | 32 | meta.zero_2); |
| 33 | } | 33 | } |
| 34 | if (meta.zero_3 != 0) { | 34 | if (meta.zero_3 != 0) { |
| 35 | LOG_WARNING(Service_FS, | 35 | LOG_WARNING(Service_FS, |
| 36 | "Possibly incorrect SaveDataDescriptor, type is " | 36 | "Possibly incorrect SaveDataAttribute, type is " |
| 37 | "SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).", | 37 | "SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).", |
| 38 | meta.zero_3); | 38 | meta.zero_3); |
| 39 | } | 39 | } |
| @@ -41,33 +41,32 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { | |||
| 41 | 41 | ||
| 42 | if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) { | 42 | if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) { |
| 43 | LOG_WARNING(Service_FS, | 43 | LOG_WARNING(Service_FS, |
| 44 | "Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is " | 44 | "Possibly incorrect SaveDataAttribute, type is SystemSaveData but title_id is " |
| 45 | "non-zero ({:016X}).", | 45 | "non-zero ({:016X}).", |
| 46 | meta.title_id); | 46 | meta.title_id); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) { | 49 | if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) { |
| 50 | LOG_WARNING(Service_FS, | 50 | LOG_WARNING(Service_FS, |
| 51 | "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is " | 51 | "Possibly incorrect SaveDataAttribute, type is DeviceSaveData but user_id is " |
| 52 | "non-zero ({:016X}{:016X})", | 52 | "non-zero ({:016X}{:016X})", |
| 53 | meta.user_id[1], meta.user_id[0]); | 53 | meta.user_id[1], meta.user_id[0]); |
| 54 | } | 54 | } |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) { | 57 | bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataAttribute& attr) { |
| 58 | return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage || | 58 | return attr.type == SaveDataType::CacheStorage || attr.type == SaveDataType::TemporaryStorage || |
| 59 | (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User | 59 | (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User |
| 60 | (desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) && | 60 | (attr.type == SaveDataType::SaveData || attr.type == SaveDataType::DeviceSaveData) && |
| 61 | desc.title_id == 0 && desc.save_id == 0); | 61 | attr.title_id == 0 && attr.save_id == 0); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | } // Anonymous namespace | 64 | } // Anonymous namespace |
| 65 | 65 | ||
| 66 | std::string SaveDataDescriptor::DebugInfo() const { | 66 | std::string SaveDataAttribute::DebugInfo() const { |
| 67 | return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, " | 67 | return fmt::format("[title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, type={:02X}, " |
| 68 | "save_id={:016X}, " | ||
| 69 | "rank={}, index={}]", | 68 | "rank={}, index={}]", |
| 70 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | 69 | title_id, user_id[1], user_id[0], save_id, static_cast<u8>(type), |
| 71 | static_cast<u8>(rank), index); | 70 | static_cast<u8>(rank), index); |
| 72 | } | 71 | } |
| 73 | 72 | ||
| @@ -80,8 +79,8 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save | |||
| 80 | SaveDataFactory::~SaveDataFactory() = default; | 79 | SaveDataFactory::~SaveDataFactory() = default; |
| 81 | 80 | ||
| 82 | ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, | 81 | ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, |
| 83 | const SaveDataDescriptor& meta) const { | 82 | const SaveDataAttribute& meta) const { |
| 84 | PrintSaveDataDescriptorWarnings(meta); | 83 | PrintSaveDataAttributeWarnings(meta); |
| 85 | 84 | ||
| 86 | const auto save_directory = | 85 | const auto save_directory = |
| 87 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | 86 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); |
| @@ -98,7 +97,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, | |||
| 98 | } | 97 | } |
| 99 | 98 | ||
| 100 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, | 99 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, |
| 101 | const SaveDataDescriptor& meta) const { | 100 | const SaveDataAttribute& meta) const { |
| 102 | 101 | ||
| 103 | const auto save_directory = | 102 | const auto save_directory = |
| 104 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | 103 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); |
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 991e57aa1..6625bbbd8 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -21,6 +21,7 @@ enum class SaveDataSpaceId : u8 { | |||
| 21 | TemporaryStorage = 3, | 21 | TemporaryStorage = 3, |
| 22 | SdCardUser = 4, | 22 | SdCardUser = 4, |
| 23 | ProperSystem = 100, | 23 | ProperSystem = 100, |
| 24 | SafeMode = 101, | ||
| 24 | }; | 25 | }; |
| 25 | 26 | ||
| 26 | enum class SaveDataType : u8 { | 27 | enum class SaveDataType : u8 { |
| @@ -30,28 +31,50 @@ enum class SaveDataType : u8 { | |||
| 30 | DeviceSaveData = 3, | 31 | DeviceSaveData = 3, |
| 31 | TemporaryStorage = 4, | 32 | TemporaryStorage = 4, |
| 32 | CacheStorage = 5, | 33 | CacheStorage = 5, |
| 34 | SystemBcat = 6, | ||
| 33 | }; | 35 | }; |
| 34 | 36 | ||
| 35 | enum class SaveDataRank : u8 { | 37 | enum class SaveDataRank : u8 { |
| 36 | Primary, | 38 | Primary = 0, |
| 37 | Secondary, | 39 | Secondary = 1, |
| 38 | }; | 40 | }; |
| 39 | 41 | ||
| 40 | struct SaveDataDescriptor { | 42 | enum class SaveDataFlags : u32 { |
| 41 | u64_le title_id; | 43 | None = (0 << 0), |
| 44 | KeepAfterResettingSystemSaveData = (1 << 0), | ||
| 45 | KeepAfterRefurbishment = (1 << 1), | ||
| 46 | KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2), | ||
| 47 | NeedsSecureDelete = (1 << 3), | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct SaveDataAttribute { | ||
| 51 | u64 title_id; | ||
| 42 | u128 user_id; | 52 | u128 user_id; |
| 43 | u64_le save_id; | 53 | u64 save_id; |
| 44 | SaveDataType type; | 54 | SaveDataType type; |
| 45 | SaveDataRank rank; | 55 | SaveDataRank rank; |
| 46 | u16_le index; | 56 | u16 index; |
| 47 | INSERT_PADDING_BYTES(4); | 57 | INSERT_PADDING_BYTES(4); |
| 48 | u64_le zero_1; | 58 | u64 zero_1; |
| 49 | u64_le zero_2; | 59 | u64 zero_2; |
| 50 | u64_le zero_3; | 60 | u64 zero_3; |
| 51 | 61 | ||
| 52 | std::string DebugInfo() const; | 62 | std::string DebugInfo() const; |
| 53 | }; | 63 | }; |
| 54 | static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size."); | 64 | static_assert(sizeof(SaveDataAttribute) == 0x40, "SaveDataAttribute has incorrect size."); |
| 65 | |||
| 66 | struct SaveDataExtraData { | ||
| 67 | SaveDataAttribute attr; | ||
| 68 | u64 owner_id; | ||
| 69 | s64 timestamp; | ||
| 70 | SaveDataFlags flags; | ||
| 71 | INSERT_PADDING_BYTES(4); | ||
| 72 | s64 available_size; | ||
| 73 | s64 journal_size; | ||
| 74 | s64 commit_id; | ||
| 75 | std::array<u8, 0x190> unused; | ||
| 76 | }; | ||
| 77 | static_assert(sizeof(SaveDataExtraData) == 0x200, "SaveDataExtraData has incorrect size."); | ||
| 55 | 78 | ||
| 56 | struct SaveDataSize { | 79 | struct SaveDataSize { |
| 57 | u64 normal; | 80 | u64 normal; |
| @@ -64,8 +87,8 @@ public: | |||
| 64 | explicit SaveDataFactory(VirtualDir dir); | 87 | explicit SaveDataFactory(VirtualDir dir); |
| 65 | ~SaveDataFactory(); | 88 | ~SaveDataFactory(); |
| 66 | 89 | ||
| 67 | ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; | 90 | ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; |
| 68 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; | 91 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const; |
| 69 | 92 | ||
| 70 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; | 93 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; |
| 71 | 94 | ||
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp index 61bb67945..d65c7d234 100644 --- a/src/core/file_sys/system_archive/mii_model.cpp +++ b/src/core/file_sys/system_archive/mii_model.cpp | |||
| @@ -27,18 +27,12 @@ VirtualDir MiiModel() { | |||
| 27 | auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, | 27 | auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, |
| 28 | std::vector<VirtualDir>{}, "data"); | 28 | std::vector<VirtualDir>{}, "data"); |
| 29 | 29 | ||
| 30 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>( | 30 | out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); |
| 31 | MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); | 31 | out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); |
| 32 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>( | 32 | out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); |
| 33 | MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); | 33 | out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat")); |
| 34 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>( | 34 | out->AddFile(MakeArrayFile(MiiModelData::SHAPE_HIGH, "ShapeHigh.dat")); |
| 35 | MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); | 35 | out->AddFile(MakeArrayFile(MiiModelData::SHAPE_MID, "ShapeMid.dat")); |
| 36 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>( | ||
| 37 | MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat")); | ||
| 38 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>( | ||
| 39 | MiiModelData::SHAPE_HIGH, "ShapeHigh.dat")); | ||
| 40 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>( | ||
| 41 | MiiModelData::SHAPE_MID, "ShapeMid.dat")); | ||
| 42 | 36 | ||
| 43 | return out; | 37 | return out; |
| 44 | } | 38 | } |
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp index f4443784d..100d3c5db 100644 --- a/src/core/file_sys/system_archive/ng_word.cpp +++ b/src/core/file_sys/system_archive/ng_word.cpp | |||
| @@ -24,19 +24,18 @@ constexpr std::array<u8, 30> WORD_TXT{ | |||
| 24 | } // namespace NgWord1Data | 24 | } // namespace NgWord1Data |
| 25 | 25 | ||
| 26 | VirtualDir NgWord1() { | 26 | VirtualDir NgWord1() { |
| 27 | std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); | 27 | std::vector<VirtualFile> files; |
| 28 | files.reserve(NgWord1Data::NUMBER_WORD_TXT_FILES); | ||
| 28 | 29 | ||
| 29 | for (std::size_t i = 0; i < files.size(); ++i) { | 30 | for (std::size_t i = 0; i < files.size(); ++i) { |
| 30 | files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( | 31 | files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, fmt::format("{}.txt", i))); |
| 31 | NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)); | ||
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( | 34 | files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, "common.txt")); |
| 35 | NgWord1Data::WORD_TXT, "common.txt")); | 35 | files.push_back(MakeArrayFile(NgWord1Data::VERSION_DAT, "version.dat")); |
| 36 | files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>( | ||
| 37 | NgWord1Data::VERSION_DAT, "version.dat")); | ||
| 38 | 36 | ||
| 39 | return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); | 37 | return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{}, |
| 38 | "data"); | ||
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | namespace NgWord2Data { | 41 | namespace NgWord2Data { |
| @@ -55,27 +54,22 @@ constexpr std::array<u8, 0x2C> AC_NX_DATA{ | |||
| 55 | } // namespace NgWord2Data | 54 | } // namespace NgWord2Data |
| 56 | 55 | ||
| 57 | VirtualDir NgWord2() { | 56 | VirtualDir NgWord2() { |
| 58 | std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3); | 57 | std::vector<VirtualFile> files; |
| 58 | files.reserve(NgWord2Data::NUMBER_AC_NX_FILES * 3); | ||
| 59 | 59 | ||
| 60 | for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { | 60 | for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { |
| 61 | files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | 61 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i))); |
| 62 | NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)); | 62 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i))); |
| 63 | files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | 63 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i))); |
| 64 | NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)); | ||
| 65 | files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | ||
| 66 | NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)); | ||
| 67 | } | 64 | } |
| 68 | 65 | ||
| 69 | files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | 66 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); |
| 70 | NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); | 67 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); |
| 71 | files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | 68 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx")); |
| 72 | NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); | 69 | files.push_back(MakeArrayFile(NgWord2Data::VERSION_DAT, "version.dat")); |
| 73 | files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | ||
| 74 | NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx")); | ||
| 75 | files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>( | ||
| 76 | NgWord2Data::VERSION_DAT, "version.dat")); | ||
| 77 | 70 | ||
| 78 | return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); | 71 | return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{}, |
| 72 | "data"); | ||
| 79 | } | 73 | } |
| 80 | 74 | ||
| 81 | } // namespace FileSys::SystemArchive | 75 | } // namespace FileSys::SystemArchive |
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index 9806bd197..8fd005012 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp | |||
| @@ -2,6 +2,9 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 6 | #include <vector> | ||
| 7 | |||
| 5 | #include "common/swap.h" | 8 | #include "common/swap.h" |
| 6 | #include "core/file_sys/system_archive/time_zone_binary.h" | 9 | #include "core/file_sys/system_archive/time_zone_binary.h" |
| 7 | #include "core/file_sys/vfs_vector.h" | 10 | #include "core/file_sys/vfs_vector.h" |
| @@ -615,43 +618,49 @@ static constexpr std::array<u8, 9633> LOCATION_NAMES{ | |||
| 615 | 0x0a}; | 618 | 0x0a}; |
| 616 | 619 | ||
| 617 | static VirtualFile GenerateDefaultTimeZoneFile() { | 620 | static VirtualFile GenerateDefaultTimeZoneFile() { |
| 618 | struct { | 621 | struct TimeZoneInfo { |
| 619 | s64_be at; | 622 | s64_be at; |
| 620 | INSERT_PADDING_BYTES(7); | 623 | std::array<u8, 7> padding1; |
| 621 | std::array<char, 4> time_zone_chars; | 624 | std::array<char, 4> time_zone_chars; |
| 622 | INSERT_PADDING_BYTES(2); | 625 | std::array<u8, 2> padding2; |
| 623 | std::array<char, 6> time_zone_name; | 626 | std::array<char, 6> time_zone_name; |
| 624 | } time_zone_info{}; | 627 | }; |
| 625 | 628 | ||
| 626 | const VirtualFile file{std::make_shared<VectorVfsFile>( | 629 | VirtualFile file{std::make_shared<VectorVfsFile>( |
| 627 | std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(time_zone_info)), | 630 | std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(TimeZoneInfo)), |
| 628 | "GMT")}; | 631 | "GMT")}; |
| 629 | 632 | ||
| 630 | Service::Time::TimeZone::TzifHeader header{}; | 633 | const Service::Time::TimeZone::TzifHeader header{ |
| 631 | header.magic = 0x545a6966; | 634 | .magic = 0x545a6966, |
| 632 | header.version = 0x32; | 635 | .version = 0x32, |
| 633 | header.ttis_gmt_count = 0x1; | 636 | .ttis_gmt_count = 1, |
| 634 | header.ttis_std_count = 0x1; | 637 | .ttis_std_count = 1, |
| 635 | header.time_count = 0x1; | 638 | .time_count = 1, |
| 636 | header.type_count = 0x1; | 639 | .type_count = 1, |
| 637 | header.char_count = 0x4; | 640 | .char_count = 4, |
| 641 | }; | ||
| 638 | file->WriteObject(header, 0); | 642 | file->WriteObject(header, 0); |
| 639 | 643 | ||
| 640 | time_zone_info.at = 0xf8; | 644 | const TimeZoneInfo time_zone_info{ |
| 641 | time_zone_info.time_zone_chars = {'G', 'M', 'T', '\0'}; | 645 | .at = 0xf8, |
| 642 | time_zone_info.time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'}; | 646 | .padding1 = {}, |
| 647 | .time_zone_chars = {'G', 'M', 'T', '\0'}, | ||
| 648 | .padding2 = {}, | ||
| 649 | .time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'}, | ||
| 650 | }; | ||
| 643 | file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader)); | 651 | file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader)); |
| 644 | 652 | ||
| 645 | return file; | 653 | return file; |
| 646 | } | 654 | } |
| 647 | 655 | ||
| 648 | VirtualDir TimeZoneBinary() { | 656 | VirtualDir TimeZoneBinary() { |
| 649 | const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( | 657 | std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( |
| 650 | std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, | 658 | std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, |
| 651 | "zoneinfo")}; | 659 | "zoneinfo")}; |
| 652 | const std::vector<VirtualFile> root_files{ | 660 | std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")}; |
| 653 | std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")}; | 661 | |
| 654 | return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data"); | 662 | return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs), |
| 663 | "data"); | ||
| 655 | } | 664 | } |
| 656 | 665 | ||
| 657 | } // namespace FileSys::SystemArchive | 666 | } // namespace FileSys::SystemArchive |
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 96ce5957c..0db0091f6 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -18,20 +18,22 @@ static std::string ModeFlagsToString(Mode mode) { | |||
| 18 | std::string mode_str; | 18 | std::string mode_str; |
| 19 | 19 | ||
| 20 | // Calculate the correct open mode for the file. | 20 | // Calculate the correct open mode for the file. |
| 21 | if (mode & Mode::Read && mode & Mode::Write) { | 21 | if (True(mode & Mode::Read) && True(mode & Mode::Write)) { |
| 22 | if (mode & Mode::Append) | 22 | if (True(mode & Mode::Append)) { |
| 23 | mode_str = "a+"; | 23 | mode_str = "a+"; |
| 24 | else | 24 | } else { |
| 25 | mode_str = "r+"; | 25 | mode_str = "r+"; |
| 26 | } | ||
| 26 | } else { | 27 | } else { |
| 27 | if (mode & Mode::Read) | 28 | if (True(mode & Mode::Read)) { |
| 28 | mode_str = "r"; | 29 | mode_str = "r"; |
| 29 | else if (mode & Mode::Append) | 30 | } else if (True(mode & Mode::Append)) { |
| 30 | mode_str = "a"; | 31 | mode_str = "a"; |
| 31 | else if (mode & Mode::Write) | 32 | } else if (True(mode & Mode::Write)) { |
| 32 | mode_str = "w"; | 33 | mode_str = "w"; |
| 33 | else | 34 | } else { |
| 34 | UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); | 35 | UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); |
| 36 | } | ||
| 35 | } | 37 | } |
| 36 | 38 | ||
| 37 | mode_str += "b"; | 39 | mode_str += "b"; |
| @@ -73,8 +75,9 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | |||
| 73 | } | 75 | } |
| 74 | } | 76 | } |
| 75 | 77 | ||
| 76 | if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0) | 78 | if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { |
| 77 | FileUtil::CreateEmptyFile(path); | 79 | FileUtil::CreateEmptyFile(path); |
| 80 | } | ||
| 78 | 81 | ||
| 79 | auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); | 82 | auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); |
| 80 | cache[path] = backing; | 83 | cache[path] = backing; |
| @@ -247,11 +250,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { | |||
| 247 | } | 250 | } |
| 248 | 251 | ||
| 249 | bool RealVfsFile::IsWritable() const { | 252 | bool RealVfsFile::IsWritable() const { |
| 250 | return (perms & Mode::WriteAppend) != 0; | 253 | return True(perms & Mode::WriteAppend); |
| 251 | } | 254 | } |
| 252 | 255 | ||
| 253 | bool RealVfsFile::IsReadable() const { | 256 | bool RealVfsFile::IsReadable() const { |
| 254 | return (perms & Mode::ReadWrite) != 0; | 257 | return True(perms & Mode::ReadWrite); |
| 255 | } | 258 | } |
| 256 | 259 | ||
| 257 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { | 260 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { |
| @@ -319,8 +322,9 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& | |||
| 319 | path_components(FileUtil::SplitPathComponents(path)), | 322 | path_components(FileUtil::SplitPathComponents(path)), |
| 320 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | 323 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), |
| 321 | perms(perms_) { | 324 | perms(perms_) { |
| 322 | if (!FileUtil::Exists(path) && perms & Mode::WriteAppend) | 325 | if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { |
| 323 | FileUtil::CreateDir(path); | 326 | FileUtil::CreateDir(path); |
| 327 | } | ||
| 324 | } | 328 | } |
| 325 | 329 | ||
| 326 | RealVfsDirectory::~RealVfsDirectory() = default; | 330 | RealVfsDirectory::~RealVfsDirectory() = default; |
| @@ -371,11 +375,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() | |||
| 371 | } | 375 | } |
| 372 | 376 | ||
| 373 | bool RealVfsDirectory::IsWritable() const { | 377 | bool RealVfsDirectory::IsWritable() const { |
| 374 | return (perms & Mode::WriteAppend) != 0; | 378 | return True(perms & Mode::WriteAppend); |
| 375 | } | 379 | } |
| 376 | 380 | ||
| 377 | bool RealVfsDirectory::IsReadable() const { | 381 | bool RealVfsDirectory::IsReadable() const { |
| 378 | return (perms & Mode::ReadWrite) != 0; | 382 | return True(perms & Mode::ReadWrite); |
| 379 | } | 383 | } |
| 380 | 384 | ||
| 381 | std::string RealVfsDirectory::GetName() const { | 385 | std::string RealVfsDirectory::GetName() const { |
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index ac36cb2ee..95d3da2f2 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h | |||
| @@ -4,7 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <cstring> | 8 | #include <cstring> |
| 9 | #include <memory> | ||
| 10 | #include <string> | ||
| 11 | #include <vector> | ||
| 8 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 9 | 13 | ||
| 10 | namespace FileSys { | 14 | namespace FileSys { |
| @@ -13,7 +17,8 @@ namespace FileSys { | |||
| 13 | template <std::size_t size> | 17 | template <std::size_t size> |
| 14 | class ArrayVfsFile : public VfsFile { | 18 | class ArrayVfsFile : public VfsFile { |
| 15 | public: | 19 | public: |
| 16 | ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr) | 20 | explicit ArrayVfsFile(const std::array<u8, size>& data, std::string name = "", |
| 21 | VirtualDir parent = nullptr) | ||
| 17 | : data(data), name(std::move(name)), parent(std::move(parent)) {} | 22 | : data(data), name(std::move(name)), parent(std::move(parent)) {} |
| 18 | 23 | ||
| 19 | std::string GetName() const override { | 24 | std::string GetName() const override { |
| @@ -61,6 +66,12 @@ private: | |||
| 61 | VirtualDir parent; | 66 | VirtualDir parent; |
| 62 | }; | 67 | }; |
| 63 | 68 | ||
| 69 | template <std::size_t Size, typename... Args> | ||
| 70 | std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data, | ||
| 71 | Args&&... args) { | ||
| 72 | return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...); | ||
| 73 | } | ||
| 74 | |||
| 64 | // An implementation of VfsFile that is backed by a vector optionally supplied upon construction | 75 | // An implementation of VfsFile that is backed by a vector optionally supplied upon construction |
| 65 | class VectorVfsFile : public VfsFile { | 76 | class VectorVfsFile : public VfsFile { |
| 66 | public: | 77 | public: |
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index 86e06ccb9..81413c684 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp | |||
| @@ -70,14 +70,18 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) | |||
| 70 | NAX::~NAX() = default; | 70 | NAX::~NAX() = default; |
| 71 | 71 | ||
| 72 | Loader::ResultStatus NAX::Parse(std::string_view path) { | 72 | Loader::ResultStatus NAX::Parse(std::string_view path) { |
| 73 | if (file->ReadObject(header.get()) != sizeof(NAXHeader)) | 73 | if (file == nullptr) { |
| 74 | return Loader::ResultStatus::ErrorNullFile; | ||
| 75 | } | ||
| 76 | if (file->ReadObject(header.get()) != sizeof(NAXHeader)) { | ||
| 74 | return Loader::ResultStatus::ErrorBadNAXHeader; | 77 | return Loader::ResultStatus::ErrorBadNAXHeader; |
| 75 | 78 | } | |
| 76 | if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) | 79 | if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) { |
| 77 | return Loader::ResultStatus::ErrorBadNAXHeader; | 80 | return Loader::ResultStatus::ErrorBadNAXHeader; |
| 78 | 81 | } | |
| 79 | if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) | 82 | if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) { |
| 80 | return Loader::ResultStatus::ErrorIncorrectNAXFileSize; | 83 | return Loader::ResultStatus::ErrorIncorrectNAXFileSize; |
| 84 | } | ||
| 81 | 85 | ||
| 82 | keys.DeriveSDSeedLazy(); | 86 | keys.DeriveSDSeedLazy(); |
| 83 | std::array<Core::Crypto::Key256, 2> sd_keys{}; | 87 | std::array<Core::Crypto::Key256, 2> sd_keys{}; |
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index efc1030c1..645f26e91 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp | |||
| @@ -11,8 +11,8 @@ | |||
| 11 | namespace Core::Hardware { | 11 | namespace Core::Hardware { |
| 12 | 12 | ||
| 13 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { | 13 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { |
| 14 | gpu_interrupt_event = | 14 | gpu_interrupt_event = Core::Timing::CreateEvent( |
| 15 | Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, std::chrono::nanoseconds) { | 15 | "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { |
| 16 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); | 16 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); |
| 17 | const u32 syncpt = static_cast<u32>(message >> 32); | 17 | const u32 syncpt = static_cast<u32>(message >> 32); |
| 18 | const u32 value = static_cast<u32>(message); | 18 | const u32 value = static_cast<u32>(message); |
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 0dc6a4a43..1b503331f 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -229,6 +229,8 @@ inline void ResponseBuilder::Push(u32 value) { | |||
| 229 | 229 | ||
| 230 | template <typename T> | 230 | template <typename T> |
| 231 | void ResponseBuilder::PushRaw(const T& value) { | 231 | void ResponseBuilder::PushRaw(const T& value) { |
| 232 | static_assert(std::is_trivially_copyable_v<T>, | ||
| 233 | "It's undefined behavior to use memcpy with non-trivially copyable objects"); | ||
| 232 | std::memcpy(cmdbuf + index, &value, sizeof(T)); | 234 | std::memcpy(cmdbuf + index, &value, sizeof(T)); |
| 233 | index += (sizeof(T) + 3) / 4; // round up to word length | 235 | index += (sizeof(T) + 3) / 4; // round up to word length |
| 234 | } | 236 | } |
| @@ -384,6 +386,8 @@ inline s32 RequestParser::Pop() { | |||
| 384 | 386 | ||
| 385 | template <typename T> | 387 | template <typename T> |
| 386 | void RequestParser::PopRaw(T& value) { | 388 | void RequestParser::PopRaw(T& value) { |
| 389 | static_assert(std::is_trivially_copyable_v<T>, | ||
| 390 | "It's undefined behavior to use memcpy with non-trivially copyable objects"); | ||
| 387 | std::memcpy(&value, cmdbuf + index, sizeof(T)); | 391 | std::memcpy(&value, cmdbuf + index, sizeof(T)); |
| 388 | index += (sizeof(T) + 3) / 4; // round up to word length | 392 | index += (sizeof(T) + 3) / 4; // round up to word length |
| 389 | } | 393 | } |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index df0debe1b..b882eaa0f 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -81,7 +81,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 | |||
| 81 | do { | 81 | do { |
| 82 | current_value = monitor.ExclusiveRead32(current_core, address); | 82 | current_value = monitor.ExclusiveRead32(current_core, address); |
| 83 | 83 | ||
| 84 | if (current_value != value) { | 84 | if (current_value != static_cast<u32>(value)) { |
| 85 | return ERR_INVALID_STATE; | 85 | return ERR_INVALID_STATE; |
| 86 | } | 86 | } |
| 87 | current_value++; | 87 | current_value++; |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 9277b5d08..81f85643b 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -293,13 +293,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { | |||
| 293 | BufferDescriptorA()[buffer_index].Size()}; | 293 | BufferDescriptorA()[buffer_index].Size()}; |
| 294 | 294 | ||
| 295 | if (is_buffer_a) { | 295 | if (is_buffer_a) { |
| 296 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, | 296 | ASSERT_OR_EXECUTE_MSG( |
| 297 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 297 | BufferDescriptorA().size() > buffer_index, { return buffer; }, |
| 298 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | ||
| 298 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); | 299 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); |
| 299 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); | 300 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 300 | } else { | 301 | } else { |
| 301 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, | 302 | ASSERT_OR_EXECUTE_MSG( |
| 302 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 303 | BufferDescriptorX().size() > buffer_index, { return buffer; }, |
| 304 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | ||
| 303 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); | 305 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); |
| 304 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); | 306 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 305 | } | 307 | } |
| @@ -324,16 +326,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | |||
| 324 | } | 326 | } |
| 325 | 327 | ||
| 326 | if (is_buffer_b) { | 328 | if (is_buffer_b) { |
| 327 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && | 329 | ASSERT_OR_EXECUTE_MSG( |
| 328 | BufferDescriptorB()[buffer_index].Size() >= size, | 330 | BufferDescriptorB().size() > buffer_index && |
| 329 | { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", | 331 | BufferDescriptorB()[buffer_index].Size() >= size, |
| 330 | buffer_index, size); | 332 | { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size); |
| 331 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | 333 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); |
| 332 | } else { | 334 | } else { |
| 333 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && | 335 | ASSERT_OR_EXECUTE_MSG( |
| 334 | BufferDescriptorC()[buffer_index].Size() >= size, | 336 | BufferDescriptorC().size() > buffer_index && |
| 335 | { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", | 337 | BufferDescriptorC()[buffer_index].Size() >= size, |
| 336 | buffer_index, size); | 338 | { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size); |
| 337 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | 339 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); |
| 338 | } | 340 | } |
| 339 | 341 | ||
| @@ -344,12 +346,14 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const | |||
| 344 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && | 346 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 345 | BufferDescriptorA()[buffer_index].Size()}; | 347 | BufferDescriptorA()[buffer_index].Size()}; |
| 346 | if (is_buffer_a) { | 348 | if (is_buffer_a) { |
| 347 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, | 349 | ASSERT_OR_EXECUTE_MSG( |
| 348 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 350 | BufferDescriptorA().size() > buffer_index, { return 0; }, |
| 351 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | ||
| 349 | return BufferDescriptorA()[buffer_index].Size(); | 352 | return BufferDescriptorA()[buffer_index].Size(); |
| 350 | } else { | 353 | } else { |
| 351 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, | 354 | ASSERT_OR_EXECUTE_MSG( |
| 352 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 355 | BufferDescriptorX().size() > buffer_index, { return 0; }, |
| 356 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | ||
| 353 | return BufferDescriptorX()[buffer_index].Size(); | 357 | return BufferDescriptorX()[buffer_index].Size(); |
| 354 | } | 358 | } |
| 355 | } | 359 | } |
| @@ -358,12 +362,14 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons | |||
| 358 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && | 362 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && |
| 359 | BufferDescriptorB()[buffer_index].Size()}; | 363 | BufferDescriptorB()[buffer_index].Size()}; |
| 360 | if (is_buffer_b) { | 364 | if (is_buffer_b) { |
| 361 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, | 365 | ASSERT_OR_EXECUTE_MSG( |
| 362 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | 366 | BufferDescriptorB().size() > buffer_index, { return 0; }, |
| 367 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | ||
| 363 | return BufferDescriptorB()[buffer_index].Size(); | 368 | return BufferDescriptorB()[buffer_index].Size(); |
| 364 | } else { | 369 | } else { |
| 365 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, | 370 | ASSERT_OR_EXECUTE_MSG( |
| 366 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | 371 | BufferDescriptorC().size() > buffer_index, { return 0; }, |
| 372 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | ||
| 367 | return BufferDescriptorC()[buffer_index].Size(); | 373 | return BufferDescriptorC()[buffer_index].Size(); |
| 368 | } | 374 | } |
| 369 | return 0; | 375 | return 0; |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index b31673928..f3277b766 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <vector> | 13 | #include <vector> |
| 14 | #include <boost/container/small_vector.hpp> | 14 | #include <boost/container/small_vector.hpp> |
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "common/concepts.h" | ||
| 16 | #include "common/swap.h" | 17 | #include "common/swap.h" |
| 17 | #include "core/hle/ipc.h" | 18 | #include "core/hle/ipc.h" |
| 18 | #include "core/hle/kernel/object.h" | 19 | #include "core/hle/kernel/object.h" |
| @@ -193,23 +194,24 @@ public: | |||
| 193 | 194 | ||
| 194 | /* Helper function to write a buffer using the appropriate buffer descriptor | 195 | /* Helper function to write a buffer using the appropriate buffer descriptor |
| 195 | * | 196 | * |
| 196 | * @tparam ContiguousContainer an arbitrary container that satisfies the | 197 | * @tparam T an arbitrary container that satisfies the |
| 197 | * ContiguousContainer concept in the C++ standard library. | 198 | * ContiguousContainer concept in the C++ standard library or a trivially copyable type. |
| 198 | * | 199 | * |
| 199 | * @param container The container to write the data of into a buffer. | 200 | * @param data The container/data to write into a buffer. |
| 200 | * @param buffer_index The buffer in particular to write to. | 201 | * @param buffer_index The buffer in particular to write to. |
| 201 | */ | 202 | */ |
| 202 | template <typename ContiguousContainer, | 203 | template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> |
| 203 | typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> | 204 | std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { |
| 204 | std::size_t WriteBuffer(const ContiguousContainer& container, | 205 | if constexpr (Common::IsSTLContainer<T>) { |
| 205 | std::size_t buffer_index = 0) const { | 206 | using ContiguousType = typename T::value_type; |
| 206 | using ContiguousType = typename ContiguousContainer::value_type; | 207 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| 207 | 208 | "Container to WriteBuffer must contain trivially copyable objects"); | |
| 208 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 209 | return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType), |
| 209 | "Container to WriteBuffer must contain trivially copyable objects"); | 210 | buffer_index); |
| 210 | 211 | } else { | |
| 211 | return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType), | 212 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| 212 | buffer_index); | 213 | return WriteBuffer(&data, sizeof(T), buffer_index); |
| 214 | } | ||
| 213 | } | 215 | } |
| 214 | 216 | ||
| 215 | /// Helper function to get the size of the input buffer | 217 | /// Helper function to get the size of the input buffer |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 8dd4a2637..cabe8d418 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -145,7 +145,7 @@ struct KernelCore::Impl { | |||
| 145 | 145 | ||
| 146 | void InitializePreemption(KernelCore& kernel) { | 146 | void InitializePreemption(KernelCore& kernel) { |
| 147 | preemption_event = Core::Timing::CreateEvent( | 147 | preemption_event = Core::Timing::CreateEvent( |
| 148 | "PreemptionCallback", [this, &kernel](u64, std::chrono::nanoseconds) { | 148 | "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { |
| 149 | { | 149 | { |
| 150 | SchedulerLock lock(kernel); | 150 | SchedulerLock lock(kernel); |
| 151 | global_scheduler.PreemptThreads(); | 151 | global_scheduler.PreemptThreads(); |
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp index 5d6aac00f..a3fadb533 100644 --- a/src/core/hle/kernel/memory/page_table.cpp +++ b/src/core/hle/kernel/memory/page_table.cpp | |||
| @@ -604,7 +604,6 @@ ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_lis | |||
| 604 | if (const auto result{ | 604 | if (const auto result{ |
| 605 | Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; | 605 | Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; |
| 606 | result.IsError()) { | 606 | result.IsError()) { |
| 607 | const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()}; | ||
| 608 | const std::size_t num_pages{(addr - cur_addr) / PageSize}; | 607 | const std::size_t num_pages{(addr - cur_addr) / PageSize}; |
| 609 | 608 | ||
| 610 | ASSERT( | 609 | ASSERT( |
| @@ -852,11 +851,12 @@ ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { | |||
| 852 | return result; | 851 | return result; |
| 853 | } | 852 | } |
| 854 | 853 | ||
| 855 | block_manager->UpdateLock(addr, size / PageSize, | 854 | block_manager->UpdateLock( |
| 856 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { | 855 | addr, size / PageSize, |
| 857 | block->ShareToDevice(perm); | 856 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { |
| 858 | }, | 857 | block->ShareToDevice(perm); |
| 859 | perm); | 858 | }, |
| 859 | perm); | ||
| 860 | 860 | ||
| 861 | return RESULT_SUCCESS; | 861 | return RESULT_SUCCESS; |
| 862 | } | 862 | } |
| @@ -874,11 +874,12 @@ ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) | |||
| 874 | return result; | 874 | return result; |
| 875 | } | 875 | } |
| 876 | 876 | ||
| 877 | block_manager->UpdateLock(addr, size / PageSize, | 877 | block_manager->UpdateLock( |
| 878 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { | 878 | addr, size / PageSize, |
| 879 | block->UnshareToDevice(perm); | 879 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { |
| 880 | }, | 880 | block->UnshareToDevice(perm); |
| 881 | perm); | 881 | }, |
| 882 | perm); | ||
| 882 | 883 | ||
| 883 | return RESULT_SUCCESS; | 884 | return RESULT_SUCCESS; |
| 884 | } | 885 | } |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index f93e5e4b0..a4b234424 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -131,7 +131,8 @@ u32 GlobalScheduler::SelectThreads() { | |||
| 131 | u32 cores_needing_context_switch{}; | 131 | u32 cores_needing_context_switch{}; |
| 132 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 132 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 133 | Scheduler& sched = kernel.Scheduler(core); | 133 | Scheduler& sched = kernel.Scheduler(core); |
| 134 | ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); | 134 | ASSERT(top_threads[core] == nullptr || |
| 135 | static_cast<u32>(top_threads[core]->GetProcessorID()) == core); | ||
| 135 | if (update_thread(top_threads[core], sched)) { | 136 | if (update_thread(top_threads[core], sched)) { |
| 136 | cores_needing_context_switch |= (1ul << core); | 137 | cores_needing_context_switch |= (1ul << core); |
| 137 | } | 138 | } |
| @@ -663,32 +664,26 @@ void Scheduler::Reload() { | |||
| 663 | } | 664 | } |
| 664 | 665 | ||
| 665 | void Scheduler::SwitchContextStep2() { | 666 | void Scheduler::SwitchContextStep2() { |
| 666 | Thread* previous_thread = current_thread_prev.get(); | ||
| 667 | Thread* new_thread = selected_thread.get(); | ||
| 668 | |||
| 669 | // Load context of new thread | 667 | // Load context of new thread |
| 670 | Process* const previous_process = | 668 | if (selected_thread) { |
| 671 | previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; | 669 | ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, |
| 672 | |||
| 673 | if (new_thread) { | ||
| 674 | ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, | ||
| 675 | "Thread must be runnable."); | 670 | "Thread must be runnable."); |
| 676 | 671 | ||
| 677 | // Cancel any outstanding wakeup events for this thread | 672 | // Cancel any outstanding wakeup events for this thread |
| 678 | new_thread->SetIsRunning(true); | 673 | selected_thread->SetIsRunning(true); |
| 679 | new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | 674 | selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); |
| 680 | new_thread->SetWasRunning(false); | 675 | selected_thread->SetWasRunning(false); |
| 681 | 676 | ||
| 682 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | 677 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); |
| 683 | if (thread_owner_process != nullptr) { | 678 | if (thread_owner_process != nullptr) { |
| 684 | system.Kernel().MakeCurrentProcess(thread_owner_process); | 679 | system.Kernel().MakeCurrentProcess(thread_owner_process); |
| 685 | } | 680 | } |
| 686 | if (!new_thread->IsHLEThread()) { | 681 | if (!selected_thread->IsHLEThread()) { |
| 687 | Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); | 682 | Core::ARM_Interface& cpu_core = selected_thread->ArmInterface(); |
| 688 | cpu_core.LoadContext(new_thread->GetContext32()); | 683 | cpu_core.LoadContext(selected_thread->GetContext32()); |
| 689 | cpu_core.LoadContext(new_thread->GetContext64()); | 684 | cpu_core.LoadContext(selected_thread->GetContext64()); |
| 690 | cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); | 685 | cpu_core.SetTlsAddress(selected_thread->GetTLSAddress()); |
| 691 | cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | 686 | cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0()); |
| 692 | cpu_core.ChangeProcessorID(this->core_id); | 687 | cpu_core.ChangeProcessorID(this->core_id); |
| 693 | cpu_core.ClearExclusiveState(); | 688 | cpu_core.ClearExclusiveState(); |
| 694 | } | 689 | } |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index af22f4c33..7e6391c6c 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -33,8 +33,10 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern | |||
| 33 | std::string name) { | 33 | std::string name) { |
| 34 | std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; | 34 | std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; |
| 35 | 35 | ||
| 36 | session->request_event = Core::Timing::CreateEvent( | 36 | session->request_event = |
| 37 | name, [session](u64, std::chrono::nanoseconds) { session->CompleteSyncRequest(); }); | 37 | Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) { |
| 38 | session->CompleteSyncRequest(); | ||
| 39 | }); | ||
| 38 | session->name = std::move(name); | 40 | session->name = std::move(name); |
| 39 | session->parent = std::move(parent); | 41 | session->parent = std::move(parent); |
| 40 | 42 | ||
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 88b01b751..95f2446c9 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -16,14 +16,14 @@ namespace Kernel { | |||
| 16 | 16 | ||
| 17 | TimeManager::TimeManager(Core::System& system_) : system{system_} { | 17 | TimeManager::TimeManager(Core::System& system_) : system{system_} { |
| 18 | time_manager_event_type = Core::Timing::CreateEvent( | 18 | time_manager_event_type = Core::Timing::CreateEvent( |
| 19 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, std::chrono::nanoseconds) { | 19 | "Kernel::TimeManagerCallback", |
| 20 | SchedulerLock lock(system.Kernel()); | 20 | [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { |
| 21 | Handle proper_handle = static_cast<Handle>(thread_handle); | 21 | const SchedulerLock lock(system.Kernel()); |
| 22 | const auto proper_handle = static_cast<Handle>(thread_handle); | ||
| 22 | if (cancelled_events[proper_handle]) { | 23 | if (cancelled_events[proper_handle]) { |
| 23 | return; | 24 | return; |
| 24 | } | 25 | } |
| 25 | std::shared_ptr<Thread> thread = | 26 | auto thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); |
| 26 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | ||
| 27 | thread->OnWakeUp(); | 27 | thread->OnWakeUp(); |
| 28 | }); | 28 | }); |
| 29 | } | 29 | } |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 8ac856ec3..63e4aeca0 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -286,9 +286,7 @@ protected: | |||
| 286 | ProfileBase profile_base{}; | 286 | ProfileBase profile_base{}; |
| 287 | ProfileData data{}; | 287 | ProfileData data{}; |
| 288 | if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { | 288 | if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { |
| 289 | std::array<u8, sizeof(ProfileData)> raw_data; | 289 | ctx.WriteBuffer(data); |
| 290 | std::memcpy(raw_data.data(), &data, sizeof(ProfileData)); | ||
| 291 | ctx.WriteBuffer(raw_data); | ||
| 292 | IPC::ResponseBuilder rb{ctx, 16}; | 290 | IPC::ResponseBuilder rb{ctx, 16}; |
| 293 | rb.Push(RESULT_SUCCESS); | 291 | rb.Push(RESULT_SUCCESS); |
| 294 | rb.PushRaw(profile_base); | 292 | rb.PushRaw(profile_base); |
| @@ -333,7 +331,7 @@ protected: | |||
| 333 | std::vector<u8> buffer(size); | 331 | std::vector<u8> buffer(size); |
| 334 | image.ReadBytes(buffer.data(), buffer.size()); | 332 | image.ReadBytes(buffer.data(), buffer.size()); |
| 335 | 333 | ||
| 336 | ctx.WriteBuffer(buffer.data(), buffer.size()); | 334 | ctx.WriteBuffer(buffer); |
| 337 | rb.Push<u32>(size); | 335 | rb.Push<u32>(size); |
| 338 | } | 336 | } |
| 339 | 337 | ||
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index eb8c81645..a98d57b5c 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -58,7 +58,7 @@ ProfileManager::~ProfileManager() { | |||
| 58 | /// internal management of the users profiles | 58 | /// internal management of the users profiles |
| 59 | std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { | 59 | std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { |
| 60 | if (user_count >= MAX_USERS) { | 60 | if (user_count >= MAX_USERS) { |
| 61 | return {}; | 61 | return std::nullopt; |
| 62 | } | 62 | } |
| 63 | profiles[user_count] = profile; | 63 | profiles[user_count] = profile; |
| 64 | return user_count++; | 64 | return user_count++; |
| @@ -101,13 +101,14 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern | |||
| 101 | [&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) { | 101 | [&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) { |
| 102 | return ERROR_USER_ALREADY_EXISTS; | 102 | return ERROR_USER_ALREADY_EXISTS; |
| 103 | } | 103 | } |
| 104 | ProfileInfo profile; | 104 | |
| 105 | profile.user_uuid = uuid; | 105 | return AddUser({ |
| 106 | profile.username = username; | 106 | .user_uuid = uuid, |
| 107 | profile.data = {}; | 107 | .username = username, |
| 108 | profile.creation_time = 0x0; | 108 | .creation_time = 0, |
| 109 | profile.is_open = false; | 109 | .data = {}, |
| 110 | return AddUser(profile); | 110 | .is_open = false, |
| 111 | }); | ||
| 111 | } | 112 | } |
| 112 | 113 | ||
| 113 | /// Creates a new user on the system. This function allows a much simpler method of registration | 114 | /// Creates a new user on the system. This function allows a much simpler method of registration |
| @@ -126,7 +127,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) | |||
| 126 | 127 | ||
| 127 | std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | 128 | std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { |
| 128 | if (index >= MAX_USERS) { | 129 | if (index >= MAX_USERS) { |
| 129 | return {}; | 130 | return std::nullopt; |
| 130 | } | 131 | } |
| 131 | 132 | ||
| 132 | return profiles[index].user_uuid; | 133 | return profiles[index].user_uuid; |
| @@ -135,13 +136,13 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | |||
| 135 | /// Returns a users profile index based on their user id. | 136 | /// Returns a users profile index based on their user id. |
| 136 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 137 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 137 | if (!uuid) { | 138 | if (!uuid) { |
| 138 | return {}; | 139 | return std::nullopt; |
| 139 | } | 140 | } |
| 140 | 141 | ||
| 141 | const auto iter = std::find_if(profiles.begin(), profiles.end(), | 142 | const auto iter = std::find_if(profiles.begin(), profiles.end(), |
| 142 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); | 143 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); |
| 143 | if (iter == profiles.end()) { | 144 | if (iter == profiles.end()) { |
| 144 | return {}; | 145 | return std::nullopt; |
| 145 | } | 146 | } |
| 146 | 147 | ||
| 147 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); | 148 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); |
| @@ -339,7 +340,13 @@ void ProfileManager::ParseUserSaveFile() { | |||
| 339 | continue; | 340 | continue; |
| 340 | } | 341 | } |
| 341 | 342 | ||
| 342 | AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false}); | 343 | AddUser({ |
| 344 | .user_uuid = user.uuid, | ||
| 345 | .username = user.username, | ||
| 346 | .creation_time = user.timestamp, | ||
| 347 | .data = user.extra_data, | ||
| 348 | .is_open = false, | ||
| 349 | }); | ||
| 343 | } | 350 | } |
| 344 | 351 | ||
| 345 | std::stable_partition(profiles.begin(), profiles.end(), | 352 | std::stable_partition(profiles.begin(), profiles.end(), |
| @@ -350,11 +357,13 @@ void ProfileManager::WriteUserSaveFile() { | |||
| 350 | ProfileDataRaw raw{}; | 357 | ProfileDataRaw raw{}; |
| 351 | 358 | ||
| 352 | for (std::size_t i = 0; i < MAX_USERS; ++i) { | 359 | for (std::size_t i = 0; i < MAX_USERS; ++i) { |
| 353 | raw.users[i].username = profiles[i].username; | 360 | raw.users[i] = { |
| 354 | raw.users[i].uuid2 = profiles[i].user_uuid; | 361 | .uuid = profiles[i].user_uuid, |
| 355 | raw.users[i].uuid = profiles[i].user_uuid; | 362 | .uuid2 = profiles[i].user_uuid, |
| 356 | raw.users[i].timestamp = profiles[i].creation_time; | 363 | .timestamp = profiles[i].creation_time, |
| 357 | raw.users[i].extra_data = profiles[i].data; | 364 | .username = profiles[i].username, |
| 365 | .extra_data = profiles[i].data, | ||
| 366 | }; | ||
| 358 | } | 367 | } |
| 359 | 368 | ||
| 360 | const auto raw_path = | 369 | const auto raw_path = |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4e7a0bec9..7d92b25a3 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -378,7 +378,11 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& | |||
| 378 | } | 378 | } |
| 379 | 379 | ||
| 380 | void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { | 380 | void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { |
| 381 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 381 | IPC::RequestParser rp{ctx}; |
| 382 | const auto permission = rp.PopEnum<ScreenshotPermission>(); | ||
| 383 | LOG_DEBUG(Service_AM, "called, permission={}", permission); | ||
| 384 | |||
| 385 | screenshot_permission = permission; | ||
| 382 | 386 | ||
| 383 | IPC::ResponseBuilder rb{ctx, 2}; | 387 | IPC::ResponseBuilder rb{ctx, 2}; |
| 384 | rb.Push(RESULT_SUCCESS); | 388 | rb.Push(RESULT_SUCCESS); |
| @@ -1342,12 +1346,12 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { | |||
| 1342 | 1346 | ||
| 1343 | LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); | 1347 | LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); |
| 1344 | 1348 | ||
| 1345 | FileSys::SaveDataDescriptor descriptor{}; | 1349 | FileSys::SaveDataAttribute attribute{}; |
| 1346 | descriptor.title_id = system.CurrentProcess()->GetTitleID(); | 1350 | attribute.title_id = system.CurrentProcess()->GetTitleID(); |
| 1347 | descriptor.user_id = user_id; | 1351 | attribute.user_id = user_id; |
| 1348 | descriptor.type = FileSys::SaveDataType::SaveData; | 1352 | attribute.type = FileSys::SaveDataType::SaveData; |
| 1349 | const auto res = system.GetFileSystemController().CreateSaveData( | 1353 | const auto res = system.GetFileSystemController().CreateSaveData( |
| 1350 | FileSys::SaveDataSpaceId::NandUser, descriptor); | 1354 | FileSys::SaveDataSpaceId::NandUser, attribute); |
| 1351 | 1355 | ||
| 1352 | IPC::ResponseBuilder rb{ctx, 4}; | 1356 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1353 | rb.Push(res.Code()); | 1357 | rb.Push(res.Code()); |
| @@ -1405,7 +1409,6 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { | |||
| 1405 | // Get supported languages from NACP, if possible | 1409 | // Get supported languages from NACP, if possible |
| 1406 | // Default to 0 (all languages supported) | 1410 | // Default to 0 (all languages supported) |
| 1407 | u32 supported_languages = 0; | 1411 | u32 supported_languages = 0; |
| 1408 | FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; | ||
| 1409 | 1412 | ||
| 1410 | const auto res = [this] { | 1413 | const auto res = [this] { |
| 1411 | const auto title_id = system.CurrentProcess()->GetTitleID(); | 1414 | const auto title_id = system.CurrentProcess()->GetTitleID(); |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 6cfb11b48..6e69796ec 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -149,6 +149,12 @@ private: | |||
| 149 | void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); | 149 | void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); |
| 150 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); | 150 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); |
| 151 | 151 | ||
| 152 | enum class ScreenshotPermission : u32 { | ||
| 153 | Inherit = 0, | ||
| 154 | Enable = 1, | ||
| 155 | Disable = 2, | ||
| 156 | }; | ||
| 157 | |||
| 152 | Core::System& system; | 158 | Core::System& system; |
| 153 | std::shared_ptr<NVFlinger::NVFlinger> nvflinger; | 159 | std::shared_ptr<NVFlinger::NVFlinger> nvflinger; |
| 154 | Kernel::EventPair launchable_event; | 160 | Kernel::EventPair launchable_event; |
| @@ -157,6 +163,7 @@ private: | |||
| 157 | u32 idle_time_detection_extension = 0; | 163 | u32 idle_time_detection_extension = 0; |
| 158 | u64 num_fatal_sections_entered = 0; | 164 | u64 num_fatal_sections_entered = 0; |
| 159 | bool is_auto_sleep_disabled = false; | 165 | bool is_auto_sleep_disabled = false; |
| 166 | ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit; | ||
| 160 | }; | 167 | }; |
| 161 | 168 | ||
| 162 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { | 169 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { |
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index fbe3686ae..289da2619 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp | |||
| @@ -13,11 +13,23 @@ | |||
| 13 | 13 | ||
| 14 | namespace Service::AM::Applets { | 14 | namespace Service::AM::Applets { |
| 15 | 15 | ||
| 16 | namespace { | ||
| 17 | enum class Request : u32 { | ||
| 18 | Finalize = 0x4, | ||
| 19 | SetUserWordInfo = 0x6, | ||
| 20 | SetCustomizeDic = 0x7, | ||
| 21 | Calc = 0xa, | ||
| 22 | SetCustomizedDictionaries = 0xb, | ||
| 23 | UnsetCustomizedDictionaries = 0xc, | ||
| 24 | UnknownD = 0xd, | ||
| 25 | UnknownE = 0xe, | ||
| 26 | }; | ||
| 27 | constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8; | ||
| 16 | constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; | 28 | constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; |
| 17 | constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; | 29 | constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; |
| 18 | constexpr std::size_t DEFAULT_MAX_LENGTH = 500; | 30 | constexpr std::size_t DEFAULT_MAX_LENGTH = 500; |
| 19 | constexpr bool INTERACTIVE_STATUS_OK = false; | 31 | constexpr bool INTERACTIVE_STATUS_OK = false; |
| 20 | 32 | } // Anonymous namespace | |
| 21 | static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( | 33 | static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( |
| 22 | KeyboardConfig config, std::u16string initial_text) { | 34 | KeyboardConfig config, std::u16string initial_text) { |
| 23 | Core::Frontend::SoftwareKeyboardParameters params{}; | 35 | Core::Frontend::SoftwareKeyboardParameters params{}; |
| @@ -47,6 +59,7 @@ SoftwareKeyboard::~SoftwareKeyboard() = default; | |||
| 47 | 59 | ||
| 48 | void SoftwareKeyboard::Initialize() { | 60 | void SoftwareKeyboard::Initialize() { |
| 49 | complete = false; | 61 | complete = false; |
| 62 | is_inline = false; | ||
| 50 | initial_text.clear(); | 63 | initial_text.clear(); |
| 51 | final_data.clear(); | 64 | final_data.clear(); |
| 52 | 65 | ||
| @@ -56,6 +69,11 @@ void SoftwareKeyboard::Initialize() { | |||
| 56 | ASSERT(keyboard_config_storage != nullptr); | 69 | ASSERT(keyboard_config_storage != nullptr); |
| 57 | const auto& keyboard_config = keyboard_config_storage->GetData(); | 70 | const auto& keyboard_config = keyboard_config_storage->GetData(); |
| 58 | 71 | ||
| 72 | if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) { | ||
| 73 | is_inline = true; | ||
| 74 | return; | ||
| 75 | } | ||
| 76 | |||
| 59 | ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); | 77 | ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); |
| 60 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); | 78 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); |
| 61 | 79 | ||
| @@ -87,16 +105,32 @@ void SoftwareKeyboard::ExecuteInteractive() { | |||
| 87 | const auto storage = broker.PopInteractiveDataToApplet(); | 105 | const auto storage = broker.PopInteractiveDataToApplet(); |
| 88 | ASSERT(storage != nullptr); | 106 | ASSERT(storage != nullptr); |
| 89 | const auto data = storage->GetData(); | 107 | const auto data = storage->GetData(); |
| 90 | const auto status = static_cast<bool>(data[0]); | 108 | if (!is_inline) { |
| 91 | 109 | const auto status = static_cast<bool>(data[0]); | |
| 92 | if (status == INTERACTIVE_STATUS_OK) { | 110 | if (status == INTERACTIVE_STATUS_OK) { |
| 93 | complete = true; | 111 | complete = true; |
| 112 | } else { | ||
| 113 | std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; | ||
| 114 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); | ||
| 115 | frontend.SendTextCheckDialog( | ||
| 116 | Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), | ||
| 117 | [this] { broker.SignalStateChanged(); }); | ||
| 118 | } | ||
| 94 | } else { | 119 | } else { |
| 95 | std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; | 120 | Request request{}; |
| 96 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); | 121 | std::memcpy(&request, data.data(), sizeof(Request)); |
| 97 | frontend.SendTextCheckDialog( | 122 | |
| 98 | Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), | 123 | switch (request) { |
| 99 | [this] { broker.SignalStateChanged(); }); | 124 | case Request::Calc: { |
| 125 | broker.PushNormalDataFromApplet( | ||
| 126 | std::make_shared<IStorage>(std::move(std::vector<u8>{1}))); | ||
| 127 | broker.SignalStateChanged(); | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | default: | ||
| 131 | UNIMPLEMENTED_MSG("Request {:X} is not implemented", request); | ||
| 132 | break; | ||
| 133 | } | ||
| 100 | } | 134 | } |
| 101 | } | 135 | } |
| 102 | 136 | ||
| @@ -108,9 +142,10 @@ void SoftwareKeyboard::Execute() { | |||
| 108 | } | 142 | } |
| 109 | 143 | ||
| 110 | const auto parameters = ConvertToFrontendParameters(config, initial_text); | 144 | const auto parameters = ConvertToFrontendParameters(config, initial_text); |
| 111 | 145 | if (!is_inline) { | |
| 112 | frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, | 146 | frontend.RequestText( |
| 113 | parameters); | 147 | [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters); |
| 148 | } | ||
| 114 | } | 149 | } |
| 115 | 150 | ||
| 116 | void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { | 151 | void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { |
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index ef4801fc6..5a3824b5a 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h | |||
| @@ -78,6 +78,7 @@ private: | |||
| 78 | KeyboardConfig config; | 78 | KeyboardConfig config; |
| 79 | std::u16string initial_text; | 79 | std::u16string initial_text; |
| 80 | bool complete = false; | 80 | bool complete = false; |
| 81 | bool is_inline = false; | ||
| 81 | std::vector<u8> final_data; | 82 | std::vector<u8> final_data; |
| 82 | }; | 83 | }; |
| 83 | 84 | ||
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 9f30e167d..4157fbf39 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp | |||
| @@ -551,7 +551,8 @@ void WebBrowser::ExecuteShop() { | |||
| 551 | } | 551 | } |
| 552 | 552 | ||
| 553 | void WebBrowser::ExecuteOffline() { | 553 | void WebBrowser::ExecuteOffline() { |
| 554 | frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); | 554 | frontend.OpenPageLocal( |
| 555 | filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); | ||
| 555 | } | 556 | } |
| 556 | 557 | ||
| 557 | } // namespace Service::AM::Applets | 558 | } // namespace Service::AM::Applets |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 106e89743..9b4910e53 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -71,7 +71,7 @@ public: | |||
| 71 | 71 | ||
| 72 | stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, | 72 | stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, |
| 73 | audio_params.channel_count, std::move(unique_name), | 73 | audio_params.channel_count, std::move(unique_name), |
| 74 | [=]() { buffer_event.writable->Signal(); }); | 74 | [this] { buffer_event.writable->Signal(); }); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | private: | 77 | private: |
| @@ -206,7 +206,7 @@ private: | |||
| 206 | AudioCore::StreamPtr stream; | 206 | AudioCore::StreamPtr stream; |
| 207 | std::string device_name; | 207 | std::string device_name; |
| 208 | 208 | ||
| 209 | [[maybe_unused]] AudoutParams audio_params {}; | 209 | [[maybe_unused]] AudoutParams audio_params{}; |
| 210 | 210 | ||
| 211 | /// This is the event handle used to check if the audio buffer was released | 211 | /// This is the event handle used to check if the audio buffer was released |
| 212 | Kernel::EventPair buffer_event; | 212 | Kernel::EventPair buffer_event; |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index d19513cbb..f1d81602c 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -92,7 +92,7 @@ private: | |||
| 92 | if (performance) { | 92 | if (performance) { |
| 93 | rb.Push<u64>(*performance); | 93 | rb.Push<u64>(*performance); |
| 94 | } | 94 | } |
| 95 | ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); | 95 | ctx.WriteBuffer(samples); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, | 98 | bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, |
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index d29e78d7e..51c2ba964 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp | |||
| @@ -365,8 +365,7 @@ bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) | |||
| 365 | 365 | ||
| 366 | std::thread([this, title, &progress] { | 366 | std::thread([this, title, &progress] { |
| 367 | SynchronizeInternal(applet_manager, dir_getter, title, progress); | 367 | SynchronizeInternal(applet_manager, dir_getter, title, progress); |
| 368 | }) | 368 | }).detach(); |
| 369 | .detach(); | ||
| 370 | 369 | ||
| 371 | return true; | 370 | return true; |
| 372 | } | 371 | } |
| @@ -377,8 +376,7 @@ bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, | |||
| 377 | 376 | ||
| 378 | std::thread([this, title, name, &progress] { | 377 | std::thread([this, title, name, &progress] { |
| 379 | SynchronizeInternal(applet_manager, dir_getter, title, progress, name); | 378 | SynchronizeInternal(applet_manager, dir_getter, title, progress, name); |
| 380 | }) | 379 | }).detach(); |
| 381 | .detach(); | ||
| 382 | 380 | ||
| 383 | return true; | 381 | return true; |
| 384 | } | 382 | } |
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 603b64d4f..db0e06ca1 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -112,7 +112,7 @@ private: | |||
| 112 | void GetImpl(Kernel::HLERequestContext& ctx) { | 112 | void GetImpl(Kernel::HLERequestContext& ctx) { |
| 113 | LOG_DEBUG(Service_BCAT, "called"); | 113 | LOG_DEBUG(Service_BCAT, "called"); |
| 114 | 114 | ||
| 115 | ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl)); | 115 | ctx.WriteBuffer(impl); |
| 116 | 116 | ||
| 117 | IPC::ResponseBuilder rb{ctx, 2}; | 117 | IPC::ResponseBuilder rb{ctx, 2}; |
| 118 | rb.Push(RESULT_SUCCESS); | 118 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index a41c73c48..c2737a365 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -160,7 +160,7 @@ private: | |||
| 160 | return; | 160 | return; |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | ctx.WriteBuffer(key.data(), key.size()); | 163 | ctx.WriteBuffer(key); |
| 164 | 164 | ||
| 165 | IPC::ResponseBuilder rb{ctx, 2}; | 165 | IPC::ResponseBuilder rb{ctx, 2}; |
| 166 | rb.Push(RESULT_SUCCESS); | 166 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index c66124998..4490f8e4c 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -311,7 +311,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( | |||
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( | 313 | ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( |
| 314 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const { | 314 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const { |
| 315 | LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", | 315 | LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", |
| 316 | static_cast<u8>(space), save_struct.DebugInfo()); | 316 | static_cast<u8>(space), save_struct.DebugInfo()); |
| 317 | 317 | ||
| @@ -323,15 +323,15 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( | |||
| 323 | } | 323 | } |
| 324 | 324 | ||
| 325 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( | 325 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( |
| 326 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const { | 326 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const { |
| 327 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", | 327 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", |
| 328 | static_cast<u8>(space), descriptor.DebugInfo()); | 328 | static_cast<u8>(space), attribute.DebugInfo()); |
| 329 | 329 | ||
| 330 | if (save_data_factory == nullptr) { | 330 | if (save_data_factory == nullptr) { |
| 331 | return FileSys::ERROR_ENTITY_NOT_FOUND; | 331 | return FileSys::ERROR_ENTITY_NOT_FOUND; |
| 332 | } | 332 | } |
| 333 | 333 | ||
| 334 | return save_data_factory->Open(space, descriptor); | 334 | return save_data_factory->Open(space, attribute); |
| 335 | } | 335 | } |
| 336 | 336 | ||
| 337 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( | 337 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( |
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 1b0a6a949..6dbbf0b2b 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -31,7 +31,7 @@ enum class SaveDataSpaceId : u8; | |||
| 31 | enum class SaveDataType : u8; | 31 | enum class SaveDataType : u8; |
| 32 | enum class StorageId : u8; | 32 | enum class StorageId : u8; |
| 33 | 33 | ||
| 34 | struct SaveDataDescriptor; | 34 | struct SaveDataAttribute; |
| 35 | struct SaveDataSize; | 35 | struct SaveDataSize; |
| 36 | } // namespace FileSys | 36 | } // namespace FileSys |
| 37 | 37 | ||
| @@ -69,9 +69,9 @@ public: | |||
| 69 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | 69 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, |
| 70 | FileSys::ContentRecordType type) const; | 70 | FileSys::ContentRecordType type) const; |
| 71 | ResultVal<FileSys::VirtualDir> CreateSaveData( | 71 | ResultVal<FileSys::VirtualDir> CreateSaveData( |
| 72 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; | 72 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const; |
| 73 | ResultVal<FileSys::VirtualDir> OpenSaveData( | 73 | ResultVal<FileSys::VirtualDir> OpenSaveData( |
| 74 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; | 74 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const; |
| 75 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; | 75 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; |
| 76 | ResultVal<FileSys::VirtualDir> OpenSDMC() const; | 76 | ResultVal<FileSys::VirtualDir> OpenSDMC() const; |
| 77 | ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; | 77 | ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 20c331b77..26fd87f58 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -696,8 +696,8 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) | |||
| 696 | {67, nullptr, "FindSaveDataWithFilter"}, | 696 | {67, nullptr, "FindSaveDataWithFilter"}, |
| 697 | {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, | 697 | {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, |
| 698 | {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, | 698 | {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, |
| 699 | {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, | 699 | {70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, |
| 700 | {71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, | 700 | {71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, |
| 701 | {80, nullptr, "OpenSaveDataMetaFile"}, | 701 | {80, nullptr, "OpenSaveDataMetaFile"}, |
| 702 | {81, nullptr, "OpenSaveDataTransferManager"}, | 702 | {81, nullptr, "OpenSaveDataTransferManager"}, |
| 703 | {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, | 703 | {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, |
| @@ -812,7 +812,7 @@ void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 812 | void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | 812 | void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { |
| 813 | IPC::RequestParser rp{ctx}; | 813 | IPC::RequestParser rp{ctx}; |
| 814 | 814 | ||
| 815 | auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); | 815 | auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>(); |
| 816 | [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); | 816 | [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); |
| 817 | u128 uid = rp.PopRaw<u128>(); | 817 | u128 uid = rp.PopRaw<u128>(); |
| 818 | 818 | ||
| @@ -826,17 +826,18 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 826 | } | 826 | } |
| 827 | 827 | ||
| 828 | void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | 828 | void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { |
| 829 | LOG_INFO(Service_FS, "called."); | 829 | IPC::RequestParser rp{ctx}; |
| 830 | 830 | ||
| 831 | struct Parameters { | 831 | struct Parameters { |
| 832 | FileSys::SaveDataSpaceId save_data_space_id; | 832 | FileSys::SaveDataSpaceId space_id; |
| 833 | FileSys::SaveDataDescriptor descriptor; | 833 | FileSys::SaveDataAttribute attribute; |
| 834 | }; | 834 | }; |
| 835 | 835 | ||
| 836 | IPC::RequestParser rp{ctx}; | ||
| 837 | const auto parameters = rp.PopRaw<Parameters>(); | 836 | const auto parameters = rp.PopRaw<Parameters>(); |
| 838 | 837 | ||
| 839 | auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor); | 838 | LOG_INFO(Service_FS, "called."); |
| 839 | |||
| 840 | auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute); | ||
| 840 | if (dir.Failed()) { | 841 | if (dir.Failed()) { |
| 841 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; | 842 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; |
| 842 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); | 843 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); |
| @@ -844,13 +845,18 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 844 | } | 845 | } |
| 845 | 846 | ||
| 846 | FileSys::StorageId id; | 847 | FileSys::StorageId id; |
| 847 | if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) { | 848 | |
| 849 | switch (parameters.space_id) { | ||
| 850 | case FileSys::SaveDataSpaceId::NandUser: | ||
| 848 | id = FileSys::StorageId::NandUser; | 851 | id = FileSys::StorageId::NandUser; |
| 849 | } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem || | 852 | break; |
| 850 | parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) { | 853 | case FileSys::SaveDataSpaceId::SdCardSystem: |
| 854 | case FileSys::SaveDataSpaceId::SdCardUser: | ||
| 851 | id = FileSys::StorageId::SdCard; | 855 | id = FileSys::StorageId::SdCard; |
| 852 | } else { | 856 | break; |
| 857 | case FileSys::SaveDataSpaceId::NandSystem: | ||
| 853 | id = FileSys::StorageId::NandSystem; | 858 | id = FileSys::StorageId::NandSystem; |
| 859 | break; | ||
| 854 | } | 860 | } |
| 855 | 861 | ||
| 856 | auto filesystem = | 862 | auto filesystem = |
| @@ -876,22 +882,31 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& | |||
| 876 | rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); | 882 | rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); |
| 877 | } | 883 | } |
| 878 | 884 | ||
| 879 | void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 885 | void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx) { |
| 880 | IPC::RequestParser rp{ctx}; | 886 | LOG_WARNING(Service_FS, "(STUBBED) called."); |
| 881 | log_mode = rp.PopEnum<LogMode>(); | ||
| 882 | |||
| 883 | LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode)); | ||
| 884 | 887 | ||
| 885 | IPC::ResponseBuilder rb{ctx, 2}; | 888 | IPC::ResponseBuilder rb{ctx, 2}; |
| 886 | rb.Push(RESULT_SUCCESS); | 889 | rb.Push(RESULT_SUCCESS); |
| 887 | } | 890 | } |
| 888 | 891 | ||
| 889 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 892 | void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute( |
| 890 | LOG_DEBUG(Service_FS, "called"); | 893 | Kernel::HLERequestContext& ctx) { |
| 894 | IPC::RequestParser rp{ctx}; | ||
| 895 | |||
| 896 | struct Parameters { | ||
| 897 | FileSys::SaveDataSpaceId space_id; | ||
| 898 | FileSys::SaveDataAttribute attribute; | ||
| 899 | }; | ||
| 900 | |||
| 901 | const auto parameters = rp.PopRaw<Parameters>(); | ||
| 902 | // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData | ||
| 903 | constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None); | ||
| 904 | |||
| 905 | LOG_WARNING(Service_FS, "(STUBBED) called, flags={}", flags); | ||
| 891 | 906 | ||
| 892 | IPC::ResponseBuilder rb{ctx, 3}; | 907 | IPC::ResponseBuilder rb{ctx, 3}; |
| 893 | rb.Push(RESULT_SUCCESS); | 908 | rb.Push(RESULT_SUCCESS); |
| 894 | rb.PushEnum(log_mode); | 909 | rb.Push(flags); |
| 895 | } | 910 | } |
| 896 | 911 | ||
| 897 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | 912 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { |
| @@ -966,6 +981,24 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct | |||
| 966 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); | 981 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); |
| 967 | } | 982 | } |
| 968 | 983 | ||
| 984 | void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | ||
| 985 | IPC::RequestParser rp{ctx}; | ||
| 986 | log_mode = rp.PopEnum<LogMode>(); | ||
| 987 | |||
| 988 | LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode)); | ||
| 989 | |||
| 990 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 991 | rb.Push(RESULT_SUCCESS); | ||
| 992 | } | ||
| 993 | |||
| 994 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | ||
| 995 | LOG_DEBUG(Service_FS, "called"); | ||
| 996 | |||
| 997 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 998 | rb.Push(RESULT_SUCCESS); | ||
| 999 | rb.PushEnum(log_mode); | ||
| 1000 | } | ||
| 1001 | |||
| 969 | void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { | 1002 | void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { |
| 970 | const auto raw = ctx.ReadBuffer(); | 1003 | const auto raw = ctx.ReadBuffer(); |
| 971 | auto log = Common::StringFromFixedZeroTerminatedBuffer( | 1004 | auto log = Common::StringFromFixedZeroTerminatedBuffer( |
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index dfb3e395b..4964e874e 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h | |||
| @@ -43,11 +43,13 @@ private: | |||
| 43 | void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx); | 43 | void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx); |
| 44 | void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); | 44 | void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); |
| 45 | void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); | 45 | void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); |
| 46 | void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | 46 | void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx); |
| 47 | void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | 47 | void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(Kernel::HLERequestContext& ctx); |
| 48 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | 48 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); |
| 49 | void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); | 49 | void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); |
| 50 | void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | 50 | void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); |
| 51 | void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | ||
| 52 | void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | ||
| 51 | void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); | 53 | void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); |
| 52 | void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); | 54 | void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); |
| 53 | void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); | 55 | void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index cb35919e9..ad251ed4a 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -39,33 +39,36 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, | |||
| 39 | 39 | ||
| 40 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 40 | cur_entry.sampling_number = last_entry.sampling_number + 1; |
| 41 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 41 | cur_entry.sampling_number2 = cur_entry.sampling_number; |
| 42 | cur_entry.attribute.connected.Assign(1); | ||
| 43 | auto& pad = cur_entry.pad_state; | ||
| 44 | 42 | ||
| 45 | using namespace Settings::NativeButton; | 43 | if (Settings::values.debug_pad_enabled) { |
| 46 | pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); | 44 | cur_entry.attribute.connected.Assign(1); |
| 47 | pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); | 45 | auto& pad = cur_entry.pad_state; |
| 48 | pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 49 | pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 50 | pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 51 | pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 52 | pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 53 | pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 54 | pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 55 | pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 56 | pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 57 | pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 58 | pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 59 | pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 60 | 46 | ||
| 61 | const auto [stick_l_x_f, stick_l_y_f] = | 47 | using namespace Settings::NativeButton; |
| 62 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | 48 | pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); |
| 63 | const auto [stick_r_x_f, stick_r_y_f] = | 49 | pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); |
| 64 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | 50 | pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); |
| 65 | cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | 51 | pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); |
| 66 | cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | 52 | pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); |
| 67 | cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | 53 | pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); |
| 68 | cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | 54 | pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); |
| 55 | pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 56 | pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 57 | pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 58 | pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 59 | pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 60 | pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 61 | pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 62 | |||
| 63 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 64 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 65 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 66 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 67 | cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | ||
| 68 | cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | ||
| 69 | cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | ||
| 70 | cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | ||
| 71 | } | ||
| 69 | 72 | ||
| 70 | std::memcpy(data, &shared_memory, sizeof(SharedMemory)); | 73 | std::memcpy(data, &shared_memory, sizeof(SharedMemory)); |
| 71 | } | 74 | } |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index feae89525..0b896d5ad 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -40,15 +40,16 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, | |||
| 40 | 40 | ||
| 41 | cur_entry.key.fill(0); | 41 | cur_entry.key.fill(0); |
| 42 | cur_entry.modifier = 0; | 42 | cur_entry.modifier = 0; |
| 43 | 43 | if (Settings::values.keyboard_enabled) { | |
| 44 | for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { | 44 | for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { |
| 45 | cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); | 45 | cur_entry.key[i / KEYS_PER_BYTE] |= |
| 46 | } | 46 | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); |
| 47 | 47 | } | |
| 48 | for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { | 48 | |
| 49 | cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); | 49 | for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { |
| 50 | cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); | ||
| 51 | } | ||
| 50 | } | 52 | } |
| 51 | |||
| 52 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); | 53 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); |
| 53 | } | 54 | } |
| 54 | 55 | ||
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 680290cbd..1e95b7580 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -77,8 +77,9 @@ IAppletResource::IAppletResource(Core::System& system) | |||
| 77 | 77 | ||
| 78 | // Register update callbacks | 78 | // Register update callbacks |
| 79 | pad_update_event = Core::Timing::CreateEvent( | 79 | pad_update_event = Core::Timing::CreateEvent( |
| 80 | "HID::UpdatePadCallback", [this](u64 userdata, std::chrono::nanoseconds ns_late) { | 80 | "HID::UpdatePadCallback", |
| 81 | UpdateControllers(userdata, ns_late); | 81 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 82 | UpdateControllers(user_data, ns_late); | ||
| 82 | }); | 83 | }); |
| 83 | 84 | ||
| 84 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) | 85 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) |
| @@ -108,7 +109,8 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | |||
| 108 | rb.PushCopyObjects(shared_mem); | 109 | rb.PushCopyObjects(shared_mem); |
| 109 | } | 110 | } |
| 110 | 111 | ||
| 111 | void IAppletResource::UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late) { | 112 | void IAppletResource::UpdateControllers(std::uintptr_t user_data, |
| 113 | std::chrono::nanoseconds ns_late) { | ||
| 112 | auto& core_timing = system.CoreTiming(); | 114 | auto& core_timing = system.CoreTiming(); |
| 113 | 115 | ||
| 114 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); | 116 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index c6f0a2584..efb07547f 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -64,7 +64,7 @@ private: | |||
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); | 66 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); |
| 67 | void UpdateControllers(u64 userdata, std::chrono::nanoseconds ns_late); | 67 | void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 68 | 68 | ||
| 69 | std::shared_ptr<Kernel::SharedMemory> shared_mem; | 69 | std::shared_ptr<Kernel::SharedMemory> shared_mem; |
| 70 | 70 | ||
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 64a526b9e..d8cd10e31 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -310,7 +310,7 @@ public: | |||
| 310 | 310 | ||
| 311 | ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress, | 311 | ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress, |
| 312 | u64 size) const { | 312 | u64 size) const { |
| 313 | for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { | 313 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { |
| 314 | auto& page_table{process->PageTable()}; | 314 | auto& page_table{process->PageTable()}; |
| 315 | const VAddr addr{GetRandomMapRegion(page_table, size)}; | 315 | const VAddr addr{GetRandomMapRegion(page_table, size)}; |
| 316 | const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; | 316 | const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; |
| @@ -331,8 +331,7 @@ public: | |||
| 331 | 331 | ||
| 332 | ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size, | 332 | ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size, |
| 333 | VAddr bss_addr, std::size_t bss_size, std::size_t size) const { | 333 | VAddr bss_addr, std::size_t bss_size, std::size_t size) const { |
| 334 | 334 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { | |
| 335 | for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { | ||
| 336 | auto& page_table{process->PageTable()}; | 335 | auto& page_table{process->PageTable()}; |
| 337 | VAddr addr{}; | 336 | VAddr addr{}; |
| 338 | 337 | ||
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 4b79eb81d..5e2d769a4 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -127,7 +127,7 @@ private: | |||
| 127 | const u32 array_size = rp.Pop<u32>(); | 127 | const u32 array_size = rp.Pop<u32>(); |
| 128 | LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); | 128 | LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); |
| 129 | 129 | ||
| 130 | ctx.WriteBuffer(&device_handle, sizeof(device_handle)); | 130 | ctx.WriteBuffer(device_handle); |
| 131 | 131 | ||
| 132 | IPC::ResponseBuilder rb{ctx, 3}; | 132 | IPC::ResponseBuilder rb{ctx, 3}; |
| 133 | rb.Push(RESULT_SUCCESS); | 133 | rb.Push(RESULT_SUCCESS); |
| @@ -220,7 +220,7 @@ private: | |||
| 220 | 220 | ||
| 221 | tag_info.protocol = 1; // TODO(ogniK): Figure out actual values | 221 | tag_info.protocol = 1; // TODO(ogniK): Figure out actual values |
| 222 | tag_info.tag_type = 2; | 222 | tag_info.tag_type = 2; |
| 223 | ctx.WriteBuffer(&tag_info, sizeof(TagInfo)); | 223 | ctx.WriteBuffer(tag_info); |
| 224 | rb.Push(RESULT_SUCCESS); | 224 | rb.Push(RESULT_SUCCESS); |
| 225 | } | 225 | } |
| 226 | 226 | ||
| @@ -237,7 +237,7 @@ private: | |||
| 237 | 237 | ||
| 238 | IPC::ResponseBuilder rb{ctx, 2}; | 238 | IPC::ResponseBuilder rb{ctx, 2}; |
| 239 | auto amiibo = nfp_interface.GetAmiiboBuffer(); | 239 | auto amiibo = nfp_interface.GetAmiiboBuffer(); |
| 240 | ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info)); | 240 | ctx.WriteBuffer(amiibo.model_info); |
| 241 | rb.Push(RESULT_SUCCESS); | 241 | rb.Push(RESULT_SUCCESS); |
| 242 | } | 242 | } |
| 243 | 243 | ||
| @@ -283,7 +283,7 @@ private: | |||
| 283 | 283 | ||
| 284 | CommonInfo common_info{}; | 284 | CommonInfo common_info{}; |
| 285 | common_info.application_area_size = 0; | 285 | common_info.application_area_size = 0; |
| 286 | ctx.WriteBuffer(&common_info, sizeof(CommonInfo)); | 286 | ctx.WriteBuffer(common_info); |
| 287 | 287 | ||
| 288 | IPC::ResponseBuilder rb{ctx, 2}; | 288 | IPC::ResponseBuilder rb{ctx, 2}; |
| 289 | rb.Push(RESULT_SUCCESS); | 289 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 195421cc0..d4ba88147 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -16,11 +16,12 @@ | |||
| 16 | #include "video_core/renderer_base.h" | 16 | #include "video_core/renderer_base.h" |
| 17 | 17 | ||
| 18 | namespace Service::Nvidia::Devices { | 18 | namespace Service::Nvidia::Devices { |
| 19 | |||
| 19 | namespace NvErrCodes { | 20 | namespace NvErrCodes { |
| 20 | enum { | 21 | constexpr u32 Success{}; |
| 21 | InvalidNmapHandle = -22, | 22 | constexpr u32 OutOfMemory{static_cast<u32>(-12)}; |
| 22 | }; | 23 | constexpr u32 InvalidInput{static_cast<u32>(-22)}; |
| 23 | } | 24 | } // namespace NvErrCodes |
| 24 | 25 | ||
| 25 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | 26 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) |
| 26 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | 27 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} |
| @@ -49,8 +50,9 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std: | |||
| 49 | break; | 50 | break; |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) | 53 | if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) { |
| 53 | return Remap(input, output); | 54 | return Remap(input, output); |
| 55 | } | ||
| 54 | 56 | ||
| 55 | UNIMPLEMENTED_MSG("Unimplemented ioctl command"); | 57 | UNIMPLEMENTED_MSG("Unimplemented ioctl command"); |
| 56 | return 0; | 58 | return 0; |
| @@ -59,6 +61,7 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std: | |||
| 59 | u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { | 61 | u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { |
| 60 | IoctlInitalizeEx params{}; | 62 | IoctlInitalizeEx params{}; |
| 61 | std::memcpy(¶ms, input.data(), input.size()); | 63 | std::memcpy(¶ms, input.data(), input.size()); |
| 64 | |||
| 62 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); | 65 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); |
| 63 | 66 | ||
| 64 | return 0; | 67 | return 0; |
| @@ -67,53 +70,61 @@ u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 67 | u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { | 70 | u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { |
| 68 | IoctlAllocSpace params{}; | 71 | IoctlAllocSpace params{}; |
| 69 | std::memcpy(¶ms, input.data(), input.size()); | 72 | std::memcpy(¶ms, input.data(), input.size()); |
| 73 | |||
| 70 | LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, | 74 | LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, |
| 71 | params.page_size, params.flags); | 75 | params.page_size, params.flags); |
| 72 | 76 | ||
| 73 | auto& gpu = system.GPU(); | 77 | const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; |
| 74 | const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; | 78 | if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { |
| 75 | if (params.flags & 1) { | 79 | params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); |
| 76 | params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1); | ||
| 77 | } else { | 80 | } else { |
| 78 | params.offset = gpu.MemoryManager().AllocateSpace(size, params.align); | 81 | params.offset = system.GPU().MemoryManager().Allocate(size, params.align); |
| 82 | } | ||
| 83 | |||
| 84 | auto result{NvErrCodes::Success}; | ||
| 85 | if (!params.offset) { | ||
| 86 | LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); | ||
| 87 | result = NvErrCodes::OutOfMemory; | ||
| 79 | } | 88 | } |
| 80 | 89 | ||
| 81 | std::memcpy(output.data(), ¶ms, output.size()); | 90 | std::memcpy(output.data(), ¶ms, output.size()); |
| 82 | return 0; | 91 | return result; |
| 83 | } | 92 | } |
| 84 | 93 | ||
| 85 | u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { | 94 | u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { |
| 86 | std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry); | 95 | const auto num_entries = input.size() / sizeof(IoctlRemapEntry); |
| 87 | 96 | ||
| 88 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); | 97 | LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); |
| 89 | 98 | ||
| 99 | auto result{NvErrCodes::Success}; | ||
| 90 | std::vector<IoctlRemapEntry> entries(num_entries); | 100 | std::vector<IoctlRemapEntry> entries(num_entries); |
| 91 | std::memcpy(entries.data(), input.data(), input.size()); | 101 | std::memcpy(entries.data(), input.data(), input.size()); |
| 92 | 102 | ||
| 93 | auto& gpu = system.GPU(); | ||
| 94 | for (const auto& entry : entries) { | 103 | for (const auto& entry : entries) { |
| 95 | LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", | 104 | LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", |
| 96 | entry.offset, entry.nvmap_handle, entry.pages); | 105 | entry.offset, entry.nvmap_handle, entry.pages); |
| 97 | GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10; | 106 | |
| 98 | auto object = nvmap_dev->GetObject(entry.nvmap_handle); | 107 | const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; |
| 99 | if (!object) { | 108 | if (!object) { |
| 100 | LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle); | 109 | LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); |
| 101 | std::memcpy(output.data(), entries.data(), output.size()); | 110 | result = NvErrCodes::InvalidInput; |
| 102 | return static_cast<u32>(NvErrCodes::InvalidNmapHandle); | 111 | break; |
| 103 | } | 112 | } |
| 104 | 113 | ||
| 105 | ASSERT(object->status == nvmap::Object::Status::Allocated); | 114 | const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10}; |
| 115 | const auto size{static_cast<u64>(entry.pages) << 0x10}; | ||
| 116 | const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10}; | ||
| 117 | const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; | ||
| 106 | 118 | ||
| 107 | const u64 size = static_cast<u64>(entry.pages) << 0x10; | 119 | if (!addr) { |
| 108 | ASSERT(size <= object->size); | 120 | LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); |
| 109 | const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10; | 121 | result = NvErrCodes::InvalidInput; |
| 110 | 122 | break; | |
| 111 | const GPUVAddr returned = | 123 | } |
| 112 | gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size); | ||
| 113 | ASSERT(returned == offset); | ||
| 114 | } | 124 | } |
| 125 | |||
| 115 | std::memcpy(output.data(), entries.data(), output.size()); | 126 | std::memcpy(output.data(), entries.data(), output.size()); |
| 116 | return 0; | 127 | return result; |
| 117 | } | 128 | } |
| 118 | 129 | ||
| 119 | u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | 130 | u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { |
| @@ -126,44 +137,76 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 126 | params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, | 137 | params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, |
| 127 | params.offset); | 138 | params.offset); |
| 128 | 139 | ||
| 129 | if (!params.nvmap_handle) { | 140 | const auto object{nvmap_dev->GetObject(params.nvmap_handle)}; |
| 130 | return 0; | 141 | if (!object) { |
| 142 | LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); | ||
| 143 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 144 | return NvErrCodes::InvalidInput; | ||
| 131 | } | 145 | } |
| 132 | 146 | ||
| 133 | auto object = nvmap_dev->GetObject(params.nvmap_handle); | ||
| 134 | ASSERT(object); | ||
| 135 | |||
| 136 | // We can only map objects that have already been assigned a CPU address. | ||
| 137 | ASSERT(object->status == nvmap::Object::Status::Allocated); | ||
| 138 | |||
| 139 | ASSERT(params.buffer_offset == 0); | ||
| 140 | |||
| 141 | // The real nvservices doesn't make a distinction between handles and ids, and | 147 | // The real nvservices doesn't make a distinction between handles and ids, and |
| 142 | // object can only have one handle and it will be the same as its id. Assert that this is the | 148 | // object can only have one handle and it will be the same as its id. Assert that this is the |
| 143 | // case to prevent unexpected behavior. | 149 | // case to prevent unexpected behavior. |
| 144 | ASSERT(object->id == params.nvmap_handle); | 150 | ASSERT(object->id == params.nvmap_handle); |
| 145 | |||
| 146 | auto& gpu = system.GPU(); | 151 | auto& gpu = system.GPU(); |
| 147 | 152 | ||
| 148 | if (params.flags & 1) { | 153 | u64 page_size{params.page_size}; |
| 149 | params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size); | 154 | if (!page_size) { |
| 150 | } else { | 155 | page_size = object->align; |
| 151 | params.offset = gpu.MemoryManager().MapBufferEx(object->addr, object->size); | 156 | } |
| 157 | |||
| 158 | if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { | ||
| 159 | if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { | ||
| 160 | const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)}; | ||
| 161 | const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)}; | ||
| 162 | |||
| 163 | if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { | ||
| 164 | LOG_CRITICAL(Service_NVDRV, | ||
| 165 | "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " | ||
| 166 | "mapping_size = {}, offset={}", | ||
| 167 | params.flags, params.nvmap_handle, params.buffer_offset, | ||
| 168 | params.mapping_size, params.offset); | ||
| 169 | |||
| 170 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 171 | return NvErrCodes::InvalidInput; | ||
| 172 | } | ||
| 173 | |||
| 174 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 175 | return NvErrCodes::Success; | ||
| 176 | } else { | ||
| 177 | LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); | ||
| 178 | |||
| 179 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 180 | return NvErrCodes::InvalidInput; | ||
| 181 | } | ||
| 152 | } | 182 | } |
| 153 | 183 | ||
| 154 | // Create a new mapping entry for this operation. | 184 | // We can only map objects that have already been assigned a CPU address. |
| 155 | ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(), | 185 | ASSERT(object->status == nvmap::Object::Status::Allocated); |
| 156 | "Offset is already mapped"); | 186 | |
| 187 | const auto physical_address{object->addr + params.buffer_offset}; | ||
| 188 | u64 size{params.mapping_size}; | ||
| 189 | if (!size) { | ||
| 190 | size = object->size; | ||
| 191 | } | ||
| 157 | 192 | ||
| 158 | BufferMapping mapping{}; | 193 | const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; |
| 159 | mapping.nvmap_handle = params.nvmap_handle; | 194 | if (is_alloc) { |
| 160 | mapping.offset = params.offset; | 195 | params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size); |
| 161 | mapping.size = object->size; | 196 | } else { |
| 197 | params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); | ||
| 198 | } | ||
| 162 | 199 | ||
| 163 | buffer_mappings[params.offset] = mapping; | 200 | auto result{NvErrCodes::Success}; |
| 201 | if (!params.offset) { | ||
| 202 | LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); | ||
| 203 | result = NvErrCodes::InvalidInput; | ||
| 204 | } else { | ||
| 205 | AddBufferMap(params.offset, size, physical_address, is_alloc); | ||
| 206 | } | ||
| 164 | 207 | ||
| 165 | std::memcpy(output.data(), ¶ms, output.size()); | 208 | std::memcpy(output.data(), ¶ms, output.size()); |
| 166 | return 0; | 209 | return result; |
| 167 | } | 210 | } |
| 168 | 211 | ||
| 169 | u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | 212 | u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { |
| @@ -172,24 +215,20 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 172 | 215 | ||
| 173 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); | 216 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); |
| 174 | 217 | ||
| 175 | const auto itr = buffer_mappings.find(params.offset); | 218 | if (const auto size{RemoveBufferMap(params.offset)}; size) { |
| 176 | if (itr == buffer_mappings.end()) { | 219 | system.GPU().MemoryManager().Unmap(params.offset, *size); |
| 177 | LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset); | 220 | } else { |
| 178 | // Hardware tests shows that unmapping an already unmapped buffer always returns successful | 221 | LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); |
| 179 | // and doesn't fail. | ||
| 180 | return 0; | ||
| 181 | } | 222 | } |
| 182 | 223 | ||
| 183 | params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size); | ||
| 184 | buffer_mappings.erase(itr->second.offset); | ||
| 185 | |||
| 186 | std::memcpy(output.data(), ¶ms, output.size()); | 224 | std::memcpy(output.data(), ¶ms, output.size()); |
| 187 | return 0; | 225 | return NvErrCodes::Success; |
| 188 | } | 226 | } |
| 189 | 227 | ||
| 190 | u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { | 228 | u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { |
| 191 | IoctlBindChannel params{}; | 229 | IoctlBindChannel params{}; |
| 192 | std::memcpy(¶ms, input.data(), input.size()); | 230 | std::memcpy(¶ms, input.data(), input.size()); |
| 231 | |||
| 193 | LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); | 232 | LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); |
| 194 | 233 | ||
| 195 | channel = params.fd; | 234 | channel = params.fd; |
| @@ -199,6 +238,7 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 199 | u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { | 238 | u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { |
| 200 | IoctlGetVaRegions params{}; | 239 | IoctlGetVaRegions params{}; |
| 201 | std::memcpy(¶ms, input.data(), input.size()); | 240 | std::memcpy(¶ms, input.data(), input.size()); |
| 241 | |||
| 202 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, | 242 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, |
| 203 | params.buf_size); | 243 | params.buf_size); |
| 204 | 244 | ||
| @@ -210,9 +250,43 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o | |||
| 210 | params.regions[1].offset = 0x04000000; | 250 | params.regions[1].offset = 0x04000000; |
| 211 | params.regions[1].page_size = 0x10000; | 251 | params.regions[1].page_size = 0x10000; |
| 212 | params.regions[1].pages = 0x1bffff; | 252 | params.regions[1].pages = 0x1bffff; |
| 253 | |||
| 213 | // TODO(ogniK): This probably can stay stubbed but should add support way way later | 254 | // TODO(ogniK): This probably can stay stubbed but should add support way way later |
| 255 | |||
| 214 | std::memcpy(output.data(), ¶ms, output.size()); | 256 | std::memcpy(output.data(), ¶ms, output.size()); |
| 215 | return 0; | 257 | return 0; |
| 216 | } | 258 | } |
| 217 | 259 | ||
| 260 | std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { | ||
| 261 | const auto end{buffer_mappings.upper_bound(gpu_addr)}; | ||
| 262 | for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { | ||
| 263 | if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) { | ||
| 264 | return iter->second; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | return {}; | ||
| 269 | } | ||
| 270 | |||
| 271 | void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, | ||
| 272 | bool is_allocated) { | ||
| 273 | buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated}; | ||
| 274 | } | ||
| 275 | |||
| 276 | std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { | ||
| 277 | if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) { | ||
| 278 | std::size_t size{}; | ||
| 279 | |||
| 280 | if (iter->second.IsAllocated()) { | ||
| 281 | size = iter->second.Size(); | ||
| 282 | } | ||
| 283 | |||
| 284 | buffer_mappings.erase(iter); | ||
| 285 | |||
| 286 | return size; | ||
| 287 | } | ||
| 288 | |||
| 289 | return {}; | ||
| 290 | } | ||
| 291 | |||
| 218 | } // namespace Service::Nvidia::Devices | 292 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index f79fcc065..9a0cdff0c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h | |||
| @@ -4,9 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <map> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <unordered_map> | 9 | #include <optional> |
| 9 | #include <vector> | 10 | #include <vector> |
| 11 | |||
| 12 | #include "common/common_funcs.h" | ||
| 10 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 12 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 15 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| @@ -15,6 +18,13 @@ namespace Service::Nvidia::Devices { | |||
| 15 | 18 | ||
| 16 | class nvmap; | 19 | class nvmap; |
| 17 | 20 | ||
| 21 | enum class AddressSpaceFlags : u32 { | ||
| 22 | None = 0x0, | ||
| 23 | FixedOffset = 0x1, | ||
| 24 | Remap = 0x100, | ||
| 25 | }; | ||
| 26 | DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); | ||
| 27 | |||
| 18 | class nvhost_as_gpu final : public nvdevice { | 28 | class nvhost_as_gpu final : public nvdevice { |
| 19 | public: | 29 | public: |
| 20 | explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 30 | explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| @@ -25,6 +35,45 @@ public: | |||
| 25 | IoctlVersion version) override; | 35 | IoctlVersion version) override; |
| 26 | 36 | ||
| 27 | private: | 37 | private: |
| 38 | class BufferMap final { | ||
| 39 | public: | ||
| 40 | constexpr BufferMap() = default; | ||
| 41 | |||
| 42 | constexpr BufferMap(GPUVAddr start_addr, std::size_t size) | ||
| 43 | : start_addr{start_addr}, end_addr{start_addr + size} {} | ||
| 44 | |||
| 45 | constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr, | ||
| 46 | bool is_allocated) | ||
| 47 | : start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr}, | ||
| 48 | is_allocated{is_allocated} {} | ||
| 49 | |||
| 50 | constexpr VAddr StartAddr() const { | ||
| 51 | return start_addr; | ||
| 52 | } | ||
| 53 | |||
| 54 | constexpr VAddr EndAddr() const { | ||
| 55 | return end_addr; | ||
| 56 | } | ||
| 57 | |||
| 58 | constexpr std::size_t Size() const { | ||
| 59 | return end_addr - start_addr; | ||
| 60 | } | ||
| 61 | |||
| 62 | constexpr VAddr CpuAddr() const { | ||
| 63 | return cpu_addr; | ||
| 64 | } | ||
| 65 | |||
| 66 | constexpr bool IsAllocated() const { | ||
| 67 | return is_allocated; | ||
| 68 | } | ||
| 69 | |||
| 70 | private: | ||
| 71 | GPUVAddr start_addr{}; | ||
| 72 | GPUVAddr end_addr{}; | ||
| 73 | VAddr cpu_addr{}; | ||
| 74 | bool is_allocated{}; | ||
| 75 | }; | ||
| 76 | |||
| 28 | enum class IoctlCommand : u32_le { | 77 | enum class IoctlCommand : u32_le { |
| 29 | IocInitalizeExCommand = 0x40284109, | 78 | IocInitalizeExCommand = 0x40284109, |
| 30 | IocAllocateSpaceCommand = 0xC0184102, | 79 | IocAllocateSpaceCommand = 0xC0184102, |
| @@ -49,7 +98,7 @@ private: | |||
| 49 | struct IoctlAllocSpace { | 98 | struct IoctlAllocSpace { |
| 50 | u32_le pages; | 99 | u32_le pages; |
| 51 | u32_le page_size; | 100 | u32_le page_size; |
| 52 | u32_le flags; | 101 | AddressSpaceFlags flags; |
| 53 | INSERT_PADDING_WORDS(1); | 102 | INSERT_PADDING_WORDS(1); |
| 54 | union { | 103 | union { |
| 55 | u64_le offset; | 104 | u64_le offset; |
| @@ -69,18 +118,18 @@ private: | |||
| 69 | static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); | 118 | static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); |
| 70 | 119 | ||
| 71 | struct IoctlMapBufferEx { | 120 | struct IoctlMapBufferEx { |
| 72 | u32_le flags; // bit0: fixed_offset, bit2: cacheable | 121 | AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable |
| 73 | u32_le kind; // -1 is default | 122 | u32_le kind; // -1 is default |
| 74 | u32_le nvmap_handle; | 123 | u32_le nvmap_handle; |
| 75 | u32_le page_size; // 0 means don't care | 124 | u32_le page_size; // 0 means don't care |
| 76 | u64_le buffer_offset; | 125 | s64_le buffer_offset; |
| 77 | u64_le mapping_size; | 126 | u64_le mapping_size; |
| 78 | u64_le offset; | 127 | s64_le offset; |
| 79 | }; | 128 | }; |
| 80 | static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); | 129 | static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); |
| 81 | 130 | ||
| 82 | struct IoctlUnmapBuffer { | 131 | struct IoctlUnmapBuffer { |
| 83 | u64_le offset; | 132 | s64_le offset; |
| 84 | }; | 133 | }; |
| 85 | static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); | 134 | static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); |
| 86 | 135 | ||
| @@ -106,15 +155,6 @@ private: | |||
| 106 | static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, | 155 | static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, |
| 107 | "IoctlGetVaRegions is incorrect size"); | 156 | "IoctlGetVaRegions is incorrect size"); |
| 108 | 157 | ||
| 109 | struct BufferMapping { | ||
| 110 | u64 offset; | ||
| 111 | u64 size; | ||
| 112 | u32 nvmap_handle; | ||
| 113 | }; | ||
| 114 | |||
| 115 | /// Map containing the nvmap object mappings in GPU memory. | ||
| 116 | std::unordered_map<u64, BufferMapping> buffer_mappings; | ||
| 117 | |||
| 118 | u32 channel{}; | 158 | u32 channel{}; |
| 119 | 159 | ||
| 120 | u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); | 160 | u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); |
| @@ -125,7 +165,14 @@ private: | |||
| 125 | u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); | 165 | u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); |
| 126 | u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); | 166 | u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); |
| 127 | 167 | ||
| 168 | std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; | ||
| 169 | void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); | ||
| 170 | std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); | ||
| 171 | |||
| 128 | std::shared_ptr<nvmap> nvmap_dev; | 172 | std::shared_ptr<nvmap> nvmap_dev; |
| 173 | |||
| 174 | // This is expected to be ordered, therefore we must use a map, not unordered_map | ||
| 175 | std::map<GPUVAddr, BufferMap> buffer_mappings; | ||
| 129 | }; | 176 | }; |
| 130 | 177 | ||
| 131 | } // namespace Service::Nvidia::Devices | 178 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 8c742316c..9436e16ad 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -18,7 +18,12 @@ enum { | |||
| 18 | }; | 18 | }; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | nvmap::nvmap(Core::System& system) : nvdevice(system) {} | 21 | nvmap::nvmap(Core::System& system) : nvdevice(system) { |
| 22 | // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to | ||
| 23 | // represent this. | ||
| 24 | CreateObject(0); | ||
| 25 | } | ||
| 26 | |||
| 22 | nvmap::~nvmap() = default; | 27 | nvmap::~nvmap() = default; |
| 23 | 28 | ||
| 24 | VAddr nvmap::GetObjectAddress(u32 handle) const { | 29 | VAddr nvmap::GetObjectAddress(u32 handle) const { |
| @@ -50,6 +55,21 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector< | |||
| 50 | return 0; | 55 | return 0; |
| 51 | } | 56 | } |
| 52 | 57 | ||
| 58 | u32 nvmap::CreateObject(u32 size) { | ||
| 59 | // Create a new nvmap object and obtain a handle to it. | ||
| 60 | auto object = std::make_shared<Object>(); | ||
| 61 | object->id = next_id++; | ||
| 62 | object->size = size; | ||
| 63 | object->status = Object::Status::Created; | ||
| 64 | object->refcount = 1; | ||
| 65 | |||
| 66 | const u32 handle = next_handle++; | ||
| 67 | |||
| 68 | handles.insert_or_assign(handle, std::move(object)); | ||
| 69 | |||
| 70 | return handle; | ||
| 71 | } | ||
| 72 | |||
| 53 | u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | 73 | u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { |
| 54 | IocCreateParams params; | 74 | IocCreateParams params; |
| 55 | std::memcpy(¶ms, input.data(), sizeof(params)); | 75 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| @@ -59,17 +79,8 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 59 | LOG_ERROR(Service_NVDRV, "Size is 0"); | 79 | LOG_ERROR(Service_NVDRV, "Size is 0"); |
| 60 | return static_cast<u32>(NvErrCodes::InvalidValue); | 80 | return static_cast<u32>(NvErrCodes::InvalidValue); |
| 61 | } | 81 | } |
| 62 | // Create a new nvmap object and obtain a handle to it. | ||
| 63 | auto object = std::make_shared<Object>(); | ||
| 64 | object->id = next_id++; | ||
| 65 | object->size = params.size; | ||
| 66 | object->status = Object::Status::Created; | ||
| 67 | object->refcount = 1; | ||
| 68 | |||
| 69 | u32 handle = next_handle++; | ||
| 70 | handles[handle] = std::move(object); | ||
| 71 | 82 | ||
| 72 | params.handle = handle; | 83 | params.handle = CreateObject(params.size); |
| 73 | 84 | ||
| 74 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 85 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 75 | return 0; | 86 | return 0; |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 73c2e8809..84624be00 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h | |||
| @@ -49,10 +49,10 @@ public: | |||
| 49 | 49 | ||
| 50 | private: | 50 | private: |
| 51 | /// Id to use for the next handle that is created. | 51 | /// Id to use for the next handle that is created. |
| 52 | u32 next_handle = 1; | 52 | u32 next_handle = 0; |
| 53 | 53 | ||
| 54 | /// Id to use for the next object that is created. | 54 | /// Id to use for the next object that is created. |
| 55 | u32 next_id = 1; | 55 | u32 next_id = 0; |
| 56 | 56 | ||
| 57 | /// Mapping of currently allocated handles to the objects they represent. | 57 | /// Mapping of currently allocated handles to the objects they represent. |
| 58 | std::unordered_map<u32, std::shared_ptr<Object>> handles; | 58 | std::unordered_map<u32, std::shared_ptr<Object>> handles; |
| @@ -119,6 +119,8 @@ private: | |||
| 119 | }; | 119 | }; |
| 120 | static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); | 120 | static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); |
| 121 | 121 | ||
| 122 | u32 CreateObject(u32 size); | ||
| 123 | |||
| 122 | u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output); | 124 | u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output); |
| 123 | u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); | 125 | u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); |
| 124 | u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output); | 126 | u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output); |
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index deaf0808b..88fbfa9b0 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp | |||
| @@ -60,24 +60,24 @@ void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { | |||
| 60 | 60 | ||
| 61 | if (ctrl.must_delay) { | 61 | if (ctrl.must_delay) { |
| 62 | ctrl.fresh_call = false; | 62 | ctrl.fresh_call = false; |
| 63 | ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout, | 63 | ctx.SleepClientThread( |
| 64 | [=](std::shared_ptr<Kernel::Thread> thread, | 64 | "NVServices::DelayedResponse", ctrl.timeout, |
| 65 | Kernel::HLERequestContext& ctx, | 65 | [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, |
| 66 | Kernel::ThreadWakeupReason reason) { | 66 | Kernel::ThreadWakeupReason reason) { |
| 67 | IoctlCtrl ctrl2{ctrl}; | 67 | IoctlCtrl ctrl2{ctrl}; |
| 68 | std::vector<u8> tmp_output = output; | 68 | std::vector<u8> tmp_output = output; |
| 69 | std::vector<u8> tmp_output2 = output2; | 69 | std::vector<u8> tmp_output2 = output2; |
| 70 | u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, | 70 | const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, |
| 71 | tmp_output2, ctrl2, version); | 71 | tmp_output2, ctrl2, version); |
| 72 | ctx.WriteBuffer(tmp_output, 0); | 72 | ctx_.WriteBuffer(tmp_output, 0); |
| 73 | if (version == IoctlVersion::Version3) { | 73 | if (version == IoctlVersion::Version3) { |
| 74 | ctx.WriteBuffer(tmp_output2, 1); | 74 | ctx_.WriteBuffer(tmp_output2, 1); |
| 75 | } | 75 | } |
| 76 | IPC::ResponseBuilder rb{ctx, 3}; | 76 | IPC::ResponseBuilder rb{ctx_, 3}; |
| 77 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| 78 | rb.Push(result); | 78 | rb.Push(ioctl_result); |
| 79 | }, | 79 | }, |
| 80 | nvdrv->GetEventWriteable(ctrl.event_id)); | 80 | nvdrv->GetEventWriteable(ctrl.event_id)); |
| 81 | } else { | 81 | } else { |
| 82 | ctx.WriteBuffer(output); | 82 | ctx.WriteBuffer(output); |
| 83 | if (version == IoctlVersion::Version3) { | 83 | if (version == IoctlVersion::Version3) { |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index caca80dde..637b310d7 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp | |||
| @@ -24,13 +24,13 @@ BufferQueue::~BufferQueue() = default; | |||
| 24 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { | 24 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { |
| 25 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); | 25 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); |
| 26 | 26 | ||
| 27 | Buffer buffer{}; | ||
| 28 | buffer.slot = slot; | ||
| 29 | buffer.igbp_buffer = igbp_buffer; | ||
| 30 | buffer.status = Buffer::Status::Free; | ||
| 31 | free_buffers.push_back(slot); | 27 | free_buffers.push_back(slot); |
| 28 | queue.push_back({ | ||
| 29 | .slot = slot, | ||
| 30 | .status = Buffer::Status::Free, | ||
| 31 | .igbp_buffer = igbp_buffer, | ||
| 32 | }); | ||
| 32 | 33 | ||
| 33 | queue.emplace_back(buffer); | ||
| 34 | buffer_wait_event.writable->Signal(); | 34 | buffer_wait_event.writable->Signal(); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| @@ -38,7 +38,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue | |||
| 38 | u32 height) { | 38 | u32 height) { |
| 39 | 39 | ||
| 40 | if (free_buffers.empty()) { | 40 | if (free_buffers.empty()) { |
| 41 | return {}; | 41 | return std::nullopt; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | auto f_itr = free_buffers.begin(); | 44 | auto f_itr = free_buffers.begin(); |
| @@ -69,7 +69,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue | |||
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | if (itr == queue.end()) { | 71 | if (itr == queue.end()) { |
| 72 | return {}; | 72 | return std::nullopt; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | itr->status = Buffer::Status::Dequeued; | 75 | itr->status = Buffer::Status::Dequeued; |
| @@ -103,14 +103,15 @@ std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::Ac | |||
| 103 | auto itr = queue.end(); | 103 | auto itr = queue.end(); |
| 104 | // Iterate to find a queued buffer matching the requested slot. | 104 | // Iterate to find a queued buffer matching the requested slot. |
| 105 | while (itr == queue.end() && !queue_sequence.empty()) { | 105 | while (itr == queue.end() && !queue_sequence.empty()) { |
| 106 | u32 slot = queue_sequence.front(); | 106 | const u32 slot = queue_sequence.front(); |
| 107 | itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { | 107 | itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { |
| 108 | return buffer.status == Buffer::Status::Queued && buffer.slot == slot; | 108 | return buffer.status == Buffer::Status::Queued && buffer.slot == slot; |
| 109 | }); | 109 | }); |
| 110 | queue_sequence.pop_front(); | 110 | queue_sequence.pop_front(); |
| 111 | } | 111 | } |
| 112 | if (itr == queue.end()) | 112 | if (itr == queue.end()) { |
| 113 | return {}; | 113 | return std::nullopt; |
| 114 | } | ||
| 114 | itr->status = Buffer::Status::Acquired; | 115 | itr->status = Buffer::Status::Acquired; |
| 115 | return *itr; | 116 | return *itr; |
| 116 | } | 117 | } |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 789856118..f644a460d 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -67,8 +67,8 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { | |||
| 67 | 67 | ||
| 68 | // Schedule the screen composition events | 68 | // Schedule the screen composition events |
| 69 | composition_event = Core::Timing::CreateEvent( | 69 | composition_event = Core::Timing::CreateEvent( |
| 70 | "ScreenComposition", [this](u64, std::chrono::nanoseconds ns_late) { | 70 | "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { |
| 71 | Lock(); | 71 | const auto guard = Lock(); |
| 72 | Compose(); | 72 | Compose(); |
| 73 | 73 | ||
| 74 | const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; | 74 | const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; |
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index e4959a9af..1ebe949c0 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -54,12 +54,12 @@ public: | |||
| 54 | /// Opens the specified display and returns the ID. | 54 | /// Opens the specified display and returns the ID. |
| 55 | /// | 55 | /// |
| 56 | /// If an invalid display name is provided, then an empty optional is returned. | 56 | /// If an invalid display name is provided, then an empty optional is returned. |
| 57 | std::optional<u64> OpenDisplay(std::string_view name); | 57 | [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name); |
| 58 | 58 | ||
| 59 | /// Creates a layer on the specified display and returns the layer ID. | 59 | /// Creates a layer on the specified display and returns the layer ID. |
| 60 | /// | 60 | /// |
| 61 | /// If an invalid display ID is specified, then an empty optional is returned. | 61 | /// If an invalid display ID is specified, then an empty optional is returned. |
| 62 | std::optional<u64> CreateLayer(u64 display_id); | 62 | [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); |
| 63 | 63 | ||
| 64 | /// Closes a layer on all displays for the given layer ID. | 64 | /// Closes a layer on all displays for the given layer ID. |
| 65 | void CloseLayer(u64 layer_id); | 65 | void CloseLayer(u64 layer_id); |
| @@ -67,41 +67,41 @@ public: | |||
| 67 | /// Finds the buffer queue ID of the specified layer in the specified display. | 67 | /// Finds the buffer queue ID of the specified layer in the specified display. |
| 68 | /// | 68 | /// |
| 69 | /// If an invalid display ID or layer ID is provided, then an empty optional is returned. | 69 | /// If an invalid display ID or layer ID is provided, then an empty optional is returned. |
| 70 | std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; | 70 | [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; |
| 71 | 71 | ||
| 72 | /// Gets the vsync event for the specified display. | 72 | /// Gets the vsync event for the specified display. |
| 73 | /// | 73 | /// |
| 74 | /// If an invalid display ID is provided, then nullptr is returned. | 74 | /// If an invalid display ID is provided, then nullptr is returned. |
| 75 | std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; | 75 | [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; |
| 76 | 76 | ||
| 77 | /// Obtains a buffer queue identified by the ID. | 77 | /// Obtains a buffer queue identified by the ID. |
| 78 | BufferQueue& FindBufferQueue(u32 id); | 78 | [[nodiscard]] BufferQueue& FindBufferQueue(u32 id); |
| 79 | 79 | ||
| 80 | /// Obtains a buffer queue identified by the ID. | 80 | /// Obtains a buffer queue identified by the ID. |
| 81 | const BufferQueue& FindBufferQueue(u32 id) const; | 81 | [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const; |
| 82 | 82 | ||
| 83 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when | 83 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when |
| 84 | /// finished. | 84 | /// finished. |
| 85 | void Compose(); | 85 | void Compose(); |
| 86 | 86 | ||
| 87 | s64 GetNextTicks() const; | 87 | [[nodiscard]] s64 GetNextTicks() const; |
| 88 | 88 | ||
| 89 | std::unique_lock<std::mutex> Lock() { | 89 | [[nodiscard]] std::unique_lock<std::mutex> Lock() const { |
| 90 | return std::unique_lock{*guard}; | 90 | return std::unique_lock{*guard}; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | private: | 93 | private: |
| 94 | /// Finds the display identified by the specified ID. | 94 | /// Finds the display identified by the specified ID. |
| 95 | VI::Display* FindDisplay(u64 display_id); | 95 | [[nodiscard]] VI::Display* FindDisplay(u64 display_id); |
| 96 | 96 | ||
| 97 | /// Finds the display identified by the specified ID. | 97 | /// Finds the display identified by the specified ID. |
| 98 | const VI::Display* FindDisplay(u64 display_id) const; | 98 | [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const; |
| 99 | 99 | ||
| 100 | /// Finds the layer identified by the specified ID in the desired display. | 100 | /// Finds the layer identified by the specified ID in the desired display. |
| 101 | VI::Layer* FindLayer(u64 display_id, u64 layer_id); | 101 | [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); |
| 102 | 102 | ||
| 103 | /// Finds the layer identified by the specified ID in the desired display. | 103 | /// Finds the layer identified by the specified ID in the desired display. |
| 104 | const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; | 104 | [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; |
| 105 | 105 | ||
| 106 | static void VSyncThread(NVFlinger& nv_flinger); | 106 | static void VSyncThread(NVFlinger& nv_flinger); |
| 107 | 107 | ||
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 34fe2fd82..e64777668 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -106,7 +106,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { | |||
| 106 | 106 | ||
| 107 | IPC::ResponseBuilder rb{ctx, 2}; | 107 | IPC::ResponseBuilder rb{ctx, 2}; |
| 108 | rb.Push(RESULT_SUCCESS); | 108 | rb.Push(RESULT_SUCCESS); |
| 109 | ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); | 109 | ctx.WriteBuffer(layout); |
| 110 | } | 110 | } |
| 111 | } // Anonymous namespace | 111 | } // Anonymous namespace |
| 112 | 112 | ||
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index b06d2f103..aabf166b7 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <type_traits> | 9 | #include <type_traits> |
| 10 | #include <unordered_map> | 10 | #include <unordered_map> |
| 11 | 11 | ||
| 12 | #include "common/concepts.h" | ||
| 12 | #include "core/hle/kernel/client_port.h" | 13 | #include "core/hle/kernel/client_port.h" |
| 13 | #include "core/hle/kernel/object.h" | 14 | #include "core/hle/kernel/object.h" |
| 14 | #include "core/hle/kernel/server_port.h" | 15 | #include "core/hle/kernel/server_port.h" |
| @@ -56,10 +57,8 @@ public: | |||
| 56 | ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); | 57 | ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); |
| 57 | ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); | 58 | ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); |
| 58 | 59 | ||
| 59 | template <typename T> | 60 | template <Common::DerivedFrom<Kernel::SessionRequestHandler> T> |
| 60 | std::shared_ptr<T> GetService(const std::string& service_name) const { | 61 | std::shared_ptr<T> GetService(const std::string& service_name) const { |
| 61 | static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>, | ||
| 62 | "Not a base of ServiceFrameworkBase"); | ||
| 63 | auto service = registered_services.find(service_name); | 62 | auto service = registered_services.find(service_name); |
| 64 | if (service == registered_services.end()) { | 63 | if (service == registered_services.end()) { |
| 65 | LOG_DEBUG(Service, "Can't find service: {}", service_name); | 64 | LOG_DEBUG(Service, "Can't find service: {}", service_name); |
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 13e4b3818..ee4fa4b48 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -290,7 +290,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { | |||
| 290 | 290 | ||
| 291 | IPC::ResponseBuilder rb{ctx, 2}; | 291 | IPC::ResponseBuilder rb{ctx, 2}; |
| 292 | rb.Push(RESULT_SUCCESS); | 292 | rb.Push(RESULT_SUCCESS); |
| 293 | ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); | 293 | ctx.WriteBuffer(clock_snapshot); |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) { | 296 | void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) { |
| @@ -313,7 +313,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques | |||
| 313 | 313 | ||
| 314 | IPC::ResponseBuilder rb{ctx, 2}; | 314 | IPC::ResponseBuilder rb{ctx, 2}; |
| 315 | rb.Push(RESULT_SUCCESS); | 315 | rb.Push(RESULT_SUCCESS); |
| 316 | ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); | 316 | ctx.WriteBuffer(clock_snapshot); |
| 317 | } | 317 | } |
| 318 | 318 | ||
| 319 | void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( | 319 | void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( |
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp index c070d6e97..320672add 100644 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ b/src/core/hle/service/time/time_zone_content_manager.cpp | |||
| @@ -73,10 +73,8 @@ TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core:: | |||
| 73 | 73 | ||
| 74 | std::string location_name; | 74 | std::string location_name; |
| 75 | const auto timezone_setting = Settings::GetTimeZoneString(); | 75 | const auto timezone_setting = Settings::GetTimeZoneString(); |
| 76 | if (timezone_setting == "auto") { | 76 | if (timezone_setting == "auto" || timezone_setting == "default") { |
| 77 | location_name = Common::TimeZone::GetDefaultTimeZone(); | 77 | location_name = Common::TimeZone::GetDefaultTimeZone(); |
| 78 | } else if (timezone_setting == "default") { | ||
| 79 | location_name = location_name; | ||
| 80 | } else { | 78 | } else { |
| 81 | location_name = timezone_setting; | 79 | location_name = timezone_setting; |
| 82 | } | 80 | } |
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index db57ae069..ff3a10b3e 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp | |||
| @@ -142,7 +142,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) { | |||
| 142 | IPC::ResponseBuilder rb{ctx, 3}; | 142 | IPC::ResponseBuilder rb{ctx, 3}; |
| 143 | rb.Push(RESULT_SUCCESS); | 143 | rb.Push(RESULT_SUCCESS); |
| 144 | rb.PushRaw<u32>(1); // Number of times we're returning | 144 | rb.PushRaw<u32>(1); // Number of times we're returning |
| 145 | ctx.WriteBuffer(&posix_time, sizeof(s64)); | 145 | ctx.WriteBuffer(posix_time); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { | 148 | void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { |
| @@ -164,7 +164,7 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { | |||
| 164 | IPC::ResponseBuilder rb{ctx, 3}; | 164 | IPC::ResponseBuilder rb{ctx, 3}; |
| 165 | rb.Push(RESULT_SUCCESS); | 165 | rb.Push(RESULT_SUCCESS); |
| 166 | rb.PushRaw<u32>(1); // Number of times we're returning | 166 | rb.PushRaw<u32>(1); // Number of times we're returning |
| 167 | ctx.WriteBuffer(&posix_time, sizeof(s64)); | 167 | ctx.WriteBuffer(posix_time); |
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | } // namespace Service::Time | 170 | } // namespace Service::Time |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index ea7b4ae13..480d34725 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -511,7 +511,7 @@ private: | |||
| 511 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, | 511 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, |
| 512 | static_cast<u32>(transaction), flags); | 512 | static_cast<u32>(transaction), flags); |
| 513 | 513 | ||
| 514 | nv_flinger->Lock(); | 514 | const auto guard = nv_flinger->Lock(); |
| 515 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); | 515 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); |
| 516 | 516 | ||
| 517 | switch (transaction) { | 517 | switch (transaction) { |
| @@ -548,10 +548,10 @@ private: | |||
| 548 | // Wait the current thread until a buffer becomes available | 548 | // Wait the current thread until a buffer becomes available |
| 549 | ctx.SleepClientThread( | 549 | ctx.SleepClientThread( |
| 550 | "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, | 550 | "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, |
| 551 | [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, | 551 | [=, this](std::shared_ptr<Kernel::Thread> thread, |
| 552 | Kernel::ThreadWakeupReason reason) { | 552 | Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { |
| 553 | // Repeat TransactParcel DequeueBuffer when a buffer is available | 553 | // Repeat TransactParcel DequeueBuffer when a buffer is available |
| 554 | nv_flinger->Lock(); | 554 | const auto guard = nv_flinger->Lock(); |
| 555 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); | 555 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); |
| 556 | auto result = buffer_queue.DequeueBuffer(width, height); | 556 | auto result = buffer_queue.DequeueBuffer(width, height); |
| 557 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); | 557 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); |
| @@ -1199,6 +1199,23 @@ private: | |||
| 1199 | } | 1199 | } |
| 1200 | } | 1200 | } |
| 1201 | 1201 | ||
| 1202 | void GetIndirectLayerImageRequiredMemoryInfo(Kernel::HLERequestContext& ctx) { | ||
| 1203 | IPC::RequestParser rp{ctx}; | ||
| 1204 | const auto width = rp.Pop<u64>(); | ||
| 1205 | const auto height = rp.Pop<u64>(); | ||
| 1206 | LOG_DEBUG(Service_VI, "called width={}, height={}", width, height); | ||
| 1207 | |||
| 1208 | constexpr std::size_t base_size = 0x20000; | ||
| 1209 | constexpr std::size_t alignment = 0x1000; | ||
| 1210 | const auto texture_size = width * height * 4; | ||
| 1211 | const auto out_size = (texture_size + base_size - 1) / base_size * base_size; | ||
| 1212 | |||
| 1213 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 1214 | rb.Push(RESULT_SUCCESS); | ||
| 1215 | rb.Push(out_size); | ||
| 1216 | rb.Push(alignment); | ||
| 1217 | } | ||
| 1218 | |||
| 1202 | static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) { | 1219 | static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) { |
| 1203 | switch (mode) { | 1220 | switch (mode) { |
| 1204 | case NintendoScaleMode::None: | 1221 | case NintendoScaleMode::None: |
| @@ -1243,7 +1260,8 @@ IApplicationDisplayService::IApplicationDisplayService( | |||
| 1243 | {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, | 1260 | {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, |
| 1244 | {2450, nullptr, "GetIndirectLayerImageMap"}, | 1261 | {2450, nullptr, "GetIndirectLayerImageMap"}, |
| 1245 | {2451, nullptr, "GetIndirectLayerImageCropMap"}, | 1262 | {2451, nullptr, "GetIndirectLayerImageCropMap"}, |
| 1246 | {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"}, | 1263 | {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, |
| 1264 | "GetIndirectLayerImageRequiredMemoryInfo"}, | ||
| 1247 | {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, | 1265 | {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, |
| 1248 | {5203, nullptr, "GetDisplayVsyncEventForDebug"}, | 1266 | {5203, nullptr, "GetDisplayVsyncEventForDebug"}, |
| 1249 | }; | 1267 | }; |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 59ca7091a..7c48e55e1 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -3,8 +3,10 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <optional> | ||
| 6 | #include <ostream> | 7 | #include <ostream> |
| 7 | #include <string> | 8 | #include <string> |
| 9 | #include "common/concepts.h" | ||
| 8 | #include "common/file_util.h" | 10 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 10 | #include "common/string_util.h" | 12 | #include "common/string_util.h" |
| @@ -21,27 +23,41 @@ | |||
| 21 | 23 | ||
| 22 | namespace Loader { | 24 | namespace Loader { |
| 23 | 25 | ||
| 24 | FileType IdentifyFile(FileSys::VirtualFile file) { | 26 | namespace { |
| 25 | FileType type; | ||
| 26 | |||
| 27 | #define CHECK_TYPE(loader) \ | ||
| 28 | type = AppLoader_##loader::IdentifyType(file); \ | ||
| 29 | if (FileType::Error != type) \ | ||
| 30 | return type; | ||
| 31 | 27 | ||
| 32 | CHECK_TYPE(DeconstructedRomDirectory) | 28 | template <Common::DerivedFrom<AppLoader> T> |
| 33 | CHECK_TYPE(ELF) | 29 | std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) { |
| 34 | CHECK_TYPE(NSO) | 30 | const auto file_type = T::IdentifyType(file); |
| 35 | CHECK_TYPE(NRO) | 31 | if (file_type != FileType::Error) { |
| 36 | CHECK_TYPE(NCA) | 32 | return file_type; |
| 37 | CHECK_TYPE(XCI) | 33 | } |
| 38 | CHECK_TYPE(NAX) | 34 | return std::nullopt; |
| 39 | CHECK_TYPE(NSP) | 35 | } |
| 40 | CHECK_TYPE(KIP) | ||
| 41 | 36 | ||
| 42 | #undef CHECK_TYPE | 37 | } // namespace |
| 43 | 38 | ||
| 44 | return FileType::Unknown; | 39 | FileType IdentifyFile(FileSys::VirtualFile file) { |
| 40 | if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) { | ||
| 41 | return *romdir_type; | ||
| 42 | } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) { | ||
| 43 | return *elf_type; | ||
| 44 | } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) { | ||
| 45 | return *nso_type; | ||
| 46 | } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) { | ||
| 47 | return *nro_type; | ||
| 48 | } else if (const auto nca_type = IdentifyFileLoader<AppLoader_NCA>(file)) { | ||
| 49 | return *nca_type; | ||
| 50 | } else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) { | ||
| 51 | return *xci_type; | ||
| 52 | } else if (const auto nax_type = IdentifyFileLoader<AppLoader_NAX>(file)) { | ||
| 53 | return *nax_type; | ||
| 54 | } else if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) { | ||
| 55 | return *nsp_type; | ||
| 56 | } else if (const auto kip_type = IdentifyFileLoader<AppLoader_KIP>(file)) { | ||
| 57 | return *kip_type; | ||
| 58 | } else { | ||
| 59 | return FileType::Unknown; | ||
| 60 | } | ||
| 45 | } | 61 | } |
| 46 | 62 | ||
| 47 | FileType GuessFromFilename(const std::string& name) { | 63 | FileType GuessFromFilename(const std::string& name) { |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 2c5588933..86d17c6cb 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -704,7 +704,7 @@ struct Memory::Impl { | |||
| 704 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 704 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |
| 705 | if (page_pointer != nullptr) { | 705 | if (page_pointer != nullptr) { |
| 706 | // NOTE: Avoid adding any extra logic to this fast-path block | 706 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 707 | T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]); | 707 | auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]); |
| 708 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 708 | return Common::AtomicCompareAndSwap(pointer, data, expected); |
| 709 | } | 709 | } |
| 710 | 710 | ||
| @@ -720,9 +720,8 @@ struct Memory::Impl { | |||
| 720 | case Common::PageType::RasterizerCachedMemory: { | 720 | case Common::PageType::RasterizerCachedMemory: { |
| 721 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | 721 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; |
| 722 | system.GPU().InvalidateRegion(vaddr, sizeof(T)); | 722 | system.GPU().InvalidateRegion(vaddr, sizeof(T)); |
| 723 | T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr); | 723 | auto* pointer = reinterpret_cast<volatile T*>(&host_ptr); |
| 724 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 724 | return Common::AtomicCompareAndSwap(pointer, data, expected); |
| 725 | break; | ||
| 726 | } | 725 | } |
| 727 | default: | 726 | default: |
| 728 | UNREACHABLE(); | 727 | UNREACHABLE(); |
| @@ -734,7 +733,7 @@ struct Memory::Impl { | |||
| 734 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 733 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |
| 735 | if (page_pointer != nullptr) { | 734 | if (page_pointer != nullptr) { |
| 736 | // NOTE: Avoid adding any extra logic to this fast-path block | 735 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 737 | u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]); | 736 | auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]); |
| 738 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 737 | return Common::AtomicCompareAndSwap(pointer, data, expected); |
| 739 | } | 738 | } |
| 740 | 739 | ||
| @@ -750,9 +749,8 @@ struct Memory::Impl { | |||
| 750 | case Common::PageType::RasterizerCachedMemory: { | 749 | case Common::PageType::RasterizerCachedMemory: { |
| 751 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | 750 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; |
| 752 | system.GPU().InvalidateRegion(vaddr, sizeof(u128)); | 751 | system.GPU().InvalidateRegion(vaddr, sizeof(u128)); |
| 753 | u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr); | 752 | auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr); |
| 754 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 753 | return Common::AtomicCompareAndSwap(pointer, data, expected); |
| 755 | break; | ||
| 756 | } | 754 | } |
| 757 | default: | 755 | default: |
| 758 | UNREACHABLE(); | 756 | UNREACHABLE(); |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index ced41b1fe..e503118dd 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -42,7 +42,7 @@ u64 StandardVmCallbacks::HidKeysDown() { | |||
| 42 | if (applet_resource == nullptr) { | 42 | if (applet_resource == nullptr) { |
| 43 | LOG_WARNING(CheatEngine, | 43 | LOG_WARNING(CheatEngine, |
| 44 | "Attempted to read input state, but applet resource is not initialized!"); | 44 | "Attempted to read input state, but applet resource is not initialized!"); |
| 45 | return false; | 45 | return 0; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | const auto press_state = | 48 | const auto press_state = |
| @@ -188,28 +188,40 @@ CheatEngine::~CheatEngine() { | |||
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | void CheatEngine::Initialize() { | 190 | void CheatEngine::Initialize() { |
| 191 | event = Core::Timing::CreateEvent("CheatEngine::FrameCallback::" + | 191 | event = Core::Timing::CreateEvent( |
| 192 | Common::HexToString(metadata.main_nso_build_id), | 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), |
| 193 | [this](u64 userdata, std::chrono::nanoseconds ns_late) { | 193 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 194 | FrameCallback(userdata, ns_late); | 194 | FrameCallback(user_data, ns_late); |
| 195 | }); | 195 | }); |
| 196 | core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); | 196 | core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); |
| 197 | 197 | ||
| 198 | metadata.process_id = system.CurrentProcess()->GetProcessID(); | 198 | metadata.process_id = system.CurrentProcess()->GetProcessID(); |
| 199 | metadata.title_id = system.CurrentProcess()->GetTitleID(); | 199 | metadata.title_id = system.CurrentProcess()->GetTitleID(); |
| 200 | 200 | ||
| 201 | const auto& page_table = system.CurrentProcess()->PageTable(); | 201 | const auto& page_table = system.CurrentProcess()->PageTable(); |
| 202 | metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()}; | 202 | metadata.heap_extents = { |
| 203 | metadata.address_space_extents = {page_table.GetAddressSpaceStart(), | 203 | .base = page_table.GetHeapRegionStart(), |
| 204 | page_table.GetAddressSpaceSize()}; | 204 | .size = page_table.GetHeapRegionSize(), |
| 205 | metadata.alias_extents = {page_table.GetAliasCodeRegionStart(), | 205 | }; |
| 206 | page_table.GetAliasCodeRegionSize()}; | 206 | |
| 207 | metadata.address_space_extents = { | ||
| 208 | .base = page_table.GetAddressSpaceStart(), | ||
| 209 | .size = page_table.GetAddressSpaceSize(), | ||
| 210 | }; | ||
| 211 | |||
| 212 | metadata.alias_extents = { | ||
| 213 | .base = page_table.GetAliasCodeRegionStart(), | ||
| 214 | .size = page_table.GetAliasCodeRegionSize(), | ||
| 215 | }; | ||
| 207 | 216 | ||
| 208 | is_pending_reload.exchange(true); | 217 | is_pending_reload.exchange(true); |
| 209 | } | 218 | } |
| 210 | 219 | ||
| 211 | void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { | 220 | void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { |
| 212 | metadata.main_nso_extents = {main_region_begin, main_region_size}; | 221 | metadata.main_nso_extents = { |
| 222 | .base = main_region_begin, | ||
| 223 | .size = main_region_size, | ||
| 224 | }; | ||
| 213 | } | 225 | } |
| 214 | 226 | ||
| 215 | void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | 227 | void CheatEngine::Reload(std::vector<CheatEntry> cheats) { |
| @@ -219,7 +231,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | |||
| 219 | 231 | ||
| 220 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | 232 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); |
| 221 | 233 | ||
| 222 | void CheatEngine::FrameCallback(u64, std::chrono::nanoseconds ns_late) { | 234 | void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { |
| 223 | if (is_pending_reload.exchange(false)) { | 235 | if (is_pending_reload.exchange(false)) { |
| 224 | vm.LoadProgram(cheats); | 236 | vm.LoadProgram(cheats); |
| 225 | } | 237 | } |
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index d4068cf84..fa039a831 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h | |||
| @@ -72,7 +72,7 @@ public: | |||
| 72 | void Reload(std::vector<CheatEntry> cheats); | 72 | void Reload(std::vector<CheatEntry> cheats); |
| 73 | 73 | ||
| 74 | private: | 74 | private: |
| 75 | void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late); | 75 | void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 76 | 76 | ||
| 77 | DmntCheatVm vm; | 77 | DmntCheatVm vm; |
| 78 | CheatProcessMetadata metadata; | 78 | CheatProcessMetadata metadata; |
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index 2e7da23fe..48be80c12 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp | |||
| @@ -313,30 +313,32 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 313 | 313 | ||
| 314 | switch (opcode_type) { | 314 | switch (opcode_type) { |
| 315 | case CheatVmOpcodeType::StoreStatic: { | 315 | case CheatVmOpcodeType::StoreStatic: { |
| 316 | StoreStaticOpcode store_static{}; | ||
| 317 | // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) | 316 | // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) |
| 318 | // Read additional words. | 317 | // Read additional words. |
| 319 | const u32 second_dword = GetNextDword(); | 318 | const u32 second_dword = GetNextDword(); |
| 320 | store_static.bit_width = (first_dword >> 24) & 0xF; | 319 | const u32 bit_width = (first_dword >> 24) & 0xF; |
| 321 | store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | 320 | |
| 322 | store_static.offset_register = ((first_dword >> 16) & 0xF); | 321 | opcode.opcode = StoreStaticOpcode{ |
| 323 | store_static.rel_address = | 322 | .bit_width = bit_width, |
| 324 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | 323 | .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), |
| 325 | store_static.value = GetNextVmInt(store_static.bit_width); | 324 | .offset_register = (first_dword >> 16) & 0xF, |
| 326 | opcode.opcode = store_static; | 325 | .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, |
| 326 | .value = GetNextVmInt(bit_width), | ||
| 327 | }; | ||
| 327 | } break; | 328 | } break; |
| 328 | case CheatVmOpcodeType::BeginConditionalBlock: { | 329 | case CheatVmOpcodeType::BeginConditionalBlock: { |
| 329 | BeginConditionalOpcode begin_cond{}; | ||
| 330 | // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) | 330 | // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) |
| 331 | // Read additional words. | 331 | // Read additional words. |
| 332 | const u32 second_dword = GetNextDword(); | 332 | const u32 second_dword = GetNextDword(); |
| 333 | begin_cond.bit_width = (first_dword >> 24) & 0xF; | 333 | const u32 bit_width = (first_dword >> 24) & 0xF; |
| 334 | begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | 334 | |
| 335 | begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); | 335 | opcode.opcode = BeginConditionalOpcode{ |
| 336 | begin_cond.rel_address = | 336 | .bit_width = bit_width, |
| 337 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | 337 | .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), |
| 338 | begin_cond.value = GetNextVmInt(begin_cond.bit_width); | 338 | .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF), |
| 339 | opcode.opcode = begin_cond; | 339 | .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, |
| 340 | .value = GetNextVmInt(bit_width), | ||
| 341 | }; | ||
| 340 | } break; | 342 | } break; |
| 341 | case CheatVmOpcodeType::EndConditionalBlock: { | 343 | case CheatVmOpcodeType::EndConditionalBlock: { |
| 342 | // 20000000 | 344 | // 20000000 |
| @@ -344,12 +346,14 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 344 | opcode.opcode = EndConditionalOpcode{}; | 346 | opcode.opcode = EndConditionalOpcode{}; |
| 345 | } break; | 347 | } break; |
| 346 | case CheatVmOpcodeType::ControlLoop: { | 348 | case CheatVmOpcodeType::ControlLoop: { |
| 347 | ControlLoopOpcode ctrl_loop{}; | ||
| 348 | // 300R0000 VVVVVVVV | 349 | // 300R0000 VVVVVVVV |
| 349 | // 310R0000 | 350 | // 310R0000 |
| 350 | // Parse register, whether loop start or loop end. | 351 | // Parse register, whether loop start or loop end. |
| 351 | ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0; | 352 | ControlLoopOpcode ctrl_loop{ |
| 352 | ctrl_loop.reg_index = ((first_dword >> 20) & 0xF); | 353 | .start_loop = ((first_dword >> 24) & 0xF) == 0, |
| 354 | .reg_index = (first_dword >> 20) & 0xF, | ||
| 355 | .num_iters = 0, | ||
| 356 | }; | ||
| 353 | 357 | ||
| 354 | // Read number of iters if loop start. | 358 | // Read number of iters if loop start. |
| 355 | if (ctrl_loop.start_loop) { | 359 | if (ctrl_loop.start_loop) { |
| @@ -358,66 +362,65 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 358 | opcode.opcode = ctrl_loop; | 362 | opcode.opcode = ctrl_loop; |
| 359 | } break; | 363 | } break; |
| 360 | case CheatVmOpcodeType::LoadRegisterStatic: { | 364 | case CheatVmOpcodeType::LoadRegisterStatic: { |
| 361 | LoadRegisterStaticOpcode ldr_static{}; | ||
| 362 | // 400R0000 VVVVVVVV VVVVVVVV | 365 | // 400R0000 VVVVVVVV VVVVVVVV |
| 363 | // Read additional words. | 366 | // Read additional words. |
| 364 | ldr_static.reg_index = ((first_dword >> 16) & 0xF); | 367 | opcode.opcode = LoadRegisterStaticOpcode{ |
| 365 | ldr_static.value = | 368 | .reg_index = (first_dword >> 16) & 0xF, |
| 366 | (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); | 369 | .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(), |
| 367 | opcode.opcode = ldr_static; | 370 | }; |
| 368 | } break; | 371 | } break; |
| 369 | case CheatVmOpcodeType::LoadRegisterMemory: { | 372 | case CheatVmOpcodeType::LoadRegisterMemory: { |
| 370 | LoadRegisterMemoryOpcode ldr_memory{}; | ||
| 371 | // 5TMRI0AA AAAAAAAA | 373 | // 5TMRI0AA AAAAAAAA |
| 372 | // Read additional words. | 374 | // Read additional words. |
| 373 | const u32 second_dword = GetNextDword(); | 375 | const u32 second_dword = GetNextDword(); |
| 374 | ldr_memory.bit_width = (first_dword >> 24) & 0xF; | 376 | opcode.opcode = LoadRegisterMemoryOpcode{ |
| 375 | ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | 377 | .bit_width = (first_dword >> 24) & 0xF, |
| 376 | ldr_memory.reg_index = ((first_dword >> 16) & 0xF); | 378 | .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), |
| 377 | ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; | 379 | .reg_index = ((first_dword >> 16) & 0xF), |
| 378 | ldr_memory.rel_address = | 380 | .load_from_reg = ((first_dword >> 12) & 0xF) != 0, |
| 379 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | 381 | .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, |
| 380 | opcode.opcode = ldr_memory; | 382 | }; |
| 381 | } break; | 383 | } break; |
| 382 | case CheatVmOpcodeType::StoreStaticToAddress: { | 384 | case CheatVmOpcodeType::StoreStaticToAddress: { |
| 383 | StoreStaticToAddressOpcode str_static{}; | ||
| 384 | // 6T0RIor0 VVVVVVVV VVVVVVVV | 385 | // 6T0RIor0 VVVVVVVV VVVVVVVV |
| 385 | // Read additional words. | 386 | // Read additional words. |
| 386 | str_static.bit_width = (first_dword >> 24) & 0xF; | 387 | opcode.opcode = StoreStaticToAddressOpcode{ |
| 387 | str_static.reg_index = ((first_dword >> 16) & 0xF); | 388 | .bit_width = (first_dword >> 24) & 0xF, |
| 388 | str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0; | 389 | .reg_index = (first_dword >> 16) & 0xF, |
| 389 | str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0; | 390 | .increment_reg = ((first_dword >> 12) & 0xF) != 0, |
| 390 | str_static.offset_reg_index = ((first_dword >> 4) & 0xF); | 391 | .add_offset_reg = ((first_dword >> 8) & 0xF) != 0, |
| 391 | str_static.value = | 392 | .offset_reg_index = (first_dword >> 4) & 0xF, |
| 392 | (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); | 393 | .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(), |
| 393 | opcode.opcode = str_static; | 394 | }; |
| 394 | } break; | 395 | } break; |
| 395 | case CheatVmOpcodeType::PerformArithmeticStatic: { | 396 | case CheatVmOpcodeType::PerformArithmeticStatic: { |
| 396 | PerformArithmeticStaticOpcode perform_math_static{}; | ||
| 397 | // 7T0RC000 VVVVVVVV | 397 | // 7T0RC000 VVVVVVVV |
| 398 | // Read additional words. | 398 | // Read additional words. |
| 399 | perform_math_static.bit_width = (first_dword >> 24) & 0xF; | 399 | opcode.opcode = PerformArithmeticStaticOpcode{ |
| 400 | perform_math_static.reg_index = ((first_dword >> 16) & 0xF); | 400 | .bit_width = (first_dword >> 24) & 0xF, |
| 401 | perform_math_static.math_type = | 401 | .reg_index = ((first_dword >> 16) & 0xF), |
| 402 | static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF); | 402 | .math_type = static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF), |
| 403 | perform_math_static.value = GetNextDword(); | 403 | .value = GetNextDword(), |
| 404 | opcode.opcode = perform_math_static; | 404 | }; |
| 405 | } break; | 405 | } break; |
| 406 | case CheatVmOpcodeType::BeginKeypressConditionalBlock: { | 406 | case CheatVmOpcodeType::BeginKeypressConditionalBlock: { |
| 407 | BeginKeypressConditionalOpcode begin_keypress_cond{}; | ||
| 408 | // 8kkkkkkk | 407 | // 8kkkkkkk |
| 409 | // Just parse the mask. | 408 | // Just parse the mask. |
| 410 | begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF; | 409 | opcode.opcode = BeginKeypressConditionalOpcode{ |
| 411 | opcode.opcode = begin_keypress_cond; | 410 | .key_mask = first_dword & 0x0FFFFFFF, |
| 411 | }; | ||
| 412 | } break; | 412 | } break; |
| 413 | case CheatVmOpcodeType::PerformArithmeticRegister: { | 413 | case CheatVmOpcodeType::PerformArithmeticRegister: { |
| 414 | PerformArithmeticRegisterOpcode perform_math_reg{}; | ||
| 415 | // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) | 414 | // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) |
| 416 | perform_math_reg.bit_width = (first_dword >> 24) & 0xF; | 415 | PerformArithmeticRegisterOpcode perform_math_reg{ |
| 417 | perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF); | 416 | .bit_width = (first_dword >> 24) & 0xF, |
| 418 | perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF); | 417 | .math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF), |
| 419 | perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF); | 418 | .dst_reg_index = (first_dword >> 16) & 0xF, |
| 420 | perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0; | 419 | .src_reg_1_index = (first_dword >> 12) & 0xF, |
| 420 | .src_reg_2_index = 0, | ||
| 421 | .has_immediate = ((first_dword >> 8) & 0xF) != 0, | ||
| 422 | .value = {}, | ||
| 423 | }; | ||
| 421 | if (perform_math_reg.has_immediate) { | 424 | if (perform_math_reg.has_immediate) { |
| 422 | perform_math_reg.src_reg_2_index = 0; | 425 | perform_math_reg.src_reg_2_index = 0; |
| 423 | perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); | 426 | perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); |
| @@ -427,7 +430,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 427 | opcode.opcode = perform_math_reg; | 430 | opcode.opcode = perform_math_reg; |
| 428 | } break; | 431 | } break; |
| 429 | case CheatVmOpcodeType::StoreRegisterToAddress: { | 432 | case CheatVmOpcodeType::StoreRegisterToAddress: { |
| 430 | StoreRegisterToAddressOpcode str_register{}; | ||
| 431 | // ATSRIOxa (aaaaaaaa) | 433 | // ATSRIOxa (aaaaaaaa) |
| 432 | // A = opcode 10 | 434 | // A = opcode 10 |
| 433 | // T = bit width | 435 | // T = bit width |
| @@ -439,20 +441,23 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 439 | // Relative Address | 441 | // Relative Address |
| 440 | // x = offset register (for offset type 1), memory type (for offset type 3) | 442 | // x = offset register (for offset type 1), memory type (for offset type 3) |
| 441 | // a = relative address (for offset type 2+3) | 443 | // a = relative address (for offset type 2+3) |
| 442 | str_register.bit_width = (first_dword >> 24) & 0xF; | 444 | StoreRegisterToAddressOpcode str_register{ |
| 443 | str_register.str_reg_index = ((first_dword >> 20) & 0xF); | 445 | .bit_width = (first_dword >> 24) & 0xF, |
| 444 | str_register.addr_reg_index = ((first_dword >> 16) & 0xF); | 446 | .str_reg_index = (first_dword >> 20) & 0xF, |
| 445 | str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0; | 447 | .addr_reg_index = (first_dword >> 16) & 0xF, |
| 446 | str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)); | 448 | .increment_reg = ((first_dword >> 12) & 0xF) != 0, |
| 447 | str_register.ofs_reg_index = ((first_dword >> 4) & 0xF); | 449 | .ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)), |
| 450 | .mem_type = MemoryAccessType::MainNso, | ||
| 451 | .ofs_reg_index = (first_dword >> 4) & 0xF, | ||
| 452 | .rel_address = 0, | ||
| 453 | }; | ||
| 448 | switch (str_register.ofs_type) { | 454 | switch (str_register.ofs_type) { |
| 449 | case StoreRegisterOffsetType::None: | 455 | case StoreRegisterOffsetType::None: |
| 450 | case StoreRegisterOffsetType::Reg: | 456 | case StoreRegisterOffsetType::Reg: |
| 451 | // Nothing more to do | 457 | // Nothing more to do |
| 452 | break; | 458 | break; |
| 453 | case StoreRegisterOffsetType::Imm: | 459 | case StoreRegisterOffsetType::Imm: |
| 454 | str_register.rel_address = | 460 | str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 455 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 456 | break; | 461 | break; |
| 457 | case StoreRegisterOffsetType::MemReg: | 462 | case StoreRegisterOffsetType::MemReg: |
| 458 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 463 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| @@ -460,8 +465,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 460 | case StoreRegisterOffsetType::MemImm: | 465 | case StoreRegisterOffsetType::MemImm: |
| 461 | case StoreRegisterOffsetType::MemImmReg: | 466 | case StoreRegisterOffsetType::MemImmReg: |
| 462 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 467 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 463 | str_register.rel_address = | 468 | str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 464 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 465 | break; | 469 | break; |
| 466 | default: | 470 | default: |
| 467 | str_register.ofs_type = StoreRegisterOffsetType::None; | 471 | str_register.ofs_type = StoreRegisterOffsetType::None; |
| @@ -470,7 +474,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 470 | opcode.opcode = str_register; | 474 | opcode.opcode = str_register; |
| 471 | } break; | 475 | } break; |
| 472 | case CheatVmOpcodeType::BeginRegisterConditionalBlock: { | 476 | case CheatVmOpcodeType::BeginRegisterConditionalBlock: { |
| 473 | BeginRegisterConditionalOpcode begin_reg_cond{}; | ||
| 474 | // C0TcSX## | 477 | // C0TcSX## |
| 475 | // C0TcS0Ma aaaaaaaa | 478 | // C0TcS0Ma aaaaaaaa |
| 476 | // C0TcS1Mr | 479 | // C0TcS1Mr |
| @@ -492,11 +495,19 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 492 | // r = offset register. | 495 | // r = offset register. |
| 493 | // X = other register. | 496 | // X = other register. |
| 494 | // V = value. | 497 | // V = value. |
| 495 | begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; | 498 | |
| 496 | begin_reg_cond.cond_type = | 499 | BeginRegisterConditionalOpcode begin_reg_cond{ |
| 497 | static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); | 500 | .bit_width = (first_dword >> 20) & 0xF, |
| 498 | begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); | 501 | .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF), |
| 499 | begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF); | 502 | .val_reg_index = (first_dword >> 12) & 0xF, |
| 503 | .comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF), | ||
| 504 | .mem_type = MemoryAccessType::MainNso, | ||
| 505 | .addr_reg_index = 0, | ||
| 506 | .other_reg_index = 0, | ||
| 507 | .ofs_reg_index = 0, | ||
| 508 | .rel_address = 0, | ||
| 509 | .value = {}, | ||
| 510 | }; | ||
| 500 | 511 | ||
| 501 | switch (begin_reg_cond.comp_type) { | 512 | switch (begin_reg_cond.comp_type) { |
| 502 | case CompareRegisterValueType::StaticValue: | 513 | case CompareRegisterValueType::StaticValue: |
| @@ -508,26 +519,25 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 508 | case CompareRegisterValueType::MemoryRelAddr: | 519 | case CompareRegisterValueType::MemoryRelAddr: |
| 509 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 520 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 510 | begin_reg_cond.rel_address = | 521 | begin_reg_cond.rel_address = |
| 511 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | 522 | (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 512 | break; | 523 | break; |
| 513 | case CompareRegisterValueType::MemoryOfsReg: | 524 | case CompareRegisterValueType::MemoryOfsReg: |
| 514 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 525 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 515 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); | 526 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); |
| 516 | break; | 527 | break; |
| 517 | case CompareRegisterValueType::RegisterRelAddr: | 528 | case CompareRegisterValueType::RegisterRelAddr: |
| 518 | begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); | 529 | begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF; |
| 519 | begin_reg_cond.rel_address = | 530 | begin_reg_cond.rel_address = |
| 520 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | 531 | (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 521 | break; | 532 | break; |
| 522 | case CompareRegisterValueType::RegisterOfsReg: | 533 | case CompareRegisterValueType::RegisterOfsReg: |
| 523 | begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); | 534 | begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF; |
| 524 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); | 535 | begin_reg_cond.ofs_reg_index = first_dword & 0xF; |
| 525 | break; | 536 | break; |
| 526 | } | 537 | } |
| 527 | opcode.opcode = begin_reg_cond; | 538 | opcode.opcode = begin_reg_cond; |
| 528 | } break; | 539 | } break; |
| 529 | case CheatVmOpcodeType::SaveRestoreRegister: { | 540 | case CheatVmOpcodeType::SaveRestoreRegister: { |
| 530 | SaveRestoreRegisterOpcode save_restore_reg{}; | ||
| 531 | // C10D0Sx0 | 541 | // C10D0Sx0 |
| 532 | // C1 = opcode 0xC1 | 542 | // C1 = opcode 0xC1 |
| 533 | // D = destination index. | 543 | // D = destination index. |
| @@ -535,36 +545,37 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 535 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring | 545 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring |
| 536 | // a register. | 546 | // a register. |
| 537 | // NOTE: If we add more save slots later, current encoding is backwards compatible. | 547 | // NOTE: If we add more save slots later, current encoding is backwards compatible. |
| 538 | save_restore_reg.dst_index = (first_dword >> 16) & 0xF; | 548 | opcode.opcode = SaveRestoreRegisterOpcode{ |
| 539 | save_restore_reg.src_index = (first_dword >> 8) & 0xF; | 549 | .dst_index = (first_dword >> 16) & 0xF, |
| 540 | save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF); | 550 | .src_index = (first_dword >> 8) & 0xF, |
| 541 | opcode.opcode = save_restore_reg; | 551 | .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF), |
| 552 | }; | ||
| 542 | } break; | 553 | } break; |
| 543 | case CheatVmOpcodeType::SaveRestoreRegisterMask: { | 554 | case CheatVmOpcodeType::SaveRestoreRegisterMask: { |
| 544 | SaveRestoreRegisterMaskOpcode save_restore_regmask{}; | ||
| 545 | // C2x0XXXX | 555 | // C2x0XXXX |
| 546 | // C2 = opcode 0xC2 | 556 | // C2 = opcode 0xC2 |
| 547 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. | 557 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. |
| 548 | // X = 16-bit bitmask, bit i --> save or restore register i. | 558 | // X = 16-bit bitmask, bit i --> save or restore register i. |
| 549 | save_restore_regmask.op_type = | 559 | SaveRestoreRegisterMaskOpcode save_restore_regmask{ |
| 550 | static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF); | 560 | .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF), |
| 561 | .should_operate = {}, | ||
| 562 | }; | ||
| 551 | for (std::size_t i = 0; i < NumRegisters; i++) { | 563 | for (std::size_t i = 0; i < NumRegisters; i++) { |
| 552 | save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0; | 564 | save_restore_regmask.should_operate[i] = (first_dword & (1U << i)) != 0; |
| 553 | } | 565 | } |
| 554 | opcode.opcode = save_restore_regmask; | 566 | opcode.opcode = save_restore_regmask; |
| 555 | } break; | 567 | } break; |
| 556 | case CheatVmOpcodeType::ReadWriteStaticRegister: { | 568 | case CheatVmOpcodeType::ReadWriteStaticRegister: { |
| 557 | ReadWriteStaticRegisterOpcode rw_static_reg{}; | ||
| 558 | // C3000XXx | 569 | // C3000XXx |
| 559 | // C3 = opcode 0xC3. | 570 | // C3 = opcode 0xC3. |
| 560 | // XX = static register index. | 571 | // XX = static register index. |
| 561 | // x = register index. | 572 | // x = register index. |
| 562 | rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF); | 573 | opcode.opcode = ReadWriteStaticRegisterOpcode{ |
| 563 | rw_static_reg.idx = (first_dword & 0xF); | 574 | .static_idx = (first_dword >> 4) & 0xFF, |
| 564 | opcode.opcode = rw_static_reg; | 575 | .idx = first_dword & 0xF, |
| 576 | }; | ||
| 565 | } break; | 577 | } break; |
| 566 | case CheatVmOpcodeType::DebugLog: { | 578 | case CheatVmOpcodeType::DebugLog: { |
| 567 | DebugLogOpcode debug_log{}; | ||
| 568 | // FFFTIX## | 579 | // FFFTIX## |
| 569 | // FFFTI0Ma aaaaaaaa | 580 | // FFFTI0Ma aaaaaaaa |
| 570 | // FFFTI1Mr | 581 | // FFFTI1Mr |
| @@ -583,31 +594,36 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 583 | // a = relative address. | 594 | // a = relative address. |
| 584 | // r = offset register. | 595 | // r = offset register. |
| 585 | // X = value register. | 596 | // X = value register. |
| 586 | debug_log.bit_width = (first_dword >> 16) & 0xF; | 597 | DebugLogOpcode debug_log{ |
| 587 | debug_log.log_id = ((first_dword >> 12) & 0xF); | 598 | .bit_width = (first_dword >> 16) & 0xF, |
| 588 | debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF); | 599 | .log_id = (first_dword >> 12) & 0xF, |
| 600 | .val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF), | ||
| 601 | .mem_type = MemoryAccessType::MainNso, | ||
| 602 | .addr_reg_index = 0, | ||
| 603 | .val_reg_index = 0, | ||
| 604 | .ofs_reg_index = 0, | ||
| 605 | .rel_address = 0, | ||
| 606 | }; | ||
| 589 | 607 | ||
| 590 | switch (debug_log.val_type) { | 608 | switch (debug_log.val_type) { |
| 591 | case DebugLogValueType::RegisterValue: | 609 | case DebugLogValueType::RegisterValue: |
| 592 | debug_log.val_reg_index = ((first_dword >> 4) & 0xF); | 610 | debug_log.val_reg_index = (first_dword >> 4) & 0xF; |
| 593 | break; | 611 | break; |
| 594 | case DebugLogValueType::MemoryRelAddr: | 612 | case DebugLogValueType::MemoryRelAddr: |
| 595 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 613 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 596 | debug_log.rel_address = | 614 | debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 597 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 598 | break; | 615 | break; |
| 599 | case DebugLogValueType::MemoryOfsReg: | 616 | case DebugLogValueType::MemoryOfsReg: |
| 600 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 617 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 601 | debug_log.ofs_reg_index = (first_dword & 0xF); | 618 | debug_log.ofs_reg_index = first_dword & 0xF; |
| 602 | break; | 619 | break; |
| 603 | case DebugLogValueType::RegisterRelAddr: | 620 | case DebugLogValueType::RegisterRelAddr: |
| 604 | debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); | 621 | debug_log.addr_reg_index = (first_dword >> 4) & 0xF; |
| 605 | debug_log.rel_address = | 622 | debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 606 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 607 | break; | 623 | break; |
| 608 | case DebugLogValueType::RegisterOfsReg: | 624 | case DebugLogValueType::RegisterOfsReg: |
| 609 | debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); | 625 | debug_log.addr_reg_index = (first_dword >> 4) & 0xF; |
| 610 | debug_log.ofs_reg_index = (first_dword & 0xF); | 626 | debug_log.ofs_reg_index = first_dword & 0xF; |
| 611 | break; | 627 | break; |
| 612 | } | 628 | } |
| 613 | opcode.opcode = debug_log; | 629 | opcode.opcode = debug_log; |
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 29339ead7..b899ac884 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp | |||
| @@ -74,15 +74,16 @@ void PerfStats::EndGameFrame() { | |||
| 74 | game_frames += 1; | 74 | game_frames += 1; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | double PerfStats::GetMeanFrametime() { | 77 | double PerfStats::GetMeanFrametime() const { |
| 78 | std::lock_guard lock{object_mutex}; | 78 | std::lock_guard lock{object_mutex}; |
| 79 | 79 | ||
| 80 | if (current_index <= IgnoreFrames) { | 80 | if (current_index <= IgnoreFrames) { |
| 81 | return 0; | 81 | return 0; |
| 82 | } | 82 | } |
| 83 | |||
| 83 | const double sum = std::accumulate(perf_history.begin() + IgnoreFrames, | 84 | const double sum = std::accumulate(perf_history.begin() + IgnoreFrames, |
| 84 | perf_history.begin() + current_index, 0.0); | 85 | perf_history.begin() + current_index, 0.0); |
| 85 | return sum / (current_index - IgnoreFrames); | 86 | return sum / static_cast<double>(current_index - IgnoreFrames); |
| 86 | } | 87 | } |
| 87 | 88 | ||
| 88 | PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { | 89 | PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { |
| @@ -94,12 +95,13 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us | |||
| 94 | 95 | ||
| 95 | const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; | 96 | const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; |
| 96 | 97 | ||
| 97 | PerfStatsResults results{}; | 98 | const PerfStatsResults results{ |
| 98 | results.system_fps = static_cast<double>(system_frames) / interval; | 99 | .system_fps = static_cast<double>(system_frames) / interval, |
| 99 | results.game_fps = static_cast<double>(game_frames) / interval; | 100 | .game_fps = static_cast<double>(game_frames) / interval, |
| 100 | results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / | 101 | .frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / |
| 101 | static_cast<double>(system_frames); | 102 | static_cast<double>(system_frames), |
| 102 | results.emulation_speed = system_us_per_second.count() / 1'000'000.0; | 103 | .emulation_speed = system_us_per_second.count() / 1'000'000.0, |
| 104 | }; | ||
| 103 | 105 | ||
| 104 | // Reset counters | 106 | // Reset counters |
| 105 | reset_point = now; | 107 | reset_point = now; |
| @@ -111,7 +113,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us | |||
| 111 | return results; | 113 | return results; |
| 112 | } | 114 | } |
| 113 | 115 | ||
| 114 | double PerfStats::GetLastFrameTimeScale() { | 116 | double PerfStats::GetLastFrameTimeScale() const { |
| 115 | std::lock_guard lock{object_mutex}; | 117 | std::lock_guard lock{object_mutex}; |
| 116 | 118 | ||
| 117 | constexpr double FRAME_LENGTH = 1.0 / 60; | 119 | constexpr double FRAME_LENGTH = 1.0 / 60; |
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index d9a64f072..69256b960 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h | |||
| @@ -30,7 +30,6 @@ struct PerfStatsResults { | |||
| 30 | class PerfStats { | 30 | class PerfStats { |
| 31 | public: | 31 | public: |
| 32 | explicit PerfStats(u64 title_id); | 32 | explicit PerfStats(u64 title_id); |
| 33 | |||
| 34 | ~PerfStats(); | 33 | ~PerfStats(); |
| 35 | 34 | ||
| 36 | using Clock = std::chrono::high_resolution_clock; | 35 | using Clock = std::chrono::high_resolution_clock; |
| @@ -42,18 +41,18 @@ public: | |||
| 42 | PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); | 41 | PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); |
| 43 | 42 | ||
| 44 | /** | 43 | /** |
| 45 | * Returns the Arthimetic Mean of all frametime values stored in the performance history. | 44 | * Returns the arithmetic mean of all frametime values stored in the performance history. |
| 46 | */ | 45 | */ |
| 47 | double GetMeanFrametime(); | 46 | double GetMeanFrametime() const; |
| 48 | 47 | ||
| 49 | /** | 48 | /** |
| 50 | * Gets the ratio between walltime and the emulated time of the previous system frame. This is | 49 | * Gets the ratio between walltime and the emulated time of the previous system frame. This is |
| 51 | * useful for scaling inputs or outputs moving between the two time domains. | 50 | * useful for scaling inputs or outputs moving between the two time domains. |
| 52 | */ | 51 | */ |
| 53 | double GetLastFrameTimeScale(); | 52 | double GetLastFrameTimeScale() const; |
| 54 | 53 | ||
| 55 | private: | 54 | private: |
| 56 | std::mutex object_mutex{}; | 55 | mutable std::mutex object_mutex; |
| 57 | 56 | ||
| 58 | /// Title ID for the game that is running. 0 if there is no game running yet | 57 | /// Title ID for the game that is running. 0 if there is no game running yet |
| 59 | u64 title_id{0}; | 58 | u64 title_id{0}; |
| @@ -61,7 +60,7 @@ private: | |||
| 61 | std::size_t current_index{0}; | 60 | std::size_t current_index{0}; |
| 62 | /// Stores an hour of historical frametime data useful for processing and tracking performance | 61 | /// Stores an hour of historical frametime data useful for processing and tracking performance |
| 63 | /// regressions with code changes. | 62 | /// regressions with code changes. |
| 64 | std::array<double, 216000> perf_history = {}; | 63 | std::array<double, 216000> perf_history{}; |
| 65 | 64 | ||
| 66 | /// Point when the cumulative counters were reset | 65 | /// Point when the cumulative counters were reset |
| 67 | Clock::time_point reset_point = Clock::now(); | 66 | Clock::time_point reset_point = Clock::now(); |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 44252dd81..416b2d866 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -173,7 +173,6 @@ void RestoreGlobalState() { | |||
| 173 | values.use_assembly_shaders.SetGlobal(true); | 173 | values.use_assembly_shaders.SetGlobal(true); |
| 174 | values.use_asynchronous_shaders.SetGlobal(true); | 174 | values.use_asynchronous_shaders.SetGlobal(true); |
| 175 | values.use_fast_gpu_time.SetGlobal(true); | 175 | values.use_fast_gpu_time.SetGlobal(true); |
| 176 | values.force_30fps_mode.SetGlobal(true); | ||
| 177 | values.bg_red.SetGlobal(true); | 176 | values.bg_red.SetGlobal(true); |
| 178 | values.bg_green.SetGlobal(true); | 177 | values.bg_green.SetGlobal(true); |
| 179 | values.bg_blue.SetGlobal(true); | 178 | values.bg_blue.SetGlobal(true); |
diff --git a/src/core/settings.h b/src/core/settings.h index 386233fdf..bb145f193 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -435,7 +435,6 @@ struct Values { | |||
| 435 | Setting<bool> use_vsync; | 435 | Setting<bool> use_vsync; |
| 436 | Setting<bool> use_assembly_shaders; | 436 | Setting<bool> use_assembly_shaders; |
| 437 | Setting<bool> use_asynchronous_shaders; | 437 | Setting<bool> use_asynchronous_shaders; |
| 438 | Setting<bool> force_30fps_mode; | ||
| 439 | Setting<bool> use_fast_gpu_time; | 438 | Setting<bool> use_fast_gpu_time; |
| 440 | 439 | ||
| 441 | Setting<float> bg_red; | 440 | Setting<float> bg_red; |
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 27b894b51..5c674a099 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -55,10 +55,11 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v | |||
| 55 | 55 | ||
| 56 | Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) | 56 | Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) |
| 57 | : core_timing{core_timing_}, memory{memory_} { | 57 | : core_timing{core_timing_}, memory{memory_} { |
| 58 | event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback", | 58 | event = Core::Timing::CreateEvent( |
| 59 | [this](u64 userdata, std::chrono::nanoseconds ns_late) { | 59 | "MemoryFreezer::FrameCallback", |
| 60 | FrameCallback(userdata, ns_late); | 60 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 61 | }); | 61 | FrameCallback(user_data, ns_late); |
| 62 | }); | ||
| 62 | core_timing.ScheduleEvent(memory_freezer_ns, event); | 63 | core_timing.ScheduleEvent(memory_freezer_ns, event); |
| 63 | } | 64 | } |
| 64 | 65 | ||
| @@ -106,28 +107,21 @@ void Freezer::Unfreeze(VAddr address) { | |||
| 106 | 107 | ||
| 107 | LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); | 108 | LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); |
| 108 | 109 | ||
| 109 | entries.erase( | 110 | std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; }); |
| 110 | std::remove_if(entries.begin(), entries.end(), | ||
| 111 | [&address](const Entry& entry) { return entry.address == address; }), | ||
| 112 | entries.end()); | ||
| 113 | } | 111 | } |
| 114 | 112 | ||
| 115 | bool Freezer::IsFrozen(VAddr address) const { | 113 | bool Freezer::IsFrozen(VAddr address) const { |
| 116 | std::lock_guard lock{entries_mutex}; | 114 | std::lock_guard lock{entries_mutex}; |
| 117 | 115 | ||
| 118 | return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { | 116 | return FindEntry(address) != entries.cend(); |
| 119 | return entry.address == address; | ||
| 120 | }) != entries.end(); | ||
| 121 | } | 117 | } |
| 122 | 118 | ||
| 123 | void Freezer::SetFrozenValue(VAddr address, u64 value) { | 119 | void Freezer::SetFrozenValue(VAddr address, u64 value) { |
| 124 | std::lock_guard lock{entries_mutex}; | 120 | std::lock_guard lock{entries_mutex}; |
| 125 | 121 | ||
| 126 | const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { | 122 | const auto iter = FindEntry(address); |
| 127 | return entry.address == address; | ||
| 128 | }); | ||
| 129 | 123 | ||
| 130 | if (iter == entries.end()) { | 124 | if (iter == entries.cend()) { |
| 131 | LOG_ERROR(Common_Memory, | 125 | LOG_ERROR(Common_Memory, |
| 132 | "Tried to set freeze value for address={:016X} that is not frozen!", address); | 126 | "Tried to set freeze value for address={:016X} that is not frozen!", address); |
| 133 | return; | 127 | return; |
| @@ -142,11 +136,9 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) { | |||
| 142 | std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { | 136 | std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { |
| 143 | std::lock_guard lock{entries_mutex}; | 137 | std::lock_guard lock{entries_mutex}; |
| 144 | 138 | ||
| 145 | const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { | 139 | const auto iter = FindEntry(address); |
| 146 | return entry.address == address; | ||
| 147 | }); | ||
| 148 | 140 | ||
| 149 | if (iter == entries.end()) { | 141 | if (iter == entries.cend()) { |
| 150 | return std::nullopt; | 142 | return std::nullopt; |
| 151 | } | 143 | } |
| 152 | 144 | ||
| @@ -159,7 +151,17 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const { | |||
| 159 | return entries; | 151 | return entries; |
| 160 | } | 152 | } |
| 161 | 153 | ||
| 162 | void Freezer::FrameCallback(u64, std::chrono::nanoseconds ns_late) { | 154 | Freezer::Entries::iterator Freezer::FindEntry(VAddr address) { |
| 155 | return std::find_if(entries.begin(), entries.end(), | ||
| 156 | [address](const Entry& entry) { return entry.address == address; }); | ||
| 157 | } | ||
| 158 | |||
| 159 | Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const { | ||
| 160 | return std::find_if(entries.begin(), entries.end(), | ||
| 161 | [address](const Entry& entry) { return entry.address == address; }); | ||
| 162 | } | ||
| 163 | |||
| 164 | void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { | ||
| 163 | if (!IsActive()) { | 165 | if (!IsActive()) { |
| 164 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); | 166 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); |
| 165 | return; | 167 | return; |
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index 8438783d5..0fdb701a7 100644 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h | |||
| @@ -73,13 +73,18 @@ public: | |||
| 73 | std::vector<Entry> GetEntries() const; | 73 | std::vector<Entry> GetEntries() const; |
| 74 | 74 | ||
| 75 | private: | 75 | private: |
| 76 | void FrameCallback(u64 userdata, std::chrono::nanoseconds ns_late); | 76 | using Entries = std::vector<Entry>; |
| 77 | |||
| 78 | Entries::iterator FindEntry(VAddr address); | ||
| 79 | Entries::const_iterator FindEntry(VAddr address) const; | ||
| 80 | |||
| 81 | void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 77 | void FillEntryReads(); | 82 | void FillEntryReads(); |
| 78 | 83 | ||
| 79 | std::atomic_bool active{false}; | 84 | std::atomic_bool active{false}; |
| 80 | 85 | ||
| 81 | mutable std::mutex entries_mutex; | 86 | mutable std::mutex entries_mutex; |
| 82 | std::vector<Entry> entries; | 87 | Entries entries; |
| 83 | 88 | ||
| 84 | std::shared_ptr<Core::Timing::EventType> event; | 89 | std::shared_ptr<Core::Timing::EventType> event; |
| 85 | Core::Timing::CoreTiming& core_timing; | 90 | Core::Timing::CoreTiming& core_timing; |
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index 02d06876f..74759ea7d 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp | |||
| @@ -254,7 +254,7 @@ void Adapter::GetGCEndpoint(libusb_device* device) { | |||
| 254 | sizeof(clear_payload), nullptr, 16); | 254 | sizeof(clear_payload), nullptr, 16); |
| 255 | 255 | ||
| 256 | adapter_thread_running = true; | 256 | adapter_thread_running = true; |
| 257 | adapter_input_thread = std::thread([=] { Read(); }); // Read input | 257 | adapter_input_thread = std::thread(&Adapter::Read, this); |
| 258 | } | 258 | } |
| 259 | 259 | ||
| 260 | Adapter::~Adapter() { | 260 | Adapter::~Adapter() { |
| @@ -265,7 +265,9 @@ void Adapter::Reset() { | |||
| 265 | if (adapter_thread_running) { | 265 | if (adapter_thread_running) { |
| 266 | adapter_thread_running = false; | 266 | adapter_thread_running = false; |
| 267 | } | 267 | } |
| 268 | adapter_input_thread.join(); | 268 | if (adapter_input_thread.joinable()) { |
| 269 | adapter_input_thread.join(); | ||
| 270 | } | ||
| 269 | 271 | ||
| 270 | adapter_controllers_status.fill(ControllerTypes::None); | 272 | adapter_controllers_status.fill(ControllerTypes::None); |
| 271 | get_origin.fill(true); | 273 | get_origin.fill(true); |
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 96e22d3ad..b346fdf8e 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp | |||
| @@ -76,8 +76,7 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param | |||
| 76 | 76 | ||
| 77 | // button is not an axis/stick button | 77 | // button is not an axis/stick button |
| 78 | if (button_id != PAD_STICK_ID) { | 78 | if (button_id != PAD_STICK_ID) { |
| 79 | auto button = std::make_unique<GCButton>(port, button_id, adapter.get()); | 79 | return std::make_unique<GCButton>(port, button_id, adapter.get()); |
| 80 | return std::move(button); | ||
| 81 | } | 80 | } |
| 82 | 81 | ||
| 83 | // For Axis buttons, used by the binary sticks. | 82 | // For Axis buttons, used by the binary sticks. |
| @@ -149,19 +148,17 @@ void GCButtonFactory::EndConfiguration() { | |||
| 149 | 148 | ||
| 150 | class GCAnalog final : public Input::AnalogDevice { | 149 | class GCAnalog final : public Input::AnalogDevice { |
| 151 | public: | 150 | public: |
| 152 | GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter) | 151 | GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter, |
| 152 | float range_) | ||
| 153 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), | 153 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), |
| 154 | origin_value_x(adapter->GetOriginValue(port_, axis_x_)), | 154 | origin_value_x(adapter->GetOriginValue(port_, axis_x_)), |
| 155 | origin_value_y(adapter->GetOriginValue(port_, axis_y_)) {} | 155 | origin_value_y(adapter->GetOriginValue(port_, axis_y_)), range(range_) {} |
| 156 | 156 | ||
| 157 | float GetAxis(int axis) const { | 157 | float GetAxis(int axis) const { |
| 158 | if (gcadapter->DeviceConnected(port)) { | 158 | if (gcadapter->DeviceConnected(port)) { |
| 159 | std::lock_guard lock{mutex}; | 159 | std::lock_guard lock{mutex}; |
| 160 | const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y; | 160 | const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y; |
| 161 | // division is not by a perfect 128 to account for some variance in center location | 161 | return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range); |
| 162 | // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range | ||
| 163 | // [20-230] | ||
| 164 | return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / 95.0f; | ||
| 165 | } | 162 | } |
| 166 | return 0.0f; | 163 | return 0.0f; |
| 167 | } | 164 | } |
| @@ -216,6 +213,7 @@ private: | |||
| 216 | GCAdapter::Adapter* gcadapter; | 213 | GCAdapter::Adapter* gcadapter; |
| 217 | const float origin_value_x; | 214 | const float origin_value_x; |
| 218 | const float origin_value_y; | 215 | const float origin_value_y; |
| 216 | const float range; | ||
| 219 | mutable std::mutex mutex; | 217 | mutable std::mutex mutex; |
| 220 | }; | 218 | }; |
| 221 | 219 | ||
| @@ -235,8 +233,9 @@ std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::Param | |||
| 235 | const int axis_x = params.Get("axis_x", 0); | 233 | const int axis_x = params.Get("axis_x", 0); |
| 236 | const int axis_y = params.Get("axis_y", 1); | 234 | const int axis_y = params.Get("axis_y", 1); |
| 237 | const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); | 235 | const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); |
| 236 | const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); | ||
| 238 | 237 | ||
| 239 | return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get()); | 238 | return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range); |
| 240 | } | 239 | } |
| 241 | 240 | ||
| 242 | void GCAnalogFactory::BeginConfiguration() { | 241 | void GCAnalogFactory::BeginConfiguration() { |
| @@ -264,7 +263,8 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() { | |||
| 264 | if (analog_x_axis == -1) { | 263 | if (analog_x_axis == -1) { |
| 265 | analog_x_axis = axis; | 264 | analog_x_axis = axis; |
| 266 | controller_number = static_cast<int>(port); | 265 | controller_number = static_cast<int>(port); |
| 267 | } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) { | 266 | } else if (analog_y_axis == -1 && analog_x_axis != axis && |
| 267 | controller_number == static_cast<int>(port)) { | ||
| 268 | analog_y_axis = axis; | 268 | analog_y_axis = axis; |
| 269 | } | 269 | } |
| 270 | } | 270 | } |
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index 675b477fa..d76c279d3 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp | |||
| @@ -66,14 +66,14 @@ public: | |||
| 66 | state.axes.insert_or_assign(axis, value); | 66 | state.axes.insert_or_assign(axis, value); |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | float GetAxis(int axis) const { | 69 | float GetAxis(int axis, float range) const { |
| 70 | std::lock_guard lock{mutex}; | 70 | std::lock_guard lock{mutex}; |
| 71 | return state.axes.at(axis) / 32767.0f; | 71 | return state.axes.at(axis) / (32767.0f * range); |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { | 74 | std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const { |
| 75 | float x = GetAxis(axis_x); | 75 | float x = GetAxis(axis_x, range); |
| 76 | float y = GetAxis(axis_y); | 76 | float y = GetAxis(axis_y, range); |
| 77 | y = -y; // 3DS uses an y-axis inverse from SDL | 77 | y = -y; // 3DS uses an y-axis inverse from SDL |
| 78 | 78 | ||
| 79 | // Make sure the coordinates are in the unit circle, | 79 | // Make sure the coordinates are in the unit circle, |
| @@ -313,7 +313,7 @@ public: | |||
| 313 | trigger_if_greater(trigger_if_greater_) {} | 313 | trigger_if_greater(trigger_if_greater_) {} |
| 314 | 314 | ||
| 315 | bool GetStatus() const override { | 315 | bool GetStatus() const override { |
| 316 | const float axis_value = joystick->GetAxis(axis); | 316 | const float axis_value = joystick->GetAxis(axis, 1.0f); |
| 317 | if (trigger_if_greater) { | 317 | if (trigger_if_greater) { |
| 318 | return axis_value > threshold; | 318 | return axis_value > threshold; |
| 319 | } | 319 | } |
| @@ -329,11 +329,13 @@ private: | |||
| 329 | 329 | ||
| 330 | class SDLAnalog final : public Input::AnalogDevice { | 330 | class SDLAnalog final : public Input::AnalogDevice { |
| 331 | public: | 331 | public: |
| 332 | SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_) | 332 | SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_, |
| 333 | : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_) {} | 333 | float range_) |
| 334 | : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), | ||
| 335 | range(range_) {} | ||
| 334 | 336 | ||
| 335 | std::tuple<float, float> GetStatus() const override { | 337 | std::tuple<float, float> GetStatus() const override { |
| 336 | const auto [x, y] = joystick->GetAnalog(axis_x, axis_y); | 338 | const auto [x, y] = joystick->GetAnalog(axis_x, axis_y, range); |
| 337 | const float r = std::sqrt((x * x) + (y * y)); | 339 | const float r = std::sqrt((x * x) + (y * y)); |
| 338 | if (r > deadzone) { | 340 | if (r > deadzone) { |
| 339 | return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), | 341 | return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), |
| @@ -363,6 +365,7 @@ private: | |||
| 363 | const int axis_x; | 365 | const int axis_x; |
| 364 | const int axis_y; | 366 | const int axis_y; |
| 365 | const float deadzone; | 367 | const float deadzone; |
| 368 | const float range; | ||
| 366 | }; | 369 | }; |
| 367 | 370 | ||
| 368 | /// A button device factory that creates button devices from SDL joystick | 371 | /// A button device factory that creates button devices from SDL joystick |
| @@ -458,13 +461,13 @@ public: | |||
| 458 | const int axis_x = params.Get("axis_x", 0); | 461 | const int axis_x = params.Get("axis_x", 0); |
| 459 | const int axis_y = params.Get("axis_y", 1); | 462 | const int axis_y = params.Get("axis_y", 1); |
| 460 | const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); | 463 | const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f); |
| 461 | 464 | const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); | |
| 462 | auto joystick = state.GetSDLJoystickByGUID(guid, port); | 465 | auto joystick = state.GetSDLJoystickByGUID(guid, port); |
| 463 | 466 | ||
| 464 | // This is necessary so accessing GetAxis with axis_x and axis_y won't crash | 467 | // This is necessary so accessing GetAxis with axis_x and axis_y won't crash |
| 465 | joystick->SetAxis(axis_x, 0); | 468 | joystick->SetAxis(axis_x, 0); |
| 466 | joystick->SetAxis(axis_y, 0); | 469 | joystick->SetAxis(axis_y, 0); |
| 467 | return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone); | 470 | return std::make_unique<SDLAnalog>(joystick, axis_x, axis_y, deadzone, range); |
| 468 | } | 471 | } |
| 469 | 472 | ||
| 470 | private: | 473 | private: |
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index e63c73c4f..3f4eaf448 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include <functional> | 9 | #include <functional> |
| 10 | #include <thread> | 10 | #include <thread> |
| 11 | #include <boost/asio.hpp> | 11 | #include <boost/asio.hpp> |
| 12 | #include <boost/bind.hpp> | ||
| 13 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 14 | #include "input_common/udp/client.h" | 13 | #include "input_common/udp/client.h" |
| 15 | #include "input_common/udp/protocol.h" | 14 | #include "input_common/udp/protocol.h" |
| @@ -225,8 +224,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie | |||
| 225 | } else { | 224 | } else { |
| 226 | failure_callback(); | 225 | failure_callback(); |
| 227 | } | 226 | } |
| 228 | }) | 227 | }).detach(); |
| 229 | .detach(); | ||
| 230 | } | 228 | } |
| 231 | 229 | ||
| 232 | CalibrationConfigurationJob::CalibrationConfigurationJob( | 230 | CalibrationConfigurationJob::CalibrationConfigurationJob( |
| @@ -280,8 +278,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( | |||
| 280 | complete_event.Wait(); | 278 | complete_event.Wait(); |
| 281 | socket.Stop(); | 279 | socket.Stop(); |
| 282 | worker_thread.join(); | 280 | worker_thread.join(); |
| 283 | }) | 281 | }).detach(); |
| 284 | .detach(); | ||
| 285 | } | 282 | } |
| 286 | 283 | ||
| 287 | CalibrationConfigurationJob::~CalibrationConfigurationJob() { | 284 | CalibrationConfigurationJob::~CalibrationConfigurationJob() { |
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 244463a47..b35459152 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -25,10 +25,10 @@ std::bitset<CB_IDS.size()> callbacks_ran_flags; | |||
| 25 | u64 expected_callback = 0; | 25 | u64 expected_callback = 0; |
| 26 | 26 | ||
| 27 | template <unsigned int IDX> | 27 | template <unsigned int IDX> |
| 28 | void HostCallbackTemplate(u64 userdata, std::chrono::nanoseconds ns_late) { | 28 | void HostCallbackTemplate(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 29 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); | 29 | static_assert(IDX < CB_IDS.size(), "IDX out of range"); |
| 30 | callbacks_ran_flags.set(IDX); | 30 | callbacks_ran_flags.set(IDX); |
| 31 | REQUIRE(CB_IDS[IDX] == userdata); | 31 | REQUIRE(CB_IDS[IDX] == user_data); |
| 32 | REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); | 32 | REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]); |
| 33 | delays[IDX] = ns_late.count(); | 33 | delays[IDX] = ns_late.count(); |
| 34 | ++expected_callback; | 34 | ++expected_callback; |
| @@ -46,20 +46,16 @@ struct ScopeInit final { | |||
| 46 | Core::Timing::CoreTiming core_timing; | 46 | Core::Timing::CoreTiming core_timing; |
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | #pragma optimize("", off) | ||
| 50 | |||
| 51 | u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { | 49 | u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { |
| 52 | u64 start = core_timing.GetGlobalTimeNs().count(); | 50 | const u64 start = core_timing.GetGlobalTimeNs().count(); |
| 53 | u64 placebo = 0; | 51 | volatile u64 placebo = 0; |
| 54 | for (std::size_t i = 0; i < 1000; i++) { | 52 | for (std::size_t i = 0; i < 1000; i++) { |
| 55 | placebo += core_timing.GetGlobalTimeNs().count(); | 53 | placebo = placebo + core_timing.GetGlobalTimeNs().count(); |
| 56 | } | 54 | } |
| 57 | u64 end = core_timing.GetGlobalTimeNs().count(); | 55 | const u64 end = core_timing.GetGlobalTimeNs().count(); |
| 58 | return (end - start); | 56 | return end - start; |
| 59 | } | 57 | } |
| 60 | 58 | ||
| 61 | #pragma optimize("", on) | ||
| 62 | |||
| 63 | } // Anonymous namespace | 59 | } // Anonymous namespace |
| 64 | 60 | ||
| 65 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | 61 | TEST_CASE("CoreTiming[BasicOrder]", "[core]") { |
diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h index d1082566d..51766349b 100644 --- a/src/video_core/compatible_formats.h +++ b/src/video_core/compatible_formats.h | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | ||
| 6 | |||
| 5 | #include <array> | 7 | #include <array> |
| 6 | #include <bitset> | 8 | #include <bitset> |
| 7 | #include <cstddef> | 9 | #include <cstddef> |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index a2d3d7823..e88290754 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -94,7 +94,8 @@ void MaxwellDMA::CopyPitchToPitch() { | |||
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | void MaxwellDMA::CopyBlockLinearToPitch() { | 96 | void MaxwellDMA::CopyBlockLinearToPitch() { |
| 97 | ASSERT(regs.src_params.block_size.depth == 0); | 97 | UNIMPLEMENTED_IF(regs.src_params.block_size.depth != 0); |
| 98 | UNIMPLEMENTED_IF(regs.src_params.layer != 0); | ||
| 98 | 99 | ||
| 99 | // Optimized path for micro copies. | 100 | // Optimized path for micro copies. |
| 100 | const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; | 101 | const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; |
| @@ -123,17 +124,12 @@ void MaxwellDMA::CopyBlockLinearToPitch() { | |||
| 123 | write_buffer.resize(dst_size); | 124 | write_buffer.resize(dst_size); |
| 124 | } | 125 | } |
| 125 | 126 | ||
| 126 | if (Settings::IsGPULevelExtreme()) { | 127 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); |
| 127 | memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); | 128 | memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); |
| 128 | memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); | ||
| 129 | } else { | ||
| 130 | memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(), src_size); | ||
| 131 | memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); | ||
| 132 | } | ||
| 133 | 129 | ||
| 134 | UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel, | 130 | UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, width, bytes_per_pixel, |
| 135 | read_buffer.data() + src_layer_size * src_params.layer, write_buffer.data(), | 131 | block_height, src_params.origin.x, src_params.origin.y, write_buffer.data(), |
| 136 | block_height, src_params.origin.x, src_params.origin.y); | 132 | read_buffer.data()); |
| 137 | 133 | ||
| 138 | memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); | 134 | memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); |
| 139 | } | 135 | } |
| @@ -198,7 +194,6 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { | |||
| 198 | if (read_buffer.size() < src_size) { | 194 | if (read_buffer.size() < src_size) { |
| 199 | read_buffer.resize(src_size); | 195 | read_buffer.resize(src_size); |
| 200 | } | 196 | } |
| 201 | |||
| 202 | if (write_buffer.size() < dst_size) { | 197 | if (write_buffer.size() < dst_size) { |
| 203 | write_buffer.resize(dst_size); | 198 | write_buffer.resize(dst_size); |
| 204 | } | 199 | } |
| @@ -212,8 +207,8 @@ void MaxwellDMA::FastCopyBlockLinearToPitch() { | |||
| 212 | } | 207 | } |
| 213 | 208 | ||
| 214 | UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width, | 209 | UnswizzleSubrect(regs.line_length_in, regs.line_count, regs.pitch_out, regs.src_params.width, |
| 215 | bytes_per_pixel, read_buffer.data(), write_buffer.data(), | 210 | bytes_per_pixel, regs.src_params.block_size.height, pos_x, pos_y, |
| 216 | regs.src_params.block_size.height, pos_x, pos_y); | 211 | write_buffer.data(), read_buffer.data()); |
| 217 | 212 | ||
| 218 | memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); | 213 | memory_manager.WriteBlock(regs.offset_out, write_buffer.data(), dst_size); |
| 219 | } | 214 | } |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8e19c3373..512578c8b 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -81,7 +81,7 @@ void GPU::WaitFence(u32 syncpoint_id, u32 value) { | |||
| 81 | } | 81 | } |
| 82 | MICROPROFILE_SCOPE(GPU_wait); | 82 | MICROPROFILE_SCOPE(GPU_wait); |
| 83 | std::unique_lock lock{sync_mutex}; | 83 | std::unique_lock lock{sync_mutex}; |
| 84 | sync_cv.wait(lock, [=]() { return syncpoints[syncpoint_id].load() >= value; }); | 84 | sync_cv.wait(lock, [=, this] { return syncpoints[syncpoint_id].load() >= value; }); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | void GPU::IncrementSyncPoint(const u32 syncpoint_id) { | 87 | void GPU::IncrementSyncPoint(const u32 syncpoint_id) { |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 19a34c402..ebfc7b0c7 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -252,7 +252,7 @@ public: | |||
| 252 | const Tegra::DmaPusher& DmaPusher() const; | 252 | const Tegra::DmaPusher& DmaPusher() const; |
| 253 | 253 | ||
| 254 | struct Regs { | 254 | struct Regs { |
| 255 | static constexpr size_t NUM_REGS = 0x100; | 255 | static constexpr size_t NUM_REGS = 0x40; |
| 256 | 256 | ||
| 257 | union { | 257 | union { |
| 258 | struct { | 258 | struct { |
| @@ -271,7 +271,7 @@ public: | |||
| 271 | u32 semaphore_trigger; | 271 | u32 semaphore_trigger; |
| 272 | INSERT_UNION_PADDING_WORDS(0xC); | 272 | INSERT_UNION_PADDING_WORDS(0xC); |
| 273 | 273 | ||
| 274 | // The puser and the puller share the reference counter, the pusher only has read | 274 | // The pusher and the puller share the reference counter, the pusher only has read |
| 275 | // access | 275 | // access |
| 276 | u32 reference_count; | 276 | u32 reference_count; |
| 277 | INSERT_UNION_PADDING_WORDS(0x5); | 277 | INSERT_UNION_PADDING_WORDS(0x5); |
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index ff5505d12..844164645 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/alignment.h" | 5 | #include "common/alignment.h" |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/core.h" | 7 | #include "core/core.h" |
| 9 | #include "core/hle/kernel/memory/page_table.h" | 8 | #include "core/hle/kernel/memory/page_table.h" |
| 10 | #include "core/hle/kernel/process.h" | 9 | #include "core/hle/kernel/process.h" |
| @@ -16,121 +15,137 @@ | |||
| 16 | namespace Tegra { | 15 | namespace Tegra { |
| 17 | 16 | ||
| 18 | MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | 17 | MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) |
| 19 | : rasterizer{rasterizer}, system{system} { | 18 | : system{system}, rasterizer{rasterizer}, page_table(page_table_size) {} |
| 20 | page_table.Resize(address_space_width, page_bits, false); | ||
| 21 | |||
| 22 | // Initialize the map with a single free region covering the entire managed space. | ||
| 23 | VirtualMemoryArea initial_vma; | ||
| 24 | initial_vma.size = address_space_end; | ||
| 25 | vma_map.emplace(initial_vma.base, initial_vma); | ||
| 26 | |||
| 27 | UpdatePageTableForVMA(initial_vma); | ||
| 28 | } | ||
| 29 | 19 | ||
| 30 | MemoryManager::~MemoryManager() = default; | 20 | MemoryManager::~MemoryManager() = default; |
| 31 | 21 | ||
| 32 | GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { | 22 | GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { |
| 33 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 23 | u64 remaining_size{size}; |
| 34 | const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; | 24 | for (u64 offset{}; offset < size; offset += page_size) { |
| 35 | 25 | if (remaining_size < page_size) { | |
| 36 | AllocateMemory(gpu_addr, 0, aligned_size); | 26 | SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size); |
| 37 | 27 | } else { | |
| 28 | SetPageEntry(gpu_addr + offset, page_entry + offset); | ||
| 29 | } | ||
| 30 | remaining_size -= page_size; | ||
| 31 | } | ||
| 38 | return gpu_addr; | 32 | return gpu_addr; |
| 39 | } | 33 | } |
| 40 | 34 | ||
| 41 | GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) { | 35 | GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) { |
| 42 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 36 | return UpdateRange(gpu_addr, cpu_addr, size); |
| 43 | 37 | } | |
| 44 | AllocateMemory(gpu_addr, 0, aligned_size); | ||
| 45 | 38 | ||
| 46 | return gpu_addr; | 39 | GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) { |
| 40 | return Map(cpu_addr, *FindFreeRange(size, align), size); | ||
| 47 | } | 41 | } |
| 48 | 42 | ||
| 49 | GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { | 43 | void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { |
| 50 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 44 | if (!size) { |
| 51 | const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; | 45 | return; |
| 46 | } | ||
| 52 | 47 | ||
| 53 | MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); | 48 | // Flush and invalidate through the GPU interface, to be asynchronous if possible. |
| 54 | ASSERT( | 49 | system.GPU().FlushAndInvalidateRegion(*GpuToCpuAddress(gpu_addr), size); |
| 55 | system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess()); | ||
| 56 | 50 | ||
| 57 | return gpu_addr; | 51 | UpdateRange(gpu_addr, PageEntry::State::Unmapped, size); |
| 58 | } | 52 | } |
| 59 | 53 | ||
| 60 | GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) { | 54 | std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) { |
| 61 | ASSERT((gpu_addr & page_mask) == 0); | 55 | for (u64 offset{}; offset < size; offset += page_size) { |
| 56 | if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) { | ||
| 57 | return {}; | ||
| 58 | } | ||
| 59 | } | ||
| 62 | 60 | ||
| 63 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 61 | return UpdateRange(gpu_addr, PageEntry::State::Allocated, size); |
| 62 | } | ||
| 64 | 63 | ||
| 65 | MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); | 64 | GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) { |
| 66 | ASSERT( | 65 | return *AllocateFixed(*FindFreeRange(size, align), size); |
| 67 | system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess()); | ||
| 68 | return gpu_addr; | ||
| 69 | } | 66 | } |
| 70 | 67 | ||
| 71 | GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { | 68 | void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) { |
| 72 | ASSERT((gpu_addr & page_mask) == 0); | 69 | if (!page_entry.IsValid()) { |
| 70 | return; | ||
| 71 | } | ||
| 73 | 72 | ||
| 74 | const u64 aligned_size{Common::AlignUp(size, page_size)}; | 73 | ASSERT(system.CurrentProcess() |
| 75 | const auto cpu_addr = GpuToCpuAddress(gpu_addr); | 74 | ->PageTable() |
| 76 | ASSERT(cpu_addr); | 75 | .LockForDeviceAddressSpace(page_entry.ToAddress(), size) |
| 76 | .IsSuccess()); | ||
| 77 | } | ||
| 77 | 78 | ||
| 78 | // Flush and invalidate through the GPU interface, to be asynchronous if possible. | 79 | void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) { |
| 79 | system.GPU().FlushAndInvalidateRegion(*cpu_addr, aligned_size); | 80 | if (!page_entry.IsValid()) { |
| 81 | return; | ||
| 82 | } | ||
| 80 | 83 | ||
| 81 | UnmapRange(gpu_addr, aligned_size); | ||
| 82 | ASSERT(system.CurrentProcess() | 84 | ASSERT(system.CurrentProcess() |
| 83 | ->PageTable() | 85 | ->PageTable() |
| 84 | .UnlockForDeviceAddressSpace(cpu_addr.value(), size) | 86 | .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size) |
| 85 | .IsSuccess()); | 87 | .IsSuccess()); |
| 86 | |||
| 87 | return gpu_addr; | ||
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) const { | 90 | PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const { |
| 91 | // Find the first Free VMA. | 91 | return page_table[PageEntryIndex(gpu_addr)]; |
| 92 | const VMAHandle vma_handle{ | 92 | } |
| 93 | std::find_if(vma_map.begin(), vma_map.end(), [region_start, size](const auto& vma) { | ||
| 94 | if (vma.second.type != VirtualMemoryArea::Type::Unmapped) { | ||
| 95 | return false; | ||
| 96 | } | ||
| 97 | 93 | ||
| 98 | const VAddr vma_end{vma.second.base + vma.second.size}; | 94 | void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) { |
| 99 | return vma_end > region_start && vma_end >= region_start + size; | 95 | // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to |
| 100 | })}; | 96 | // improper tracking, but should be fixed in the future. |
| 101 | 97 | ||
| 102 | if (vma_handle == vma_map.end()) { | 98 | //// Unlock the old page |
| 103 | return {}; | 99 | // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size); |
| 104 | } | ||
| 105 | 100 | ||
| 106 | return std::max(region_start, vma_handle->second.base); | 101 | //// Lock the new page |
| 107 | } | 102 | // TryLockPage(page_entry, size); |
| 108 | 103 | ||
| 109 | bool MemoryManager::IsAddressValid(GPUVAddr addr) const { | 104 | page_table[PageEntryIndex(gpu_addr)] = page_entry; |
| 110 | return (addr >> page_bits) < page_table.pointers.size(); | ||
| 111 | } | 105 | } |
| 112 | 106 | ||
| 113 | std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) const { | 107 | std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align) const { |
| 114 | if (!IsAddressValid(addr)) { | 108 | if (!align) { |
| 115 | return {}; | 109 | align = page_size; |
| 110 | } else { | ||
| 111 | align = Common::AlignUp(align, page_size); | ||
| 116 | } | 112 | } |
| 117 | 113 | ||
| 118 | const VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]}; | 114 | u64 available_size{}; |
| 119 | if (cpu_addr) { | 115 | GPUVAddr gpu_addr{address_space_start}; |
| 120 | return cpu_addr + (addr & page_mask); | 116 | while (gpu_addr + available_size < address_space_size) { |
| 117 | if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) { | ||
| 118 | available_size += page_size; | ||
| 119 | |||
| 120 | if (available_size >= size) { | ||
| 121 | return gpu_addr; | ||
| 122 | } | ||
| 123 | } else { | ||
| 124 | gpu_addr += available_size + page_size; | ||
| 125 | available_size = 0; | ||
| 126 | |||
| 127 | const auto remainder{gpu_addr % align}; | ||
| 128 | if (remainder) { | ||
| 129 | gpu_addr = (gpu_addr - remainder) + align; | ||
| 130 | } | ||
| 131 | } | ||
| 121 | } | 132 | } |
| 122 | 133 | ||
| 123 | return {}; | 134 | return {}; |
| 124 | } | 135 | } |
| 125 | 136 | ||
| 126 | template <typename T> | 137 | std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { |
| 127 | T MemoryManager::Read(GPUVAddr addr) const { | 138 | const auto page_entry{GetPageEntry(gpu_addr)}; |
| 128 | if (!IsAddressValid(addr)) { | 139 | if (!page_entry.IsValid()) { |
| 129 | return {}; | 140 | return {}; |
| 130 | } | 141 | } |
| 131 | 142 | ||
| 132 | const u8* page_pointer{GetPointer(addr)}; | 143 | return page_entry.ToAddress() + (gpu_addr & page_mask); |
| 133 | if (page_pointer) { | 144 | } |
| 145 | |||
| 146 | template <typename T> | ||
| 147 | T MemoryManager::Read(GPUVAddr addr) const { | ||
| 148 | if (auto page_pointer{GetPointer(addr)}; page_pointer) { | ||
| 134 | // NOTE: Avoid adding any extra logic to this fast-path block | 149 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 135 | T value; | 150 | T value; |
| 136 | std::memcpy(&value, page_pointer, sizeof(T)); | 151 | std::memcpy(&value, page_pointer, sizeof(T)); |
| @@ -144,12 +159,7 @@ T MemoryManager::Read(GPUVAddr addr) const { | |||
| 144 | 159 | ||
| 145 | template <typename T> | 160 | template <typename T> |
| 146 | void MemoryManager::Write(GPUVAddr addr, T data) { | 161 | void MemoryManager::Write(GPUVAddr addr, T data) { |
| 147 | if (!IsAddressValid(addr)) { | 162 | if (auto page_pointer{GetPointer(addr)}; page_pointer) { |
| 148 | return; | ||
| 149 | } | ||
| 150 | |||
| 151 | u8* page_pointer{GetPointer(addr)}; | ||
| 152 | if (page_pointer) { | ||
| 153 | // NOTE: Avoid adding any extra logic to this fast-path block | 163 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 154 | std::memcpy(page_pointer, &data, sizeof(T)); | 164 | std::memcpy(page_pointer, &data, sizeof(T)); |
| 155 | return; | 165 | return; |
| @@ -167,66 +177,49 @@ template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data); | |||
| 167 | template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data); | 177 | template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data); |
| 168 | template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data); | 178 | template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data); |
| 169 | 179 | ||
| 170 | u8* MemoryManager::GetPointer(GPUVAddr addr) { | 180 | u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) { |
| 171 | if (!IsAddressValid(addr)) { | 181 | if (!GetPageEntry(gpu_addr).IsValid()) { |
| 172 | return {}; | 182 | return {}; |
| 173 | } | 183 | } |
| 174 | 184 | ||
| 175 | auto& memory = system.Memory(); | 185 | const auto address{GpuToCpuAddress(gpu_addr)}; |
| 176 | 186 | if (!address) { | |
| 177 | const VAddr page_addr{page_table.backing_addr[addr >> page_bits]}; | 187 | return {}; |
| 178 | |||
| 179 | if (page_addr != 0) { | ||
| 180 | return memory.GetPointer(page_addr + (addr & page_mask)); | ||
| 181 | } | 188 | } |
| 182 | 189 | ||
| 183 | LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); | 190 | return system.Memory().GetPointer(*address); |
| 184 | return {}; | ||
| 185 | } | 191 | } |
| 186 | 192 | ||
| 187 | const u8* MemoryManager::GetPointer(GPUVAddr addr) const { | 193 | const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { |
| 188 | if (!IsAddressValid(addr)) { | 194 | if (!GetPageEntry(gpu_addr).IsValid()) { |
| 189 | return {}; | 195 | return {}; |
| 190 | } | 196 | } |
| 191 | 197 | ||
| 192 | const auto& memory = system.Memory(); | 198 | const auto address{GpuToCpuAddress(gpu_addr)}; |
| 193 | 199 | if (!address) { | |
| 194 | const VAddr page_addr{page_table.backing_addr[addr >> page_bits]}; | 200 | return {}; |
| 195 | |||
| 196 | if (page_addr != 0) { | ||
| 197 | return memory.GetPointer(page_addr + (addr & page_mask)); | ||
| 198 | } | 201 | } |
| 199 | 202 | ||
| 200 | LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr); | 203 | return system.Memory().GetPointer(*address); |
| 201 | return {}; | ||
| 202 | } | ||
| 203 | |||
| 204 | bool MemoryManager::IsBlockContinuous(const GPUVAddr start, const std::size_t size) const { | ||
| 205 | const std::size_t inner_size = size - 1; | ||
| 206 | const GPUVAddr end = start + inner_size; | ||
| 207 | const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start)); | ||
| 208 | const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end)); | ||
| 209 | const auto range = static_cast<std::size_t>(host_ptr_end - host_ptr_start); | ||
| 210 | return range == inner_size; | ||
| 211 | } | 204 | } |
| 212 | 205 | ||
| 213 | void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, | 206 | void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { |
| 214 | const std::size_t size) const { | ||
| 215 | std::size_t remaining_size{size}; | 207 | std::size_t remaining_size{size}; |
| 216 | std::size_t page_index{gpu_src_addr >> page_bits}; | 208 | std::size_t page_index{gpu_src_addr >> page_bits}; |
| 217 | std::size_t page_offset{gpu_src_addr & page_mask}; | 209 | std::size_t page_offset{gpu_src_addr & page_mask}; |
| 218 | 210 | ||
| 219 | auto& memory = system.Memory(); | ||
| 220 | |||
| 221 | while (remaining_size > 0) { | 211 | while (remaining_size > 0) { |
| 222 | const std::size_t copy_amount{ | 212 | const std::size_t copy_amount{ |
| 223 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | 213 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; |
| 224 | 214 | ||
| 225 | const VAddr src_addr{page_table.backing_addr[page_index] + page_offset}; | 215 | if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { |
| 226 | // Flush must happen on the rasterizer interface, such that memory is always synchronous | 216 | const auto src_addr{*page_addr + page_offset}; |
| 227 | // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu. | 217 | |
| 228 | rasterizer.FlushRegion(src_addr, copy_amount); | 218 | // Flush must happen on the rasterizer interface, such that memory is always synchronous |
| 229 | memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); | 219 | // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu. |
| 220 | rasterizer.FlushRegion(src_addr, copy_amount); | ||
| 221 | system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); | ||
| 222 | } | ||
| 230 | 223 | ||
| 231 | page_index++; | 224 | page_index++; |
| 232 | page_offset = 0; | 225 | page_offset = 0; |
| @@ -241,18 +234,17 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, | |||
| 241 | std::size_t page_index{gpu_src_addr >> page_bits}; | 234 | std::size_t page_index{gpu_src_addr >> page_bits}; |
| 242 | std::size_t page_offset{gpu_src_addr & page_mask}; | 235 | std::size_t page_offset{gpu_src_addr & page_mask}; |
| 243 | 236 | ||
| 244 | auto& memory = system.Memory(); | ||
| 245 | |||
| 246 | while (remaining_size > 0) { | 237 | while (remaining_size > 0) { |
| 247 | const std::size_t copy_amount{ | 238 | const std::size_t copy_amount{ |
| 248 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | 239 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; |
| 249 | const u8* page_pointer = page_table.pointers[page_index]; | 240 | |
| 250 | if (page_pointer) { | 241 | if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { |
| 251 | const VAddr src_addr{page_table.backing_addr[page_index] + page_offset}; | 242 | const auto src_addr{*page_addr + page_offset}; |
| 252 | memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); | 243 | system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); |
| 253 | } else { | 244 | } else { |
| 254 | std::memset(dest_buffer, 0, copy_amount); | 245 | std::memset(dest_buffer, 0, copy_amount); |
| 255 | } | 246 | } |
| 247 | |||
| 256 | page_index++; | 248 | page_index++; |
| 257 | page_offset = 0; | 249 | page_offset = 0; |
| 258 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | 250 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; |
| @@ -260,23 +252,23 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, | |||
| 260 | } | 252 | } |
| 261 | } | 253 | } |
| 262 | 254 | ||
| 263 | void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, | 255 | void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) { |
| 264 | const std::size_t size) { | ||
| 265 | std::size_t remaining_size{size}; | 256 | std::size_t remaining_size{size}; |
| 266 | std::size_t page_index{gpu_dest_addr >> page_bits}; | 257 | std::size_t page_index{gpu_dest_addr >> page_bits}; |
| 267 | std::size_t page_offset{gpu_dest_addr & page_mask}; | 258 | std::size_t page_offset{gpu_dest_addr & page_mask}; |
| 268 | 259 | ||
| 269 | auto& memory = system.Memory(); | ||
| 270 | |||
| 271 | while (remaining_size > 0) { | 260 | while (remaining_size > 0) { |
| 272 | const std::size_t copy_amount{ | 261 | const std::size_t copy_amount{ |
| 273 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | 262 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; |
| 274 | 263 | ||
| 275 | const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset}; | 264 | if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { |
| 276 | // Invalidate must happen on the rasterizer interface, such that memory is always | 265 | const auto dest_addr{*page_addr + page_offset}; |
| 277 | // synchronous when it is written (even when in asynchronous GPU mode). | 266 | |
| 278 | rasterizer.InvalidateRegion(dest_addr, copy_amount); | 267 | // Invalidate must happen on the rasterizer interface, such that memory is always |
| 279 | memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); | 268 | // synchronous when it is written (even when in asynchronous GPU mode). |
| 269 | rasterizer.InvalidateRegion(dest_addr, copy_amount); | ||
| 270 | system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); | ||
| 271 | } | ||
| 280 | 272 | ||
| 281 | page_index++; | 273 | page_index++; |
| 282 | page_offset = 0; | 274 | page_offset = 0; |
| @@ -286,21 +278,20 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, | |||
| 286 | } | 278 | } |
| 287 | 279 | ||
| 288 | void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, | 280 | void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, |
| 289 | const std::size_t size) { | 281 | std::size_t size) { |
| 290 | std::size_t remaining_size{size}; | 282 | std::size_t remaining_size{size}; |
| 291 | std::size_t page_index{gpu_dest_addr >> page_bits}; | 283 | std::size_t page_index{gpu_dest_addr >> page_bits}; |
| 292 | std::size_t page_offset{gpu_dest_addr & page_mask}; | 284 | std::size_t page_offset{gpu_dest_addr & page_mask}; |
| 293 | 285 | ||
| 294 | auto& memory = system.Memory(); | ||
| 295 | |||
| 296 | while (remaining_size > 0) { | 286 | while (remaining_size > 0) { |
| 297 | const std::size_t copy_amount{ | 287 | const std::size_t copy_amount{ |
| 298 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | 288 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; |
| 299 | u8* page_pointer = page_table.pointers[page_index]; | 289 | |
| 300 | if (page_pointer) { | 290 | if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) { |
| 301 | const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset}; | 291 | const auto dest_addr{*page_addr + page_offset}; |
| 302 | memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); | 292 | system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); |
| 303 | } | 293 | } |
| 294 | |||
| 304 | page_index++; | 295 | page_index++; |
| 305 | page_offset = 0; | 296 | page_offset = 0; |
| 306 | src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; | 297 | src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; |
| @@ -308,273 +299,26 @@ void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buf | |||
| 308 | } | 299 | } |
| 309 | } | 300 | } |
| 310 | 301 | ||
| 311 | void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, | 302 | void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) { |
| 312 | const std::size_t size) { | ||
| 313 | std::vector<u8> tmp_buffer(size); | 303 | std::vector<u8> tmp_buffer(size); |
| 314 | ReadBlock(gpu_src_addr, tmp_buffer.data(), size); | 304 | ReadBlock(gpu_src_addr, tmp_buffer.data(), size); |
| 315 | WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); | 305 | WriteBlock(gpu_dest_addr, tmp_buffer.data(), size); |
| 316 | } | 306 | } |
| 317 | 307 | ||
| 318 | void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, | 308 | void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, |
| 319 | const std::size_t size) { | 309 | std::size_t size) { |
| 320 | std::vector<u8> tmp_buffer(size); | 310 | std::vector<u8> tmp_buffer(size); |
| 321 | ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size); | 311 | ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size); |
| 322 | WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size); | 312 | WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size); |
| 323 | } | 313 | } |
| 324 | 314 | ||
| 325 | bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { | 315 | bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { |
| 326 | const VAddr addr = page_table.backing_addr[gpu_addr >> page_bits]; | 316 | const auto cpu_addr{GpuToCpuAddress(gpu_addr)}; |
| 327 | const std::size_t page = (addr & Core::Memory::PAGE_MASK) + size; | 317 | if (!cpu_addr) { |
| 328 | return page <= Core::Memory::PAGE_SIZE; | ||
| 329 | } | ||
| 330 | |||
| 331 | void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, | ||
| 332 | VAddr backing_addr) { | ||
| 333 | LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size, | ||
| 334 | (base + size) * page_size); | ||
| 335 | |||
| 336 | const VAddr end{base + size}; | ||
| 337 | ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", | ||
| 338 | base + page_table.pointers.size()); | ||
| 339 | |||
| 340 | if (memory == nullptr) { | ||
| 341 | while (base != end) { | ||
| 342 | page_table.pointers[base] = nullptr; | ||
| 343 | page_table.backing_addr[base] = 0; | ||
| 344 | |||
| 345 | base += 1; | ||
| 346 | } | ||
| 347 | } else { | ||
| 348 | while (base != end) { | ||
| 349 | page_table.pointers[base] = memory; | ||
| 350 | page_table.backing_addr[base] = backing_addr; | ||
| 351 | |||
| 352 | base += 1; | ||
| 353 | memory += page_size; | ||
| 354 | backing_addr += page_size; | ||
| 355 | } | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | void MemoryManager::MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr) { | ||
| 360 | ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size); | ||
| 361 | ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base); | ||
| 362 | MapPages(base / page_size, size / page_size, target, Common::PageType::Memory, backing_addr); | ||
| 363 | } | ||
| 364 | |||
| 365 | void MemoryManager::UnmapRegion(GPUVAddr base, u64 size) { | ||
| 366 | ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size); | ||
| 367 | ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base); | ||
| 368 | MapPages(base / page_size, size / page_size, nullptr, Common::PageType::Unmapped); | ||
| 369 | } | ||
| 370 | |||
| 371 | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | ||
| 372 | ASSERT(base + size == next.base); | ||
| 373 | if (type != next.type) { | ||
| 374 | return {}; | ||
| 375 | } | ||
| 376 | if (type == VirtualMemoryArea::Type::Allocated && (offset + size != next.offset)) { | ||
| 377 | return {}; | ||
| 378 | } | ||
| 379 | if (type == VirtualMemoryArea::Type::Mapped && backing_memory + size != next.backing_memory) { | ||
| 380 | return {}; | ||
| 381 | } | ||
| 382 | return true; | ||
| 383 | } | ||
| 384 | |||
| 385 | MemoryManager::VMAHandle MemoryManager::FindVMA(GPUVAddr target) const { | ||
| 386 | if (target >= address_space_end) { | ||
| 387 | return vma_map.end(); | ||
| 388 | } else { | ||
| 389 | return std::prev(vma_map.upper_bound(target)); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | MemoryManager::VMAIter MemoryManager::Allocate(VMAIter vma_handle) { | ||
| 394 | VirtualMemoryArea& vma{vma_handle->second}; | ||
| 395 | |||
| 396 | vma.type = VirtualMemoryArea::Type::Allocated; | ||
| 397 | vma.backing_addr = 0; | ||
| 398 | vma.backing_memory = {}; | ||
| 399 | UpdatePageTableForVMA(vma); | ||
| 400 | |||
| 401 | return MergeAdjacent(vma_handle); | ||
| 402 | } | ||
| 403 | |||
| 404 | MemoryManager::VMAHandle MemoryManager::AllocateMemory(GPUVAddr target, std::size_t offset, | ||
| 405 | u64 size) { | ||
| 406 | |||
| 407 | // This is the appropriately sized VMA that will turn into our allocation. | ||
| 408 | VMAIter vma_handle{CarveVMA(target, size)}; | ||
| 409 | VirtualMemoryArea& vma{vma_handle->second}; | ||
| 410 | |||
| 411 | ASSERT(vma.size == size); | ||
| 412 | |||
| 413 | vma.offset = offset; | ||
| 414 | |||
| 415 | return Allocate(vma_handle); | ||
| 416 | } | ||
| 417 | |||
| 418 | MemoryManager::VMAHandle MemoryManager::MapBackingMemory(GPUVAddr target, u8* memory, u64 size, | ||
| 419 | VAddr backing_addr) { | ||
| 420 | // This is the appropriately sized VMA that will turn into our allocation. | ||
| 421 | VMAIter vma_handle{CarveVMA(target, size)}; | ||
| 422 | VirtualMemoryArea& vma{vma_handle->second}; | ||
| 423 | |||
| 424 | ASSERT(vma.size == size); | ||
| 425 | |||
| 426 | vma.type = VirtualMemoryArea::Type::Mapped; | ||
| 427 | vma.backing_memory = memory; | ||
| 428 | vma.backing_addr = backing_addr; | ||
| 429 | UpdatePageTableForVMA(vma); | ||
| 430 | |||
| 431 | return MergeAdjacent(vma_handle); | ||
| 432 | } | ||
| 433 | |||
| 434 | void MemoryManager::UnmapRange(GPUVAddr target, u64 size) { | ||
| 435 | VMAIter vma{CarveVMARange(target, size)}; | ||
| 436 | const VAddr target_end{target + size}; | ||
| 437 | const VMAIter end{vma_map.end()}; | ||
| 438 | |||
| 439 | // The comparison against the end of the range must be done using addresses since VMAs can be | ||
| 440 | // merged during this process, causing invalidation of the iterators. | ||
| 441 | while (vma != end && vma->second.base < target_end) { | ||
| 442 | // Unmapped ranges return to allocated state and can be reused | ||
| 443 | // This behavior is used by Super Mario Odyssey, Sonic Forces, and likely other games | ||
| 444 | vma = std::next(Allocate(vma)); | ||
| 445 | } | ||
| 446 | |||
| 447 | ASSERT(FindVMA(target)->second.size >= size); | ||
| 448 | } | ||
| 449 | |||
| 450 | MemoryManager::VMAIter MemoryManager::StripIterConstness(const VMAHandle& iter) { | ||
| 451 | // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given | ||
| 452 | // non-const access to its container. | ||
| 453 | return vma_map.erase(iter, iter); // Erases an empty range of elements | ||
| 454 | } | ||
| 455 | |||
| 456 | MemoryManager::VMAIter MemoryManager::CarveVMA(GPUVAddr base, u64 size) { | ||
| 457 | ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size); | ||
| 458 | ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: 0x{:016X}", base); | ||
| 459 | |||
| 460 | VMAIter vma_handle{StripIterConstness(FindVMA(base))}; | ||
| 461 | if (vma_handle == vma_map.end()) { | ||
| 462 | // Target address is outside the managed range | ||
| 463 | return {}; | ||
| 464 | } | ||
| 465 | |||
| 466 | const VirtualMemoryArea& vma{vma_handle->second}; | ||
| 467 | if (vma.type == VirtualMemoryArea::Type::Mapped) { | ||
| 468 | // Region is already allocated | ||
| 469 | return vma_handle; | ||
| 470 | } | ||
| 471 | |||
| 472 | const VAddr start_in_vma{base - vma.base}; | ||
| 473 | const VAddr end_in_vma{start_in_vma + size}; | ||
| 474 | |||
| 475 | ASSERT_MSG(end_in_vma <= vma.size, "region size 0x{:016X} is less than required size 0x{:016X}", | ||
| 476 | vma.size, end_in_vma); | ||
| 477 | |||
| 478 | if (end_in_vma < vma.size) { | ||
| 479 | // Split VMA at the end of the allocated region | ||
| 480 | SplitVMA(vma_handle, end_in_vma); | ||
| 481 | } | ||
| 482 | if (start_in_vma != 0) { | ||
| 483 | // Split VMA at the start of the allocated region | ||
| 484 | vma_handle = SplitVMA(vma_handle, start_in_vma); | ||
| 485 | } | ||
| 486 | |||
| 487 | return vma_handle; | ||
| 488 | } | ||
| 489 | |||
| 490 | MemoryManager::VMAIter MemoryManager::CarveVMARange(GPUVAddr target, u64 size) { | ||
| 491 | ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size); | ||
| 492 | ASSERT_MSG((target & page_mask) == 0, "non-page aligned base: 0x{:016X}", target); | ||
| 493 | |||
| 494 | const VAddr target_end{target + size}; | ||
| 495 | ASSERT(target_end >= target); | ||
| 496 | ASSERT(size > 0); | ||
| 497 | |||
| 498 | VMAIter begin_vma{StripIterConstness(FindVMA(target))}; | ||
| 499 | const VMAIter i_end{vma_map.lower_bound(target_end)}; | ||
| 500 | if (std::any_of(begin_vma, i_end, [](const auto& entry) { | ||
| 501 | return entry.second.type == VirtualMemoryArea::Type::Unmapped; | ||
| 502 | })) { | ||
| 503 | return {}; | 318 | return {}; |
| 504 | } | 319 | } |
| 505 | 320 | const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size}; | |
| 506 | if (target != begin_vma->second.base) { | 321 | return page <= Core::Memory::PAGE_SIZE; |
| 507 | begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base); | ||
| 508 | } | ||
| 509 | |||
| 510 | VMAIter end_vma{StripIterConstness(FindVMA(target_end))}; | ||
| 511 | if (end_vma != vma_map.end() && target_end != end_vma->second.base) { | ||
| 512 | end_vma = SplitVMA(end_vma, target_end - end_vma->second.base); | ||
| 513 | } | ||
| 514 | |||
| 515 | return begin_vma; | ||
| 516 | } | ||
| 517 | |||
| 518 | MemoryManager::VMAIter MemoryManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) { | ||
| 519 | VirtualMemoryArea& old_vma{vma_handle->second}; | ||
| 520 | VirtualMemoryArea new_vma{old_vma}; // Make a copy of the VMA | ||
| 521 | |||
| 522 | // For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably | ||
| 523 | // a bug. This restriction might be removed later. | ||
| 524 | ASSERT(offset_in_vma < old_vma.size); | ||
| 525 | ASSERT(offset_in_vma > 0); | ||
| 526 | |||
| 527 | old_vma.size = offset_in_vma; | ||
| 528 | new_vma.base += offset_in_vma; | ||
| 529 | new_vma.size -= offset_in_vma; | ||
| 530 | |||
| 531 | switch (new_vma.type) { | ||
| 532 | case VirtualMemoryArea::Type::Unmapped: | ||
| 533 | break; | ||
| 534 | case VirtualMemoryArea::Type::Allocated: | ||
| 535 | new_vma.offset += offset_in_vma; | ||
| 536 | break; | ||
| 537 | case VirtualMemoryArea::Type::Mapped: | ||
| 538 | new_vma.backing_memory += offset_in_vma; | ||
| 539 | break; | ||
| 540 | } | ||
| 541 | |||
| 542 | ASSERT(old_vma.CanBeMergedWith(new_vma)); | ||
| 543 | |||
| 544 | return vma_map.emplace_hint(std::next(vma_handle), new_vma.base, new_vma); | ||
| 545 | } | ||
| 546 | |||
| 547 | MemoryManager::VMAIter MemoryManager::MergeAdjacent(VMAIter iter) { | ||
| 548 | const VMAIter next_vma{std::next(iter)}; | ||
| 549 | if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) { | ||
| 550 | iter->second.size += next_vma->second.size; | ||
| 551 | vma_map.erase(next_vma); | ||
| 552 | } | ||
| 553 | |||
| 554 | if (iter != vma_map.begin()) { | ||
| 555 | VMAIter prev_vma{std::prev(iter)}; | ||
| 556 | if (prev_vma->second.CanBeMergedWith(iter->second)) { | ||
| 557 | prev_vma->second.size += iter->second.size; | ||
| 558 | vma_map.erase(iter); | ||
| 559 | iter = prev_vma; | ||
| 560 | } | ||
| 561 | } | ||
| 562 | |||
| 563 | return iter; | ||
| 564 | } | ||
| 565 | |||
| 566 | void MemoryManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { | ||
| 567 | switch (vma.type) { | ||
| 568 | case VirtualMemoryArea::Type::Unmapped: | ||
| 569 | UnmapRegion(vma.base, vma.size); | ||
| 570 | break; | ||
| 571 | case VirtualMemoryArea::Type::Allocated: | ||
| 572 | MapMemoryRegion(vma.base, vma.size, nullptr, vma.backing_addr); | ||
| 573 | break; | ||
| 574 | case VirtualMemoryArea::Type::Mapped: | ||
| 575 | MapMemoryRegion(vma.base, vma.size, vma.backing_memory, vma.backing_addr); | ||
| 576 | break; | ||
| 577 | } | ||
| 578 | } | 322 | } |
| 579 | 323 | ||
| 580 | } // namespace Tegra | 324 | } // namespace Tegra |
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 87658e87a..681bd9588 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -6,9 +6,9 @@ | |||
| 6 | 6 | ||
| 7 | #include <map> | 7 | #include <map> |
| 8 | #include <optional> | 8 | #include <optional> |
| 9 | #include <vector> | ||
| 9 | 10 | ||
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 11 | #include "common/page_table.h" | ||
| 12 | 12 | ||
| 13 | namespace VideoCore { | 13 | namespace VideoCore { |
| 14 | class RasterizerInterface; | 14 | class RasterizerInterface; |
| @@ -20,45 +20,57 @@ class System; | |||
| 20 | 20 | ||
| 21 | namespace Tegra { | 21 | namespace Tegra { |
| 22 | 22 | ||
| 23 | /** | 23 | class PageEntry final { |
| 24 | * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space | 24 | public: |
| 25 | * with homogeneous attributes across its extents. In this particular implementation each VMA is | 25 | enum class State : u32 { |
| 26 | * also backed by a single host memory allocation. | 26 | Unmapped = static_cast<u32>(-1), |
| 27 | */ | 27 | Allocated = static_cast<u32>(-2), |
| 28 | struct VirtualMemoryArea { | ||
| 29 | enum class Type : u8 { | ||
| 30 | Unmapped, | ||
| 31 | Allocated, | ||
| 32 | Mapped, | ||
| 33 | }; | 28 | }; |
| 34 | 29 | ||
| 35 | /// Virtual base address of the region. | 30 | constexpr PageEntry() = default; |
| 36 | GPUVAddr base{}; | 31 | constexpr PageEntry(State state) : state{state} {} |
| 37 | /// Size of the region. | 32 | constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {} |
| 38 | u64 size{}; | 33 | |
| 39 | /// Memory area mapping type. | 34 | constexpr bool IsUnmapped() const { |
| 40 | Type type{Type::Unmapped}; | 35 | return state == State::Unmapped; |
| 41 | /// CPU memory mapped address corresponding to this memory area. | 36 | } |
| 42 | VAddr backing_addr{}; | 37 | |
| 43 | /// Offset into the backing_memory the mapping starts from. | 38 | constexpr bool IsAllocated() const { |
| 44 | std::size_t offset{}; | 39 | return state == State::Allocated; |
| 45 | /// Pointer backing this VMA. | 40 | } |
| 46 | u8* backing_memory{}; | 41 | |
| 47 | 42 | constexpr bool IsValid() const { | |
| 48 | /// Tests if this area can be merged to the right with `next`. | 43 | return !IsUnmapped() && !IsAllocated(); |
| 49 | bool CanBeMergedWith(const VirtualMemoryArea& next) const; | 44 | } |
| 45 | |||
| 46 | constexpr VAddr ToAddress() const { | ||
| 47 | if (!IsValid()) { | ||
| 48 | return {}; | ||
| 49 | } | ||
| 50 | |||
| 51 | return static_cast<VAddr>(state) << ShiftBits; | ||
| 52 | } | ||
| 53 | |||
| 54 | constexpr PageEntry operator+(u64 offset) { | ||
| 55 | // If this is a reserved value, offsets do not apply | ||
| 56 | if (!IsValid()) { | ||
| 57 | return *this; | ||
| 58 | } | ||
| 59 | return PageEntry{(static_cast<VAddr>(state) << ShiftBits) + offset}; | ||
| 60 | } | ||
| 61 | |||
| 62 | private: | ||
| 63 | static constexpr std::size_t ShiftBits{12}; | ||
| 64 | |||
| 65 | State state{State::Unmapped}; | ||
| 50 | }; | 66 | }; |
| 67 | static_assert(sizeof(PageEntry) == 4, "PageEntry is too large"); | ||
| 51 | 68 | ||
| 52 | class MemoryManager final { | 69 | class MemoryManager final { |
| 53 | public: | 70 | public: |
| 54 | explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); | 71 | explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); |
| 55 | ~MemoryManager(); | 72 | ~MemoryManager(); |
| 56 | 73 | ||
| 57 | GPUVAddr AllocateSpace(u64 size, u64 align); | ||
| 58 | GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align); | ||
| 59 | GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); | ||
| 60 | GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size); | ||
| 61 | GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size); | ||
| 62 | std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; | 74 | std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const; |
| 63 | 75 | ||
| 64 | template <typename T> | 76 | template <typename T> |
| @@ -70,9 +82,6 @@ public: | |||
| 70 | u8* GetPointer(GPUVAddr addr); | 82 | u8* GetPointer(GPUVAddr addr); |
| 71 | const u8* GetPointer(GPUVAddr addr) const; | 83 | const u8* GetPointer(GPUVAddr addr) const; |
| 72 | 84 | ||
| 73 | /// Returns true if the block is continuous in host memory, false otherwise | ||
| 74 | bool IsBlockContinuous(GPUVAddr start, std::size_t size) const; | ||
| 75 | |||
| 76 | /** | 85 | /** |
| 77 | * ReadBlock and WriteBlock are full read and write operations over virtual | 86 | * ReadBlock and WriteBlock are full read and write operations over virtual |
| 78 | * GPU Memory. It's important to use these when GPU memory may not be continuous | 87 | * GPU Memory. It's important to use these when GPU memory may not be continuous |
| @@ -98,92 +107,43 @@ public: | |||
| 98 | void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); | 107 | void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size); |
| 99 | 108 | ||
| 100 | /** | 109 | /** |
| 101 | * IsGranularRange checks if a gpu region can be simply read with a pointer | 110 | * IsGranularRange checks if a gpu region can be simply read with a pointer. |
| 102 | */ | 111 | */ |
| 103 | bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size); | 112 | bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size); |
| 104 | 113 | ||
| 105 | private: | 114 | GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size); |
| 106 | using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; | 115 | GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align); |
| 107 | using VMAHandle = VMAMap::const_iterator; | 116 | std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size); |
| 108 | using VMAIter = VMAMap::iterator; | 117 | GPUVAddr Allocate(std::size_t size, std::size_t align); |
| 109 | 118 | void Unmap(GPUVAddr gpu_addr, std::size_t size); | |
| 110 | bool IsAddressValid(GPUVAddr addr) const; | ||
| 111 | void MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, | ||
| 112 | VAddr backing_addr = 0); | ||
| 113 | void MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr); | ||
| 114 | void UnmapRegion(GPUVAddr base, u64 size); | ||
| 115 | |||
| 116 | /// Finds the VMA in which the given address is included in, or `vma_map.end()`. | ||
| 117 | VMAHandle FindVMA(GPUVAddr target) const; | ||
| 118 | |||
| 119 | VMAHandle AllocateMemory(GPUVAddr target, std::size_t offset, u64 size); | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Maps an unmanaged host memory pointer at a given address. | ||
| 123 | * | ||
| 124 | * @param target The guest address to start the mapping at. | ||
| 125 | * @param memory The memory to be mapped. | ||
| 126 | * @param size Size of the mapping in bytes. | ||
| 127 | * @param backing_addr The base address of the range to back this mapping. | ||
| 128 | */ | ||
| 129 | VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr); | ||
| 130 | |||
| 131 | /// Unmaps a range of addresses, splitting VMAs as necessary. | ||
| 132 | void UnmapRange(GPUVAddr target, u64 size); | ||
| 133 | |||
| 134 | /// Converts a VMAHandle to a mutable VMAIter. | ||
| 135 | VMAIter StripIterConstness(const VMAHandle& iter); | ||
| 136 | |||
| 137 | /// Marks as the specified VMA as allocated. | ||
| 138 | VMAIter Allocate(VMAIter vma); | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing | ||
| 142 | * the appropriate error checking. | ||
| 143 | */ | ||
| 144 | VMAIter CarveVMA(GPUVAddr base, u64 size); | ||
| 145 | |||
| 146 | /** | ||
| 147 | * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each | ||
| 148 | * end of the range. | ||
| 149 | */ | ||
| 150 | VMAIter CarveVMARange(GPUVAddr base, u64 size); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Splits a VMA in two, at the specified offset. | ||
| 154 | * @returns the right side of the split, with the original iterator becoming the left side. | ||
| 155 | */ | ||
| 156 | VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma); | ||
| 157 | 119 | ||
| 158 | /** | 120 | private: |
| 159 | * Checks for and merges the specified VMA with adjacent ones if possible. | 121 | PageEntry GetPageEntry(GPUVAddr gpu_addr) const; |
| 160 | * @returns the merged VMA or the original if no merging was possible. | 122 | void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); |
| 161 | */ | 123 | GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size); |
| 162 | VMAIter MergeAdjacent(VMAIter vma); | 124 | std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const; |
| 163 | 125 | ||
| 164 | /// Updates the pages corresponding to this VMA so they match the VMA's attributes. | 126 | void TryLockPage(PageEntry page_entry, std::size_t size); |
| 165 | void UpdatePageTableForVMA(const VirtualMemoryArea& vma); | 127 | void TryUnlockPage(PageEntry page_entry, std::size_t size); |
| 166 | 128 | ||
| 167 | /// Finds a free (unmapped region) of the specified size starting at the specified address. | 129 | static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) { |
| 168 | GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size) const; | 130 | return (gpu_addr >> page_bits) & page_table_mask; |
| 131 | } | ||
| 169 | 132 | ||
| 170 | private: | 133 | static constexpr u64 address_space_size = 1ULL << 40; |
| 134 | static constexpr u64 address_space_start = 1ULL << 32; | ||
| 171 | static constexpr u64 page_bits{16}; | 135 | static constexpr u64 page_bits{16}; |
| 172 | static constexpr u64 page_size{1 << page_bits}; | 136 | static constexpr u64 page_size{1 << page_bits}; |
| 173 | static constexpr u64 page_mask{page_size - 1}; | 137 | static constexpr u64 page_mask{page_size - 1}; |
| 138 | static constexpr u64 page_table_bits{24}; | ||
| 139 | static constexpr u64 page_table_size{1 << page_table_bits}; | ||
| 140 | static constexpr u64 page_table_mask{page_table_size - 1}; | ||
| 174 | 141 | ||
| 175 | /// Address space in bits, according to Tegra X1 TRM | 142 | Core::System& system; |
| 176 | static constexpr u32 address_space_width{40}; | ||
| 177 | /// Start address for mapping, this is fairly arbitrary but must be non-zero. | ||
| 178 | static constexpr GPUVAddr address_space_base{0x100000}; | ||
| 179 | /// End of address space, based on address space in bits. | ||
| 180 | static constexpr GPUVAddr address_space_end{1ULL << address_space_width}; | ||
| 181 | 143 | ||
| 182 | Common::PageTable page_table; | ||
| 183 | VMAMap vma_map; | ||
| 184 | VideoCore::RasterizerInterface& rasterizer; | 144 | VideoCore::RasterizerInterface& rasterizer; |
| 185 | 145 | ||
| 186 | Core::System& system; | 146 | std::vector<PageEntry> page_table; |
| 187 | }; | 147 | }; |
| 188 | 148 | ||
| 189 | } // namespace Tegra | 149 | } // namespace Tegra |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 03e82c599..cb284db77 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -178,16 +178,11 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind | |||
| 178 | 178 | ||
| 179 | if (device.UseAsynchronousShaders()) { | 179 | if (device.UseAsynchronousShaders()) { |
| 180 | // Max worker threads we should allow | 180 | // Max worker threads we should allow |
| 181 | constexpr auto MAX_THREADS = 2u; | 181 | constexpr u32 MAX_THREADS = 4; |
| 182 | // Amount of threads we should reserve for other parts of yuzu | 182 | // Deduce how many threads we can use |
| 183 | constexpr auto RESERVED_THREADS = 6u; | 183 | const u32 threads_used = std::thread::hardware_concurrency() / 4; |
| 184 | // Get the amount of threads we can use(this can return zero) | ||
| 185 | const auto cpu_thread_count = | ||
| 186 | std::max(RESERVED_THREADS, std::thread::hardware_concurrency()); | ||
| 187 | // Deduce how many "extra" threads we have to use. | ||
| 188 | const auto max_threads_unused = cpu_thread_count - RESERVED_THREADS; | ||
| 189 | // Always allow at least 1 thread regardless of our settings | 184 | // Always allow at least 1 thread regardless of our settings |
| 190 | const auto max_worker_count = std::max(1u, max_threads_unused); | 185 | const auto max_worker_count = std::max(1U, threads_used); |
| 191 | // Don't use more than MAX_THREADS | 186 | // Don't use more than MAX_THREADS |
| 192 | const auto worker_count = std::min(max_worker_count, MAX_THREADS); | 187 | const auto worker_count = std::min(max_worker_count, MAX_THREADS); |
| 193 | async_shaders.AllocateWorkers(worker_count); | 188 | async_shaders.AllocateWorkers(worker_count); |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index be71e1733..eb49a36bf 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -403,7 +403,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 403 | } | 403 | } |
| 404 | }; | 404 | }; |
| 405 | 405 | ||
| 406 | const auto num_workers{static_cast<std::size_t>(std::thread::hardware_concurrency() + 1ULL)}; | 406 | const std::size_t num_workers{std::max(1U, std::thread::hardware_concurrency())}; |
| 407 | const std::size_t bucket_size{transferable->size() / num_workers}; | 407 | const std::size_t bucket_size{transferable->size() / num_workers}; |
| 408 | std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers); | 408 | std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> contexts(num_workers); |
| 409 | std::vector<std::thread> threads(num_workers); | 409 | std::vector<std::thread> threads(num_workers); |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index ce53e5a6b..a551e3de8 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -696,6 +696,7 @@ void VKBlitScreen::CreateFramebuffers() { | |||
| 696 | .flags = 0, | 696 | .flags = 0, |
| 697 | .renderPass = *renderpass, | 697 | .renderPass = *renderpass, |
| 698 | .attachmentCount = 1, | 698 | .attachmentCount = 1, |
| 699 | .pAttachments = nullptr, | ||
| 699 | .width = size.width, | 700 | .width = size.width, |
| 700 | .height = size.height, | 701 | .height = size.height, |
| 701 | .layers = 1, | 702 | .layers = 1, |
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 6245e0d78..0c03e4d83 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -771,8 +771,9 @@ std::vector<VkDeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const | |||
| 771 | .pNext = nullptr, | 771 | .pNext = nullptr, |
| 772 | .flags = 0, | 772 | .flags = 0, |
| 773 | .queueFamilyIndex = queue_family, | 773 | .queueFamilyIndex = queue_family, |
| 774 | .queueCount = 1, | ||
| 775 | .pQueuePriorities = nullptr, | ||
| 774 | }); | 776 | }); |
| 775 | ci.queueCount = 1; | ||
| 776 | ci.pQueuePriorities = &QUEUE_PRIORITY; | 777 | ci.pQueuePriorities = &QUEUE_PRIORITY; |
| 777 | } | 778 | } |
| 778 | 779 | ||
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 42b3a744c..418c62bc4 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -261,8 +261,13 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach | |||
| 261 | } | 261 | } |
| 262 | 262 | ||
| 263 | const Specialization specialization{ | 263 | const Specialization specialization{ |
| 264 | .base_binding = 0, | ||
| 264 | .workgroup_size = key.workgroup_size, | 265 | .workgroup_size = key.workgroup_size, |
| 265 | .shared_memory_size = key.shared_memory_size, | 266 | .shared_memory_size = key.shared_memory_size, |
| 267 | .point_size = std::nullopt, | ||
| 268 | .enabled_attributes = {}, | ||
| 269 | .attribute_types = {}, | ||
| 270 | .ndc_minus_one_to_one = false, | ||
| 266 | }; | 271 | }; |
| 267 | const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute, | 272 | const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute, |
| 268 | shader->GetRegistry(), specialization), | 273 | shader->GetRegistry(), specialization), |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 2ed2004f0..7500e8244 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -815,8 +815,13 @@ bool RasterizerVulkan::WalkAttachmentOverlaps(const CachedSurfaceView& attachmen | |||
| 815 | 815 | ||
| 816 | std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers( | 816 | std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers( |
| 817 | VkRenderPass renderpass) { | 817 | VkRenderPass renderpass) { |
| 818 | FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(), | 818 | FramebufferCacheKey key{ |
| 819 | std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()}; | 819 | .renderpass = renderpass, |
| 820 | .width = std::numeric_limits<u32>::max(), | ||
| 821 | .height = std::numeric_limits<u32>::max(), | ||
| 822 | .layers = std::numeric_limits<u32>::max(), | ||
| 823 | .views = {}, | ||
| 824 | }; | ||
| 820 | 825 | ||
| 821 | const auto try_push = [&key](const View& view) { | 826 | const auto try_push = [&key](const View& view) { |
| 822 | if (!view) { | 827 | if (!view) { |
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp index 2d5460776..b068888f9 100644 --- a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp | |||
| @@ -47,6 +47,7 @@ vk::Sampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) c | |||
| 47 | VkSamplerCustomBorderColorCreateInfoEXT border{ | 47 | VkSamplerCustomBorderColorCreateInfoEXT border{ |
| 48 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, | 48 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, |
| 49 | .pNext = nullptr, | 49 | .pNext = nullptr, |
| 50 | .customBorderColor = {}, | ||
| 50 | .format = VK_FORMAT_UNDEFINED, | 51 | .format = VK_FORMAT_UNDEFINED, |
| 51 | }; | 52 | }; |
| 52 | std::memcpy(&border.customBorderColor, color.data(), sizeof(color)); | 53 | std::memcpy(&border.customBorderColor, color.data(), sizeof(color)); |
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index c25e312b6..6bfd2abae 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp | |||
| @@ -156,6 +156,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, | |||
| 156 | .minImageCount = requested_image_count, | 156 | .minImageCount = requested_image_count, |
| 157 | .imageFormat = surface_format.format, | 157 | .imageFormat = surface_format.format, |
| 158 | .imageColorSpace = surface_format.colorSpace, | 158 | .imageColorSpace = surface_format.colorSpace, |
| 159 | .imageExtent = {}, | ||
| 159 | .imageArrayLayers = 1, | 160 | .imageArrayLayers = 1, |
| 160 | .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, | 161 | .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| 161 | .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, | 162 | .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| @@ -204,6 +205,7 @@ void VKSwapchain::CreateImageViews() { | |||
| 204 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 205 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| 205 | .pNext = nullptr, | 206 | .pNext = nullptr, |
| 206 | .flags = 0, | 207 | .flags = 0, |
| 208 | .image = {}, | ||
| 207 | .viewType = VK_IMAGE_VIEW_TYPE_2D, | 209 | .viewType = VK_IMAGE_VIEW_TYPE_2D, |
| 208 | .format = image_format, | 210 | .format = image_format, |
| 209 | .components = | 211 | .components = |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index d102e6d27..2c6f54101 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -138,6 +138,7 @@ VkImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceP | |||
| 138 | .flags = 0, | 138 | .flags = 0, |
| 139 | .imageType = SurfaceTargetToImage(params.target), | 139 | .imageType = SurfaceTargetToImage(params.target), |
| 140 | .format = format, | 140 | .format = format, |
| 141 | .extent = {}, | ||
| 141 | .mipLevels = params.num_levels, | 142 | .mipLevels = params.num_levels, |
| 142 | .arrayLayers = static_cast<u32>(params.GetNumLayers()), | 143 | .arrayLayers = static_cast<u32>(params.GetNumLayers()), |
| 143 | .samples = VK_SAMPLE_COUNT_1_BIT, | 144 | .samples = VK_SAMPLE_COUNT_1_BIT, |
| @@ -458,6 +459,7 @@ VkImageView CachedSurfaceView::GetAttachment() { | |||
| 458 | .pNext = nullptr, | 459 | .pNext = nullptr, |
| 459 | .flags = 0, | 460 | .flags = 0, |
| 460 | .image = surface.GetImageHandle(), | 461 | .image = surface.GetImageHandle(), |
| 462 | .viewType = VK_IMAGE_VIEW_TYPE_1D, | ||
| 461 | .format = surface.GetImage().GetFormat(), | 463 | .format = surface.GetImage().GetFormat(), |
| 462 | .components = | 464 | .components = |
| 463 | { | 465 | { |
| @@ -471,6 +473,8 @@ VkImageView CachedSurfaceView::GetAttachment() { | |||
| 471 | .aspectMask = aspect_mask, | 473 | .aspectMask = aspect_mask, |
| 472 | .baseMipLevel = base_level, | 474 | .baseMipLevel = base_level, |
| 473 | .levelCount = num_levels, | 475 | .levelCount = num_levels, |
| 476 | .baseArrayLayer = 0, | ||
| 477 | .layerCount = 0, | ||
| 474 | }, | 478 | }, |
| 475 | }; | 479 | }; |
| 476 | if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) { | 480 | if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) { |
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h index 71daac9d7..31885ef42 100644 --- a/src/video_core/renderer_vulkan/wrapper.h +++ b/src/video_core/renderer_vulkan/wrapper.h | |||
| @@ -756,8 +756,8 @@ public: | |||
| 756 | } | 756 | } |
| 757 | 757 | ||
| 758 | VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, | 758 | VkResult GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, |
| 759 | void* data, VkDeviceSize stride, VkQueryResultFlags flags) const | 759 | void* data, VkDeviceSize stride, |
| 760 | noexcept { | 760 | VkQueryResultFlags flags) const noexcept { |
| 761 | return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, | 761 | return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, |
| 762 | flags); | 762 | flags); |
| 763 | } | 763 | } |
| @@ -849,8 +849,8 @@ public: | |||
| 849 | dld->vkCmdBindPipeline(handle, bind_point, pipeline); | 849 | dld->vkCmdBindPipeline(handle, bind_point, pipeline); |
| 850 | } | 850 | } |
| 851 | 851 | ||
| 852 | void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) const | 852 | void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, |
| 853 | noexcept { | 853 | VkIndexType index_type) const noexcept { |
| 854 | dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type); | 854 | dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type); |
| 855 | } | 855 | } |
| 856 | 856 | ||
| @@ -863,8 +863,8 @@ public: | |||
| 863 | BindVertexBuffers(binding, 1, &buffer, &offset); | 863 | BindVertexBuffers(binding, 1, &buffer, &offset); |
| 864 | } | 864 | } |
| 865 | 865 | ||
| 866 | void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex, u32 first_instance) const | 866 | void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex, |
| 867 | noexcept { | 867 | u32 first_instance) const noexcept { |
| 868 | dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance); | 868 | dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance); |
| 869 | } | 869 | } |
| 870 | 870 | ||
| @@ -874,15 +874,15 @@ public: | |||
| 874 | first_instance); | 874 | first_instance); |
| 875 | } | 875 | } |
| 876 | 876 | ||
| 877 | void ClearAttachments(Span<VkClearAttachment> attachments, Span<VkClearRect> rects) const | 877 | void ClearAttachments(Span<VkClearAttachment> attachments, |
| 878 | noexcept { | 878 | Span<VkClearRect> rects) const noexcept { |
| 879 | dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), | 879 | dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), |
| 880 | rects.data()); | 880 | rects.data()); |
| 881 | } | 881 | } |
| 882 | 882 | ||
| 883 | void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, | 883 | void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, |
| 884 | VkImageLayout dst_layout, Span<VkImageBlit> regions, VkFilter filter) const | 884 | VkImageLayout dst_layout, Span<VkImageBlit> regions, |
| 885 | noexcept { | 885 | VkFilter filter) const noexcept { |
| 886 | dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), | 886 | dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), |
| 887 | regions.data(), filter); | 887 | regions.data(), filter); |
| 888 | } | 888 | } |
| @@ -907,8 +907,8 @@ public: | |||
| 907 | regions.data()); | 907 | regions.data()); |
| 908 | } | 908 | } |
| 909 | 909 | ||
| 910 | void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, Span<VkBufferCopy> regions) const | 910 | void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, |
| 911 | noexcept { | 911 | Span<VkBufferCopy> regions) const noexcept { |
| 912 | dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data()); | 912 | dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data()); |
| 913 | } | 913 | } |
| 914 | 914 | ||
| @@ -924,8 +924,8 @@ public: | |||
| 924 | regions.data()); | 924 | regions.data()); |
| 925 | } | 925 | } |
| 926 | 926 | ||
| 927 | void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size, u32 data) const | 927 | void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size, |
| 928 | noexcept { | 928 | u32 data) const noexcept { |
| 929 | dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data); | 929 | dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data); |
| 930 | } | 930 | } |
| 931 | 931 | ||
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index 8d86020f6..336397cdb 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp | |||
| @@ -187,24 +187,26 @@ std::optional<std::pair<BufferInfo, u64>> TrackLDC(const CFGRebuildState& state, | |||
| 187 | 187 | ||
| 188 | std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos, | 188 | std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos, |
| 189 | u64 ldc_tracked_register) { | 189 | u64 ldc_tracked_register) { |
| 190 | return TrackInstruction<u64>(state, pos, | 190 | return TrackInstruction<u64>( |
| 191 | [ldc_tracked_register](auto instr, const auto& opcode) { | 191 | state, pos, |
| 192 | return opcode.GetId() == OpCode::Id::SHL_IMM && | 192 | [ldc_tracked_register](auto instr, const auto& opcode) { |
| 193 | instr.gpr0.Value() == ldc_tracked_register; | 193 | return opcode.GetId() == OpCode::Id::SHL_IMM && |
| 194 | }, | 194 | instr.gpr0.Value() == ldc_tracked_register; |
| 195 | [](auto instr, const auto&) { return instr.gpr8.Value(); }); | 195 | }, |
| 196 | [](auto instr, const auto&) { return instr.gpr8.Value(); }); | ||
| 196 | } | 197 | } |
| 197 | 198 | ||
| 198 | std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos, | 199 | std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos, |
| 199 | u64 shl_tracked_register) { | 200 | u64 shl_tracked_register) { |
| 200 | return TrackInstruction<u32>(state, pos, | 201 | return TrackInstruction<u32>( |
| 201 | [shl_tracked_register](auto instr, const auto& opcode) { | 202 | state, pos, |
| 202 | return opcode.GetId() == OpCode::Id::IMNMX_IMM && | 203 | [shl_tracked_register](auto instr, const auto& opcode) { |
| 203 | instr.gpr0.Value() == shl_tracked_register; | 204 | return opcode.GetId() == OpCode::Id::IMNMX_IMM && |
| 204 | }, | 205 | instr.gpr0.Value() == shl_tracked_register; |
| 205 | [](auto instr, const auto&) { | 206 | }, |
| 206 | return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1); | 207 | [](auto instr, const auto&) { |
| 207 | }); | 208 | return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1); |
| 209 | }); | ||
| 208 | } | 210 | } |
| 209 | 211 | ||
| 210 | std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) { | 212 | std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) { |
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index 63adbc4a3..e4739394d 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp | |||
| @@ -471,9 +471,9 @@ std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackGlobalMemory(NodeBlock& | |||
| 471 | 471 | ||
| 472 | const auto [base_address, index, offset] = | 472 | const auto [base_address, index, offset] = |
| 473 | TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size())); | 473 | TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size())); |
| 474 | ASSERT_OR_EXECUTE_MSG(base_address != nullptr, | 474 | ASSERT_OR_EXECUTE_MSG( |
| 475 | { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); }, | 475 | base_address != nullptr, { return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); }, |
| 476 | "Global memory tracking failed"); | 476 | "Global memory tracking failed"); |
| 477 | 477 | ||
| 478 | bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset))); | 478 | bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset))); |
| 479 | 479 | ||
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index 9e5fe2374..e614a92df 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp | |||
| @@ -74,9 +74,9 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta | |||
| 74 | SurfaceParams params; | 74 | SurfaceParams params; |
| 75 | params.is_tiled = tic.IsTiled(); | 75 | params.is_tiled = tic.IsTiled(); |
| 76 | params.srgb_conversion = tic.IsSrgbConversionEnabled(); | 76 | params.srgb_conversion = tic.IsSrgbConversionEnabled(); |
| 77 | params.block_width = params.is_tiled ? tic.BlockWidth() : 0, | 77 | params.block_width = params.is_tiled ? tic.BlockWidth() : 0; |
| 78 | params.block_height = params.is_tiled ? tic.BlockHeight() : 0, | 78 | params.block_height = params.is_tiled ? tic.BlockHeight() : 0; |
| 79 | params.block_depth = params.is_tiled ? tic.BlockDepth() : 0, | 79 | params.block_depth = params.is_tiled ? tic.BlockDepth() : 0; |
| 80 | params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1; | 80 | params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1; |
| 81 | params.pixel_format = lookup_table.GetPixelFormat( | 81 | params.pixel_format = lookup_table.GetPixelFormat( |
| 82 | tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type); | 82 | tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type); |
| @@ -96,7 +96,6 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta | |||
| 96 | } | 96 | } |
| 97 | params.type = GetFormatType(params.pixel_format); | 97 | params.type = GetFormatType(params.pixel_format); |
| 98 | } | 98 | } |
| 99 | params.type = GetFormatType(params.pixel_format); | ||
| 100 | // TODO: on 1DBuffer we should use the tic info. | 99 | // TODO: on 1DBuffer we should use the tic info. |
| 101 | if (tic.IsBuffer()) { | 100 | if (tic.IsBuffer()) { |
| 102 | params.target = SurfaceTarget::TextureBuffer; | 101 | params.target = SurfaceTarget::TextureBuffer; |
| @@ -130,14 +129,13 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl | |||
| 130 | SurfaceParams params; | 129 | SurfaceParams params; |
| 131 | params.is_tiled = tic.IsTiled(); | 130 | params.is_tiled = tic.IsTiled(); |
| 132 | params.srgb_conversion = tic.IsSrgbConversionEnabled(); | 131 | params.srgb_conversion = tic.IsSrgbConversionEnabled(); |
| 133 | params.block_width = params.is_tiled ? tic.BlockWidth() : 0, | 132 | params.block_width = params.is_tiled ? tic.BlockWidth() : 0; |
| 134 | params.block_height = params.is_tiled ? tic.BlockHeight() : 0, | 133 | params.block_height = params.is_tiled ? tic.BlockHeight() : 0; |
| 135 | params.block_depth = params.is_tiled ? tic.BlockDepth() : 0, | 134 | params.block_depth = params.is_tiled ? tic.BlockDepth() : 0; |
| 136 | params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1; | 135 | params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1; |
| 137 | params.pixel_format = lookup_table.GetPixelFormat( | 136 | params.pixel_format = lookup_table.GetPixelFormat( |
| 138 | tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type); | 137 | tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type); |
| 139 | params.type = GetFormatType(params.pixel_format); | 138 | params.type = GetFormatType(params.pixel_format); |
| 140 | params.type = GetFormatType(params.pixel_format); | ||
| 141 | params.target = ImageTypeToSurfaceTarget(entry.type); | 139 | params.target = ImageTypeToSurfaceTarget(entry.type); |
| 142 | // TODO: on 1DBuffer we should use the tic info. | 140 | // TODO: on 1DBuffer we should use the tic info. |
| 143 | if (tic.IsBuffer()) { | 141 | if (tic.IsBuffer()) { |
| @@ -167,27 +165,30 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl | |||
| 167 | 165 | ||
| 168 | SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) { | 166 | SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) { |
| 169 | const auto& regs = system.GPU().Maxwell3D().regs; | 167 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 170 | SurfaceParams params; | ||
| 171 | params.is_tiled = regs.zeta.memory_layout.type == | ||
| 172 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; | ||
| 173 | params.srgb_conversion = false; | ||
| 174 | params.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U); | ||
| 175 | params.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U); | ||
| 176 | params.block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U); | ||
| 177 | params.tile_width_spacing = 1; | ||
| 178 | params.pixel_format = PixelFormatFromDepthFormat(regs.zeta.format); | ||
| 179 | params.type = GetFormatType(params.pixel_format); | ||
| 180 | params.width = regs.zeta_width; | ||
| 181 | params.height = regs.zeta_height; | ||
| 182 | params.pitch = 0; | ||
| 183 | params.num_levels = 1; | ||
| 184 | params.emulated_levels = 1; | ||
| 185 | 168 | ||
| 186 | const bool is_layered = regs.zeta_layers > 1 && params.block_depth == 0; | 169 | const auto block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U); |
| 187 | params.is_layered = is_layered; | 170 | const bool is_layered = regs.zeta_layers > 1 && block_depth == 0; |
| 188 | params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D; | 171 | const auto pixel_format = PixelFormatFromDepthFormat(regs.zeta.format); |
| 189 | params.depth = is_layered ? regs.zeta_layers.Value() : 1U; | 172 | |
| 190 | return params; | 173 | return { |
| 174 | .is_tiled = regs.zeta.memory_layout.type == | ||
| 175 | Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear, | ||
| 176 | .srgb_conversion = false, | ||
| 177 | .is_layered = is_layered, | ||
| 178 | .block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U), | ||
| 179 | .block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U), | ||
| 180 | .block_depth = block_depth, | ||
| 181 | .tile_width_spacing = 1, | ||
| 182 | .width = regs.zeta_width, | ||
| 183 | .height = regs.zeta_height, | ||
| 184 | .depth = is_layered ? regs.zeta_layers.Value() : 1U, | ||
| 185 | .pitch = 0, | ||
| 186 | .num_levels = 1, | ||
| 187 | .emulated_levels = 1, | ||
| 188 | .pixel_format = pixel_format, | ||
| 189 | .type = GetFormatType(pixel_format), | ||
| 190 | .target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D, | ||
| 191 | }; | ||
| 191 | } | 192 | } |
| 192 | 193 | ||
| 193 | SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) { | 194 | SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) { |
| @@ -233,24 +234,29 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz | |||
| 233 | 234 | ||
| 234 | SurfaceParams SurfaceParams::CreateForFermiCopySurface( | 235 | SurfaceParams SurfaceParams::CreateForFermiCopySurface( |
| 235 | const Tegra::Engines::Fermi2D::Regs::Surface& config) { | 236 | const Tegra::Engines::Fermi2D::Regs::Surface& config) { |
| 236 | SurfaceParams params{}; | 237 | const bool is_tiled = !config.linear; |
| 237 | params.is_tiled = !config.linear; | 238 | const auto pixel_format = PixelFormatFromRenderTargetFormat(config.format); |
| 238 | params.srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB || | 239 | |
| 239 | config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB; | 240 | SurfaceParams params{ |
| 240 | params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 5U) : 0, | 241 | .is_tiled = is_tiled, |
| 241 | params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 5U) : 0, | 242 | .srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB || |
| 242 | params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 5U) : 0, | 243 | config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB, |
| 243 | params.tile_width_spacing = 1; | 244 | .block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U, |
| 244 | params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); | 245 | .block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U, |
| 245 | params.type = GetFormatType(params.pixel_format); | 246 | .block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U, |
| 246 | params.width = config.width; | 247 | .tile_width_spacing = 1, |
| 247 | params.height = config.height; | 248 | .width = config.width, |
| 248 | params.pitch = config.pitch; | 249 | .height = config.height, |
| 249 | // TODO(Rodrigo): Try to guess texture arrays from parameters | 250 | .depth = 1, |
| 250 | params.target = SurfaceTarget::Texture2D; | 251 | .pitch = config.pitch, |
| 251 | params.depth = 1; | 252 | .num_levels = 1, |
| 252 | params.num_levels = 1; | 253 | .emulated_levels = 1, |
| 253 | params.emulated_levels = 1; | 254 | .pixel_format = pixel_format, |
| 255 | .type = GetFormatType(pixel_format), | ||
| 256 | // TODO(Rodrigo): Try to guess texture arrays from parameters | ||
| 257 | .target = SurfaceTarget::Texture2D, | ||
| 258 | }; | ||
| 259 | |||
| 254 | params.is_layered = params.IsLayered(); | 260 | params.is_layered = params.IsLayered(); |
| 255 | return params; | 261 | return params; |
| 256 | } | 262 | } |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 474ae620a..16d46a018 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -228,24 +228,30 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 | |||
| 228 | } | 228 | } |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, | 231 | void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, |
| 232 | u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, | 232 | u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { |
| 233 | u32 block_height_bit, u32 offset_x, u32 offset_y) { | 233 | const u32 stride = width * bytes_per_pixel; |
| 234 | const u32 block_height = 1U << block_height_bit; | 234 | const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; |
| 235 | for (u32 line = 0; line < subrect_height; ++line) { | 235 | const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height); |
| 236 | const u32 y2 = line + offset_y; | 236 | |
| 237 | const u32 gob_address_y = (y2 / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height + | 237 | const u32 block_height_mask = (1U << block_height) - 1; |
| 238 | ((y2 % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE; | 238 | const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height; |
| 239 | const auto& table = LEGACY_SWIZZLE_TABLE[y2 % GOB_SIZE_Y]; | 239 | |
| 240 | for (u32 x = 0; x < subrect_width; ++x) { | 240 | for (u32 line = 0; line < line_count; ++line) { |
| 241 | const u32 x2 = (x + offset_x) * bytes_per_pixel; | 241 | const u32 src_y = line + origin_y; |
| 242 | const u32 gob_address = gob_address_y + (x2 / GOB_SIZE_X) * GOB_SIZE * block_height; | 242 | const auto& table = LEGACY_SWIZZLE_TABLE[src_y % GOB_SIZE_Y]; |
| 243 | const u32 swizzled_offset = gob_address + table[x2 % GOB_SIZE_X]; | 243 | |
| 244 | const u32 unswizzled_offset = line * dest_pitch + x * bytes_per_pixel; | 244 | const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT; |
| 245 | u8* dest_line = unswizzled_data + unswizzled_offset; | 245 | const u32 src_offset_y = (block_y >> block_height) * block_size + |
| 246 | u8* source_addr = swizzled_data + swizzled_offset; | 246 | ((block_y & block_height_mask) << GOB_SIZE_SHIFT); |
| 247 | for (u32 column = 0; column < line_length_in; ++column) { | ||
| 248 | const u32 src_x = (column + origin_x) * bytes_per_pixel; | ||
| 249 | const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; | ||
| 250 | |||
| 251 | const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X]; | ||
| 252 | const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel; | ||
| 247 | 253 | ||
| 248 | std::memcpy(dest_line, source_addr, bytes_per_pixel); | 254 | std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel); |
| 249 | } | 255 | } |
| 250 | } | 256 | } |
| 251 | } | 257 | } |
| @@ -261,7 +267,7 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt | |||
| 261 | const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); | 267 | const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); |
| 262 | 268 | ||
| 263 | const u32 block_height_mask = (1U << block_height) - 1; | 269 | const u32 block_height_mask = (1U << block_height) - 1; |
| 264 | const u32 x_shift = Common::CountTrailingZeroes32(GOB_SIZE << (block_height + block_depth)); | 270 | const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth; |
| 265 | 271 | ||
| 266 | for (u32 line = 0; line < line_count; ++line) { | 272 | for (u32 line = 0; line < line_count; ++line) { |
| 267 | const auto& table = LEGACY_SWIZZLE_TABLE[line % GOB_SIZE_Y]; | 273 | const auto& table = LEGACY_SWIZZLE_TABLE[line % GOB_SIZE_Y]; |
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index d6fe35d37..01e156bc8 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h | |||
| @@ -48,9 +48,8 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 | |||
| 48 | u32 block_height_bit, u32 offset_x, u32 offset_y); | 48 | u32 block_height_bit, u32 offset_x, u32 offset_y); |
| 49 | 49 | ||
| 50 | /// Copies a tiled subrectangle into a linear surface. | 50 | /// Copies a tiled subrectangle into a linear surface. |
| 51 | void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width, | 51 | void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, |
| 52 | u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, | 52 | u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input); |
| 53 | u32 offset_x, u32 offset_y); | ||
| 54 | 53 | ||
| 55 | /// @brief Swizzles a 2D array of pixels into a 3D texture | 54 | /// @brief Swizzles a 2D array of pixels into a 3D texture |
| 56 | /// @param line_length_in Number of pixels per line | 55 | /// @param line_length_in Number of pixels per line |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5738787ac..8fc322b30 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -567,7 +567,7 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p | |||
| 567 | screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); | 567 | screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); |
| 568 | renderer.RequestScreenshot( | 568 | renderer.RequestScreenshot( |
| 569 | screenshot_image.bits(), | 569 | screenshot_image.bits(), |
| 570 | [=] { | 570 | [=, this] { |
| 571 | const std::string std_screenshot_path = screenshot_path.toStdString(); | 571 | const std::string std_screenshot_path = screenshot_path.toStdString(); |
| 572 | if (screenshot_image.mirrored(false, true).save(screenshot_path)) { | 572 | if (screenshot_image.mirrored(false, true).save(screenshot_path)) { |
| 573 | LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path); | 573 | LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path); |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 59a193edd..cb71b8d11 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -578,7 +578,6 @@ void Config::ReadPathValues() { | |||
| 578 | 578 | ||
| 579 | UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); | 579 | UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); |
| 580 | UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); | 580 | UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); |
| 581 | UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString(); | ||
| 582 | UISettings::values.game_dir_deprecated = | 581 | UISettings::values.game_dir_deprecated = |
| 583 | ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); | 582 | ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); |
| 584 | UISettings::values.game_dir_deprecated_deepscan = | 583 | UISettings::values.game_dir_deprecated_deepscan = |
| @@ -666,8 +665,6 @@ void Config::ReadRendererValues() { | |||
| 666 | QStringLiteral("use_asynchronous_shaders"), false); | 665 | QStringLiteral("use_asynchronous_shaders"), false); |
| 667 | ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), | 666 | ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), |
| 668 | true); | 667 | true); |
| 669 | ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false); | ||
| 670 | |||
| 671 | ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); | 668 | ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); |
| 672 | ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); | 669 | ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); |
| 673 | ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); | 670 | ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); |
| @@ -675,6 +672,22 @@ void Config::ReadRendererValues() { | |||
| 675 | qt_config->endGroup(); | 672 | qt_config->endGroup(); |
| 676 | } | 673 | } |
| 677 | 674 | ||
| 675 | void Config::ReadScreenshotValues() { | ||
| 676 | qt_config->beginGroup(QStringLiteral("Screenshots")); | ||
| 677 | |||
| 678 | UISettings::values.enable_screenshot_save_as = | ||
| 679 | ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); | ||
| 680 | FileUtil::GetUserPath( | ||
| 681 | FileUtil::UserPath::ScreenshotsDir, | ||
| 682 | qt_config | ||
| 683 | ->value(QStringLiteral("screenshot_path"), QString::fromStdString(FileUtil::GetUserPath( | ||
| 684 | FileUtil::UserPath::ScreenshotsDir))) | ||
| 685 | .toString() | ||
| 686 | .toStdString()); | ||
| 687 | |||
| 688 | qt_config->endGroup(); | ||
| 689 | } | ||
| 690 | |||
| 678 | void Config::ReadShortcutValues() { | 691 | void Config::ReadShortcutValues() { |
| 679 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | 692 | qt_config->beginGroup(QStringLiteral("Shortcuts")); |
| 680 | 693 | ||
| @@ -756,6 +769,7 @@ void Config::ReadUIValues() { | |||
| 756 | ReadUIGamelistValues(); | 769 | ReadUIGamelistValues(); |
| 757 | ReadUILayoutValues(); | 770 | ReadUILayoutValues(); |
| 758 | ReadPathValues(); | 771 | ReadPathValues(); |
| 772 | ReadScreenshotValues(); | ||
| 759 | ReadShortcutValues(); | 773 | ReadShortcutValues(); |
| 760 | 774 | ||
| 761 | UISettings::values.single_window_mode = | 775 | UISettings::values.single_window_mode = |
| @@ -1085,7 +1099,6 @@ void Config::SavePathValues() { | |||
| 1085 | 1099 | ||
| 1086 | WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); | 1100 | WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); |
| 1087 | WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); | 1101 | WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); |
| 1088 | WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path); | ||
| 1089 | qt_config->beginWriteArray(QStringLiteral("gamedirs")); | 1102 | qt_config->beginWriteArray(QStringLiteral("gamedirs")); |
| 1090 | for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { | 1103 | for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { |
| 1091 | qt_config->setArrayIndex(i); | 1104 | qt_config->setArrayIndex(i); |
| @@ -1153,9 +1166,6 @@ void Config::SaveRendererValues() { | |||
| 1153 | Settings::values.use_asynchronous_shaders, false); | 1166 | Settings::values.use_asynchronous_shaders, false); |
| 1154 | WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, | 1167 | WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, |
| 1155 | true); | 1168 | true); |
| 1156 | WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, | ||
| 1157 | false); | ||
| 1158 | |||
| 1159 | // Cast to double because Qt's written float values are not human-readable | 1169 | // Cast to double because Qt's written float values are not human-readable |
| 1160 | WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); | 1170 | WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); |
| 1161 | WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); | 1171 | WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); |
| @@ -1164,6 +1174,17 @@ void Config::SaveRendererValues() { | |||
| 1164 | qt_config->endGroup(); | 1174 | qt_config->endGroup(); |
| 1165 | } | 1175 | } |
| 1166 | 1176 | ||
| 1177 | void Config::SaveScreenshotValues() { | ||
| 1178 | qt_config->beginGroup(QStringLiteral("Screenshots")); | ||
| 1179 | |||
| 1180 | WriteSetting(QStringLiteral("enable_screenshot_save_as"), | ||
| 1181 | UISettings::values.enable_screenshot_save_as); | ||
| 1182 | WriteSetting(QStringLiteral("screenshot_path"), | ||
| 1183 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); | ||
| 1184 | |||
| 1185 | qt_config->endGroup(); | ||
| 1186 | } | ||
| 1187 | |||
| 1167 | void Config::SaveShortcutValues() { | 1188 | void Config::SaveShortcutValues() { |
| 1168 | qt_config->beginGroup(QStringLiteral("Shortcuts")); | 1189 | qt_config->beginGroup(QStringLiteral("Shortcuts")); |
| 1169 | 1190 | ||
| @@ -1226,6 +1247,7 @@ void Config::SaveUIValues() { | |||
| 1226 | SaveUIGamelistValues(); | 1247 | SaveUIGamelistValues(); |
| 1227 | SaveUILayoutValues(); | 1248 | SaveUILayoutValues(); |
| 1228 | SavePathValues(); | 1249 | SavePathValues(); |
| 1250 | SaveScreenshotValues(); | ||
| 1229 | SaveShortcutValues(); | 1251 | SaveShortcutValues(); |
| 1230 | 1252 | ||
| 1231 | WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true); | 1253 | WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true); |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 8e815f829..e5f39b040 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -51,6 +51,7 @@ private: | |||
| 51 | void ReadPathValues(); | 51 | void ReadPathValues(); |
| 52 | void ReadCpuValues(); | 52 | void ReadCpuValues(); |
| 53 | void ReadRendererValues(); | 53 | void ReadRendererValues(); |
| 54 | void ReadScreenshotValues(); | ||
| 54 | void ReadShortcutValues(); | 55 | void ReadShortcutValues(); |
| 55 | void ReadSystemValues(); | 56 | void ReadSystemValues(); |
| 56 | void ReadUIValues(); | 57 | void ReadUIValues(); |
| @@ -76,6 +77,7 @@ private: | |||
| 76 | void SavePathValues(); | 77 | void SavePathValues(); |
| 77 | void SaveCpuValues(); | 78 | void SaveCpuValues(); |
| 78 | void SaveRendererValues(); | 79 | void SaveRendererValues(); |
| 80 | void SaveScreenshotValues(); | ||
| 79 | void SaveShortcutValues(); | 81 | void SaveShortcutValues(); |
| 80 | void SaveSystemValues(); | 82 | void SaveSystemValues(); |
| 81 | void SaveUIValues(); | 83 | void SaveUIValues(); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 272bdd6b8..9d6feb9f7 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -171,26 +171,6 @@ | |||
| 171 | </property> | 171 | </property> |
| 172 | <layout class="QVBoxLayout" name="verticalLayout_6"> | 172 | <layout class="QVBoxLayout" name="verticalLayout_6"> |
| 173 | <item> | 173 | <item> |
| 174 | <widget class="QCheckBox" name="dump_decompressed_nso"> | ||
| 175 | <property name="whatsThis"> | ||
| 176 | <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> | ||
| 177 | </property> | ||
| 178 | <property name="text"> | ||
| 179 | <string>Dump Decompressed NSOs</string> | ||
| 180 | </property> | ||
| 181 | </widget> | ||
| 182 | </item> | ||
| 183 | <item> | ||
| 184 | <widget class="QCheckBox" name="dump_exefs"> | ||
| 185 | <property name="whatsThis"> | ||
| 186 | <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string> | ||
| 187 | </property> | ||
| 188 | <property name="text"> | ||
| 189 | <string>Dump ExeFS</string> | ||
| 190 | </property> | ||
| 191 | </widget> | ||
| 192 | </item> | ||
| 193 | <item> | ||
| 194 | <widget class="QCheckBox" name="reporting_services"> | 174 | <widget class="QCheckBox" name="reporting_services"> |
| 195 | <property name="text"> | 175 | <property name="text"> |
| 196 | <string>Enable Verbose Reporting Services</string> | 176 | <string>Enable Verbose Reporting Services</string> |
| @@ -257,8 +237,6 @@ | |||
| 257 | <tabstop>open_log_button</tabstop> | 237 | <tabstop>open_log_button</tabstop> |
| 258 | <tabstop>homebrew_args_edit</tabstop> | 238 | <tabstop>homebrew_args_edit</tabstop> |
| 259 | <tabstop>enable_graphics_debugging</tabstop> | 239 | <tabstop>enable_graphics_debugging</tabstop> |
| 260 | <tabstop>dump_decompressed_nso</tabstop> | ||
| 261 | <tabstop>dump_exefs</tabstop> | ||
| 262 | <tabstop>reporting_services</tabstop> | 240 | <tabstop>reporting_services</tabstop> |
| 263 | <tabstop>quest_flag</tabstop> | 241 | <tabstop>quest_flag</tabstop> |
| 264 | </tabstops> | 242 | </tabstops> |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 8b9180811..c5d1a778c 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp | |||
| @@ -25,14 +25,12 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { | |||
| 25 | ui->use_vsync->setEnabled(runtime_lock); | 25 | ui->use_vsync->setEnabled(runtime_lock); |
| 26 | ui->use_assembly_shaders->setEnabled(runtime_lock); | 26 | ui->use_assembly_shaders->setEnabled(runtime_lock); |
| 27 | ui->use_asynchronous_shaders->setEnabled(runtime_lock); | 27 | ui->use_asynchronous_shaders->setEnabled(runtime_lock); |
| 28 | ui->force_30fps_mode->setEnabled(runtime_lock); | ||
| 29 | ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); | 28 | ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); |
| 30 | 29 | ||
| 31 | ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); | 30 | ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); |
| 32 | ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); | 31 | ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); |
| 33 | ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); | 32 | ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); |
| 34 | ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); | 33 | ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); |
| 35 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue()); | ||
| 36 | 34 | ||
| 37 | if (Settings::configuring_global) { | 35 | if (Settings::configuring_global) { |
| 38 | ui->gpu_accuracy->setCurrentIndex( | 36 | ui->gpu_accuracy->setCurrentIndex( |
| @@ -78,9 +76,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { | |||
| 78 | if (Settings::values.use_fast_gpu_time.UsingGlobal()) { | 76 | if (Settings::values.use_fast_gpu_time.UsingGlobal()) { |
| 79 | Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked()); | 77 | Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked()); |
| 80 | } | 78 | } |
| 81 | if (Settings::values.force_30fps_mode.UsingGlobal()) { | ||
| 82 | Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked()); | ||
| 83 | } | ||
| 84 | if (Settings::values.max_anisotropy.UsingGlobal()) { | 79 | if (Settings::values.max_anisotropy.UsingGlobal()) { |
| 85 | Settings::values.max_anisotropy.SetValue( | 80 | Settings::values.max_anisotropy.SetValue( |
| 86 | ui->anisotropic_filtering_combobox->currentIndex()); | 81 | ui->anisotropic_filtering_combobox->currentIndex()); |
| @@ -97,8 +92,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { | |||
| 97 | use_asynchronous_shaders); | 92 | use_asynchronous_shaders); |
| 98 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, | 93 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, |
| 99 | ui->use_fast_gpu_time, use_fast_gpu_time); | 94 | ui->use_fast_gpu_time, use_fast_gpu_time); |
| 100 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode, | ||
| 101 | ui->force_30fps_mode, force_30fps_mode); | ||
| 102 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, | 95 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, |
| 103 | ui->anisotropic_filtering_combobox); | 96 | ui->anisotropic_filtering_combobox); |
| 104 | 97 | ||
| @@ -132,7 +125,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { | |||
| 132 | ui->use_asynchronous_shaders->setEnabled( | 125 | ui->use_asynchronous_shaders->setEnabled( |
| 133 | Settings::values.use_asynchronous_shaders.UsingGlobal()); | 126 | Settings::values.use_asynchronous_shaders.UsingGlobal()); |
| 134 | ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); | 127 | ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); |
| 135 | ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal()); | ||
| 136 | ui->anisotropic_filtering_combobox->setEnabled( | 128 | ui->anisotropic_filtering_combobox->setEnabled( |
| 137 | Settings::values.max_anisotropy.UsingGlobal()); | 129 | Settings::values.max_anisotropy.UsingGlobal()); |
| 138 | 130 | ||
| @@ -149,8 +141,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { | |||
| 149 | Settings::values.use_asynchronous_shaders, use_asynchronous_shaders); | 141 | Settings::values.use_asynchronous_shaders, use_asynchronous_shaders); |
| 150 | ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time", | 142 | ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time", |
| 151 | Settings::values.use_fast_gpu_time, use_fast_gpu_time); | 143 | Settings::values.use_fast_gpu_time, use_fast_gpu_time); |
| 152 | ConfigurationShared::SetColoredTristate(ui->force_30fps_mode, "force_30fps_mode", | ||
| 153 | Settings::values.force_30fps_mode, force_30fps_mode); | ||
| 154 | ConfigurationShared::SetColoredComboBox( | 144 | ConfigurationShared::SetColoredComboBox( |
| 155 | ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy", | 145 | ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy", |
| 156 | static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); | 146 | static_cast<int>(Settings::values.gpu_accuracy.GetValue(true))); |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 3c4f6f7bb..e61b571c7 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h | |||
| @@ -38,5 +38,4 @@ private: | |||
| 38 | ConfigurationShared::CheckState use_assembly_shaders; | 38 | ConfigurationShared::CheckState use_assembly_shaders; |
| 39 | ConfigurationShared::CheckState use_asynchronous_shaders; | 39 | ConfigurationShared::CheckState use_asynchronous_shaders; |
| 40 | ConfigurationShared::CheckState use_fast_gpu_time; | 40 | ConfigurationShared::CheckState use_fast_gpu_time; |
| 41 | ConfigurationShared::CheckState force_30fps_mode; | ||
| 42 | }; | 41 | }; |
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 6a0d29c27..a793c803d 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui | |||
| @@ -97,13 +97,6 @@ | |||
| 97 | </widget> | 97 | </widget> |
| 98 | </item> | 98 | </item> |
| 99 | <item> | 99 | <item> |
| 100 | <widget class="QCheckBox" name="force_30fps_mode"> | ||
| 101 | <property name="text"> | ||
| 102 | <string>Force 30 FPS mode</string> | ||
| 103 | </property> | ||
| 104 | </widget> | ||
| 105 | </item> | ||
| 106 | <item> | ||
| 107 | <widget class="QCheckBox" name="use_fast_gpu_time"> | 100 | <widget class="QCheckBox" name="use_fast_gpu_time"> |
| 108 | <property name="text"> | 101 | <property name="text"> |
| 109 | <string>Use Fast GPU Time</string> | 102 | <string>Use Fast GPU Time</string> |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 00433926d..597defe8c 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -280,39 +280,41 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 280 | } | 280 | } |
| 281 | 281 | ||
| 282 | button->setContextMenuPolicy(Qt::CustomContextMenu); | 282 | button->setContextMenuPolicy(Qt::CustomContextMenu); |
| 283 | connect(button, &QPushButton::clicked, [=] { | 283 | connect(button, &QPushButton::clicked, [=, this] { |
| 284 | HandleClick(button_map[button_id], | 284 | HandleClick( |
| 285 | [=](Common::ParamPackage params) { | 285 | button_map[button_id], |
| 286 | // Workaround for ZL & ZR for analog triggers like on XBOX controllors. | 286 | [=, this](Common::ParamPackage params) { |
| 287 | // Analog triggers (from controllers like the XBOX controller) would not | 287 | // Workaround for ZL & ZR for analog triggers like on XBOX controllors. |
| 288 | // work due to a different range of their signals (from 0 to 255 on | 288 | // Analog triggers (from controllers like the XBOX controller) would not |
| 289 | // analog triggers instead of -32768 to 32768 on analog joysticks). The | 289 | // work due to a different range of their signals (from 0 to 255 on |
| 290 | // SDL driver misinterprets analog triggers as analog joysticks. | 290 | // analog triggers instead of -32768 to 32768 on analog joysticks). The |
| 291 | // TODO: reinterpret the signal range for analog triggers to map the | 291 | // SDL driver misinterprets analog triggers as analog joysticks. |
| 292 | // values correctly. This is required for the correct emulation of the | 292 | // TODO: reinterpret the signal range for analog triggers to map the |
| 293 | // analog triggers of the GameCube controller. | 293 | // values correctly. This is required for the correct emulation of the |
| 294 | if (button_id == Settings::NativeButton::ZL || | 294 | // analog triggers of the GameCube controller. |
| 295 | button_id == Settings::NativeButton::ZR) { | 295 | if (button_id == Settings::NativeButton::ZL || |
| 296 | params.Set("direction", "+"); | 296 | button_id == Settings::NativeButton::ZR) { |
| 297 | params.Set("threshold", "0.5"); | 297 | params.Set("direction", "+"); |
| 298 | } | 298 | params.Set("threshold", "0.5"); |
| 299 | buttons_param[button_id] = std::move(params); | 299 | } |
| 300 | }, | 300 | buttons_param[button_id] = std::move(params); |
| 301 | InputCommon::Polling::DeviceType::Button); | 301 | }, |
| 302 | }); | 302 | InputCommon::Polling::DeviceType::Button); |
| 303 | connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { | ||
| 304 | QMenu context_menu; | ||
| 305 | context_menu.addAction(tr("Clear"), [&] { | ||
| 306 | buttons_param[button_id].Clear(); | ||
| 307 | button_map[button_id]->setText(tr("[not set]")); | ||
| 308 | }); | ||
| 309 | context_menu.addAction(tr("Restore Default"), [&] { | ||
| 310 | buttons_param[button_id] = Common::ParamPackage{ | ||
| 311 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; | ||
| 312 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | ||
| 313 | }); | ||
| 314 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | ||
| 315 | }); | 303 | }); |
| 304 | connect(button, &QPushButton::customContextMenuRequested, | ||
| 305 | [=, this](const QPoint& menu_location) { | ||
| 306 | QMenu context_menu; | ||
| 307 | context_menu.addAction(tr("Clear"), [&] { | ||
| 308 | buttons_param[button_id].Clear(); | ||
| 309 | button_map[button_id]->setText(tr("[not set]")); | ||
| 310 | }); | ||
| 311 | context_menu.addAction(tr("Restore Default"), [&] { | ||
| 312 | buttons_param[button_id] = Common::ParamPackage{ | ||
| 313 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; | ||
| 314 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | ||
| 315 | }); | ||
| 316 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | ||
| 317 | }); | ||
| 316 | } | 318 | } |
| 317 | 319 | ||
| 318 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | 320 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { |
| @@ -323,16 +325,17 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 323 | } | 325 | } |
| 324 | 326 | ||
| 325 | analog_button->setContextMenuPolicy(Qt::CustomContextMenu); | 327 | analog_button->setContextMenuPolicy(Qt::CustomContextMenu); |
| 326 | connect(analog_button, &QPushButton::clicked, [=]() { | 328 | connect(analog_button, &QPushButton::clicked, [=, this] { |
| 327 | HandleClick(analog_map_buttons[analog_id][sub_button_id], | 329 | HandleClick( |
| 328 | [=](const Common::ParamPackage& params) { | 330 | analog_map_buttons[analog_id][sub_button_id], |
| 329 | SetAnalogButton(params, analogs_param[analog_id], | 331 | [=, this](const Common::ParamPackage& params) { |
| 330 | analog_sub_buttons[sub_button_id]); | 332 | SetAnalogButton(params, analogs_param[analog_id], |
| 331 | }, | 333 | analog_sub_buttons[sub_button_id]); |
| 332 | InputCommon::Polling::DeviceType::Button); | 334 | }, |
| 335 | InputCommon::Polling::DeviceType::Button); | ||
| 333 | }); | 336 | }); |
| 334 | connect(analog_button, &QPushButton::customContextMenuRequested, | 337 | connect(analog_button, &QPushButton::customContextMenuRequested, |
| 335 | [=](const QPoint& menu_location) { | 338 | [=, this](const QPoint& menu_location) { |
| 336 | QMenu context_menu; | 339 | QMenu context_menu; |
| 337 | context_menu.addAction(tr("Clear"), [&] { | 340 | context_menu.addAction(tr("Clear"), [&] { |
| 338 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | 341 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); |
| @@ -350,7 +353,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 350 | menu_location)); | 353 | menu_location)); |
| 351 | }); | 354 | }); |
| 352 | } | 355 | } |
| 353 | connect(analog_map_stick[analog_id], &QPushButton::clicked, [=] { | 356 | connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] { |
| 354 | if (QMessageBox::information( | 357 | if (QMessageBox::information( |
| 355 | this, tr("Information"), | 358 | this, tr("Information"), |
| 356 | tr("After pressing OK, first move your joystick horizontally, " | 359 | tr("After pressing OK, first move your joystick horizontally, " |
| @@ -358,24 +361,28 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 358 | QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { | 361 | QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { |
| 359 | HandleClick( | 362 | HandleClick( |
| 360 | analog_map_stick[analog_id], | 363 | analog_map_stick[analog_id], |
| 361 | [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, | 364 | [=, this](const Common::ParamPackage& params) { |
| 365 | analogs_param[analog_id] = params; | ||
| 366 | }, | ||
| 362 | InputCommon::Polling::DeviceType::Analog); | 367 | InputCommon::Polling::DeviceType::Analog); |
| 363 | } | 368 | } |
| 364 | }); | 369 | }); |
| 365 | 370 | ||
| 366 | connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { | 371 | connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, |
| 367 | const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); | 372 | [=, this] { |
| 368 | if (analogs_param[analog_id].Get("engine", "") == "sdl" || | 373 | const float slider_value = |
| 369 | analogs_param[analog_id].Get("engine", "") == "gcpad") { | 374 | analog_map_deadzone_and_modifier_slider[analog_id]->value(); |
| 370 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( | 375 | if (analogs_param[analog_id].Get("engine", "") == "sdl" || |
| 371 | tr("Deadzone: %1%").arg(slider_value)); | 376 | analogs_param[analog_id].Get("engine", "") == "gcpad") { |
| 372 | analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); | 377 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( |
| 373 | } else { | 378 | tr("Deadzone: %1%").arg(slider_value)); |
| 374 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( | 379 | analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); |
| 375 | tr("Modifier Scale: %1%").arg(slider_value)); | 380 | } else { |
| 376 | analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); | 381 | analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( |
| 377 | } | 382 | tr("Modifier Scale: %1%").arg(slider_value)); |
| 378 | }); | 383 | analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); |
| 384 | } | ||
| 385 | }); | ||
| 379 | } | 386 | } |
| 380 | 387 | ||
| 381 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); | 388 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); |
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index e0647ea5b..5bcf5ffa8 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp | |||
| @@ -83,25 +83,29 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) | |||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | button->setContextMenuPolicy(Qt::CustomContextMenu); | 85 | button->setContextMenuPolicy(Qt::CustomContextMenu); |
| 86 | connect(button, &QPushButton::clicked, [=] { | 86 | connect(button, &QPushButton::clicked, [=, this] { |
| 87 | HandleClick( | 87 | HandleClick( |
| 88 | button_map[button_id], | 88 | button_map[button_id], |
| 89 | [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, | 89 | [=, this](const Common::ParamPackage& params) { |
| 90 | buttons_param[button_id] = params; | ||
| 91 | }, | ||
| 90 | InputCommon::Polling::DeviceType::Button); | 92 | InputCommon::Polling::DeviceType::Button); |
| 91 | }); | 93 | }); |
| 92 | connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { | 94 | connect(button, &QPushButton::customContextMenuRequested, |
| 93 | QMenu context_menu; | 95 | [=, this](const QPoint& menu_location) { |
| 94 | context_menu.addAction(tr("Clear"), [&] { | 96 | QMenu context_menu; |
| 95 | buttons_param[button_id].Clear(); | 97 | context_menu.addAction(tr("Clear"), [&] { |
| 96 | button_map[button_id]->setText(tr("[not set]")); | 98 | buttons_param[button_id].Clear(); |
| 97 | }); | 99 | button_map[button_id]->setText(tr("[not set]")); |
| 98 | context_menu.addAction(tr("Restore Default"), [&] { | 100 | }); |
| 99 | buttons_param[button_id] = Common::ParamPackage{ | 101 | context_menu.addAction(tr("Restore Default"), [&] { |
| 100 | InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])}; | 102 | buttons_param[button_id] = |
| 101 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | 103 | Common::ParamPackage{InputCommon::GenerateKeyboardParam( |
| 102 | }); | 104 | Config::default_mouse_buttons[button_id])}; |
| 103 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | 105 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); |
| 104 | }); | 106 | }); |
| 107 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | ||
| 108 | }); | ||
| 105 | } | 109 | } |
| 106 | 110 | ||
| 107 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); | 111 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); |
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 24b6c5b72..2c20b68d0 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -4,9 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <utility> | 6 | #include <utility> |
| 7 | #include <QFileDialog> | ||
| 7 | 8 | ||
| 8 | #include <QDirIterator> | 9 | #include <QDirIterator> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/file_util.h" | ||
| 10 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 11 | #include "ui_configure_ui.h" | 13 | #include "ui_configure_ui.h" |
| 12 | #include "yuzu/configuration/configure_ui.h" | 14 | #include "yuzu/configuration/configure_ui.h" |
| @@ -52,9 +54,21 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur | |||
| 52 | 54 | ||
| 53 | // Update text ComboBoxes after user interaction. | 55 | // Update text ComboBoxes after user interaction. |
| 54 | connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated), | 56 | connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated), |
| 55 | [=]() { ConfigureUi::UpdateSecondRowComboBox(); }); | 57 | [this] { ConfigureUi::UpdateSecondRowComboBox(); }); |
| 56 | connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated), | 58 | connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated), |
| 57 | [=]() { ConfigureUi::UpdateFirstRowComboBox(); }); | 59 | [this] { ConfigureUi::UpdateFirstRowComboBox(); }); |
| 60 | |||
| 61 | // Set screenshot path to user specification. | ||
| 62 | connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] { | ||
| 63 | const QString& filename = | ||
| 64 | QFileDialog::getExistingDirectory( | ||
| 65 | this, tr("Select Screenshots Path..."), | ||
| 66 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))) + | ||
| 67 | QDir::separator(); | ||
| 68 | if (!filename.isEmpty()) { | ||
| 69 | ui->screenshot_path_edit->setText(filename); | ||
| 70 | } | ||
| 71 | }); | ||
| 58 | } | 72 | } |
| 59 | 73 | ||
| 60 | ConfigureUi::~ConfigureUi() = default; | 74 | ConfigureUi::~ConfigureUi() = default; |
| @@ -66,6 +80,10 @@ void ConfigureUi::ApplyConfiguration() { | |||
| 66 | UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); | 80 | UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); |
| 67 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); | 81 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); |
| 68 | UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); | 82 | UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); |
| 83 | |||
| 84 | UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); | ||
| 85 | FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir, | ||
| 86 | ui->screenshot_path_edit->text().toStdString()); | ||
| 69 | Settings::Apply(); | 87 | Settings::Apply(); |
| 70 | } | 88 | } |
| 71 | 89 | ||
| @@ -80,6 +98,10 @@ void ConfigureUi::SetConfiguration() { | |||
| 80 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); | 98 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); |
| 81 | ui->icon_size_combobox->setCurrentIndex( | 99 | ui->icon_size_combobox->setCurrentIndex( |
| 82 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); | 100 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); |
| 101 | |||
| 102 | ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); | ||
| 103 | ui->screenshot_path_edit->setText( | ||
| 104 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); | ||
| 83 | } | 105 | } |
| 84 | 106 | ||
| 85 | void ConfigureUi::changeEvent(QEvent* event) { | 107 | void ConfigureUi::changeEvent(QEvent* event) { |
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index 0b81747d7..d895b799f 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | <rect> | 6 | <rect> |
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>300</width> | 9 | <width>363</width> |
| 10 | <height>377</height> | 10 | <height>391</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -128,6 +128,47 @@ | |||
| 128 | </widget> | 128 | </widget> |
| 129 | </item> | 129 | </item> |
| 130 | <item> | 130 | <item> |
| 131 | <widget class="QGroupBox" name="screenshots_GroupBox"> | ||
| 132 | <property name="title"> | ||
| 133 | <string>Screenshots</string> | ||
| 134 | </property> | ||
| 135 | <layout class="QVBoxLayout" name="verticalLayout_4"> | ||
| 136 | <item> | ||
| 137 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 138 | <item> | ||
| 139 | <widget class="QCheckBox" name="enable_screenshot_save_as"> | ||
| 140 | <property name="text"> | ||
| 141 | <string>Ask Where To Save Screenshots (Windows Only)</string> | ||
| 142 | </property> | ||
| 143 | </widget> | ||
| 144 | </item> | ||
| 145 | <item> | ||
| 146 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 147 | <item> | ||
| 148 | <widget class="QLabel" name="label"> | ||
| 149 | <property name="text"> | ||
| 150 | <string>Screenshots Path: </string> | ||
| 151 | </property> | ||
| 152 | </widget> | ||
| 153 | </item> | ||
| 154 | <item> | ||
| 155 | <widget class="QLineEdit" name="screenshot_path_edit"/> | ||
| 156 | </item> | ||
| 157 | <item> | ||
| 158 | <widget class="QToolButton" name="screenshot_path_button"> | ||
| 159 | <property name="text"> | ||
| 160 | <string>...</string> | ||
| 161 | </property> | ||
| 162 | </widget> | ||
| 163 | </item> | ||
| 164 | </layout> | ||
| 165 | </item> | ||
| 166 | </layout> | ||
| 167 | </item> | ||
| 168 | </layout> | ||
| 169 | </widget> | ||
| 170 | </item> | ||
| 171 | <item> | ||
| 131 | <spacer name="verticalSpacer"> | 172 | <spacer name="verticalSpacer"> |
| 132 | <property name="orientation"> | 173 | <property name="orientation"> |
| 133 | <enum>Qt::Vertical</enum> | 174 | <enum>Qt::Vertical</enum> |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index ab7fc7a24..62acc3720 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -474,28 +474,56 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 474 | 474 | ||
| 475 | void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) { | 475 | void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) { |
| 476 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); | 476 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); |
| 477 | QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); | 477 | QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location")); |
| 478 | QAction* open_transferable_shader_cache = | 478 | QAction* open_transferable_shader_cache = |
| 479 | context_menu.addAction(tr("Open Transferable Shader Cache")); | 479 | context_menu.addAction(tr("Open Transferable Shader Cache")); |
| 480 | context_menu.addSeparator(); | 480 | context_menu.addSeparator(); |
| 481 | QMenu* remove_menu = context_menu.addMenu(tr("Remove")); | ||
| 482 | QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); | ||
| 483 | QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); | ||
| 484 | QAction* remove_shader_cache = remove_menu->addAction(tr("Remove Shader Cache")); | ||
| 485 | QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); | ||
| 486 | remove_menu->addSeparator(); | ||
| 487 | QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); | ||
| 481 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); | 488 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); |
| 482 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 489 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 483 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 490 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 484 | context_menu.addSeparator(); | 491 | context_menu.addSeparator(); |
| 485 | QAction* properties = context_menu.addAction(tr("Properties")); | 492 | QAction* properties = context_menu.addAction(tr("Properties")); |
| 486 | 493 | ||
| 487 | open_save_location->setEnabled(program_id != 0); | 494 | open_save_location->setVisible(program_id != 0); |
| 495 | open_mod_location->setVisible(program_id != 0); | ||
| 496 | open_transferable_shader_cache->setVisible(program_id != 0); | ||
| 497 | remove_update->setVisible(program_id != 0); | ||
| 498 | remove_dlc->setVisible(program_id != 0); | ||
| 499 | remove_shader_cache->setVisible(program_id != 0); | ||
| 500 | remove_all_content->setVisible(program_id != 0); | ||
| 488 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 501 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| 489 | navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); | 502 | navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); |
| 490 | 503 | ||
| 491 | connect(open_save_location, &QAction::triggered, [this, program_id, path]() { | 504 | connect(open_save_location, &QAction::triggered, [this, program_id, path]() { |
| 492 | emit OpenFolderRequested(GameListOpenTarget::SaveData, path); | 505 | emit OpenFolderRequested(GameListOpenTarget::SaveData, path); |
| 493 | }); | 506 | }); |
| 494 | connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() { | 507 | connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { |
| 495 | emit OpenFolderRequested(GameListOpenTarget::ModData, path); | 508 | emit OpenFolderRequested(GameListOpenTarget::ModData, path); |
| 496 | }); | 509 | }); |
| 497 | connect(open_transferable_shader_cache, &QAction::triggered, | 510 | connect(open_transferable_shader_cache, &QAction::triggered, |
| 498 | [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); | 511 | [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); |
| 512 | connect(remove_all_content, &QAction::triggered, [this, program_id]() { | ||
| 513 | emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Game); | ||
| 514 | }); | ||
| 515 | connect(remove_update, &QAction::triggered, [this, program_id]() { | ||
| 516 | emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Update); | ||
| 517 | }); | ||
| 518 | connect(remove_dlc, &QAction::triggered, [this, program_id]() { | ||
| 519 | emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent); | ||
| 520 | }); | ||
| 521 | connect(remove_shader_cache, &QAction::triggered, [this, program_id]() { | ||
| 522 | emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache); | ||
| 523 | }); | ||
| 524 | connect(remove_custom_config, &QAction::triggered, [this, program_id]() { | ||
| 525 | emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration); | ||
| 526 | }); | ||
| 499 | connect(dump_romfs, &QAction::triggered, | 527 | connect(dump_romfs, &QAction::triggered, |
| 500 | [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); | 528 | [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); |
| 501 | connect(copy_tid, &QAction::triggered, | 529 | connect(copy_tid, &QAction::triggered, |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index a38cb2fc3..483835cce 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -39,6 +39,17 @@ enum class GameListOpenTarget { | |||
| 39 | ModData, | 39 | ModData, |
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | enum class GameListRemoveTarget { | ||
| 43 | ShaderCache, | ||
| 44 | CustomConfiguration, | ||
| 45 | }; | ||
| 46 | |||
| 47 | enum class InstalledEntryType { | ||
| 48 | Game, | ||
| 49 | Update, | ||
| 50 | AddOnContent, | ||
| 51 | }; | ||
| 52 | |||
| 42 | class GameList : public QWidget { | 53 | class GameList : public QWidget { |
| 43 | Q_OBJECT | 54 | Q_OBJECT |
| 44 | 55 | ||
| @@ -75,6 +86,8 @@ signals: | |||
| 75 | void ShouldCancelWorker(); | 86 | void ShouldCancelWorker(); |
| 76 | void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path); | 87 | void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path); |
| 77 | void OpenTransferableShaderCacheRequested(u64 program_id); | 88 | void OpenTransferableShaderCacheRequested(u64 program_id); |
| 89 | void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); | ||
| 90 | void RemoveFileRequested(u64 program_id, GameListRemoveTarget target); | ||
| 78 | void DumpRomFSRequested(u64 program_id, const std::string& game_path); | 91 | void DumpRomFSRequested(u64 program_id, const std::string& game_path); |
| 79 | void CopyTIDRequested(u64 program_id); | 92 | void CopyTIDRequested(u64 program_id); |
| 80 | void NavigateToGamedbEntryRequested(u64 program_id, | 93 | void NavigateToGamedbEntryRequested(u64 program_id, |
| @@ -117,8 +130,6 @@ private: | |||
| 117 | friend class GameListSearchField; | 130 | friend class GameListSearchField; |
| 118 | }; | 131 | }; |
| 119 | 132 | ||
| 120 | Q_DECLARE_METATYPE(GameListOpenTarget); | ||
| 121 | |||
| 122 | class GameListPlaceholder : public QWidget { | 133 | class GameListPlaceholder : public QWidget { |
| 123 | Q_OBJECT | 134 | Q_OBJECT |
| 124 | public: | 135 | public: |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 2018150db..643ca6491 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -350,6 +350,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa | |||
| 350 | 350 | ||
| 351 | void GameListWorker::run() { | 351 | void GameListWorker::run() { |
| 352 | stop_processing = false; | 352 | stop_processing = false; |
| 353 | provider->ClearAllEntries(); | ||
| 353 | 354 | ||
| 354 | for (UISettings::GameDir& game_dir : game_dirs) { | 355 | for (UISettings::GameDir& game_dir : game_dirs) { |
| 355 | if (game_dir.path == QStringLiteral("SDMC")) { | 356 | if (game_dir.path == QStringLiteral("SDMC")) { |
| @@ -368,9 +369,8 @@ void GameListWorker::run() { | |||
| 368 | watch_list.append(game_dir.path); | 369 | watch_list.append(game_dir.path); |
| 369 | auto* const game_list_dir = new GameListDir(game_dir); | 370 | auto* const game_list_dir = new GameListDir(game_dir); |
| 370 | emit DirEntryReady(game_list_dir); | 371 | emit DirEntryReady(game_list_dir); |
| 371 | provider->ClearAllEntries(); | 372 | ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), |
| 372 | ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 2, | 373 | game_dir.deep_scan ? 256 : 0, game_list_dir); |
| 373 | game_list_dir); | ||
| 374 | ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), | 374 | ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), |
| 375 | game_dir.deep_scan ? 256 : 0, game_list_dir); | 375 | game_dir.deep_scan ? 256 : 0, game_list_dir); |
| 376 | } | 376 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 31a635176..592993c36 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -583,7 +583,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 583 | renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); | 583 | renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); |
| 584 | renderer_status_button->setCheckable(true); | 584 | renderer_status_button->setCheckable(true); |
| 585 | renderer_status_button->setFocusPolicy(Qt::NoFocus); | 585 | renderer_status_button->setFocusPolicy(Qt::NoFocus); |
| 586 | connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) { | 586 | connect(renderer_status_button, &QPushButton::toggled, [this](bool checked) { |
| 587 | renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); | 587 | renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); |
| 588 | }); | 588 | }); |
| 589 | renderer_status_button->toggle(); | 589 | renderer_status_button->toggle(); |
| @@ -595,7 +595,7 @@ void GMainWindow::InitializeWidgets() { | |||
| 595 | #else | 595 | #else |
| 596 | renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == | 596 | renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == |
| 597 | Settings::RendererBackend::Vulkan); | 597 | Settings::RendererBackend::Vulkan); |
| 598 | connect(renderer_status_button, &QPushButton::clicked, [=] { | 598 | connect(renderer_status_button, &QPushButton::clicked, [this] { |
| 599 | if (emulation_running) { | 599 | if (emulation_running) { |
| 600 | return; | 600 | return; |
| 601 | } | 601 | } |
| @@ -847,6 +847,9 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 847 | connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); | 847 | connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); |
| 848 | connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, | 848 | connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, |
| 849 | &GMainWindow::OnTransferableShaderCacheOpenFile); | 849 | &GMainWindow::OnTransferableShaderCacheOpenFile); |
| 850 | connect(game_list, &GameList::RemoveInstalledEntryRequested, this, | ||
| 851 | &GMainWindow::OnGameListRemoveInstalledEntry); | ||
| 852 | connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); | ||
| 850 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); | 853 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); |
| 851 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); | 854 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); |
| 852 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | 855 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, |
| @@ -1257,7 +1260,6 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str | |||
| 1257 | case GameListOpenTarget::SaveData: { | 1260 | case GameListOpenTarget::SaveData: { |
| 1258 | open_target = tr("Save Data"); | 1261 | open_target = tr("Save Data"); |
| 1259 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); | 1262 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); |
| 1260 | ASSERT(program_id != 0); | ||
| 1261 | 1263 | ||
| 1262 | if (has_user_save) { | 1264 | if (has_user_save) { |
| 1263 | // User save data | 1265 | // User save data |
| @@ -1322,14 +1324,12 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str | |||
| 1322 | } | 1324 | } |
| 1323 | 1325 | ||
| 1324 | void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { | 1326 | void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { |
| 1325 | ASSERT(program_id != 0); | ||
| 1326 | |||
| 1327 | const QString shader_dir = | 1327 | const QString shader_dir = |
| 1328 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); | 1328 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); |
| 1329 | const QString tranferable_shader_cache_folder_path = | 1329 | const QString transferable_shader_cache_folder_path = |
| 1330 | shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); | 1330 | shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); |
| 1331 | const QString transferable_shader_cache_file_path = | 1331 | const QString transferable_shader_cache_file_path = |
| 1332 | tranferable_shader_cache_folder_path + QDir::separator() + | 1332 | transferable_shader_cache_folder_path + QDir::separator() + |
| 1333 | QString::fromStdString(fmt::format("{:016X}.bin", program_id)); | 1333 | QString::fromStdString(fmt::format("{:016X}.bin", program_id)); |
| 1334 | 1334 | ||
| 1335 | if (!QFile::exists(transferable_shader_cache_file_path)) { | 1335 | if (!QFile::exists(transferable_shader_cache_file_path)) { |
| @@ -1350,7 +1350,7 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { | |||
| 1350 | param << QDir::toNativeSeparators(transferable_shader_cache_file_path); | 1350 | param << QDir::toNativeSeparators(transferable_shader_cache_file_path); |
| 1351 | QProcess::startDetached(explorer, param); | 1351 | QProcess::startDetached(explorer, param); |
| 1352 | #else | 1352 | #else |
| 1353 | QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); | 1353 | QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path)); |
| 1354 | #endif | 1354 | #endif |
| 1355 | } | 1355 | } |
| 1356 | 1356 | ||
| @@ -1394,6 +1394,174 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src | |||
| 1394 | return true; | 1394 | return true; |
| 1395 | } | 1395 | } |
| 1396 | 1396 | ||
| 1397 | void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { | ||
| 1398 | const QString entry_type = [this, type] { | ||
| 1399 | switch (type) { | ||
| 1400 | case InstalledEntryType::Game: | ||
| 1401 | return tr("Contents"); | ||
| 1402 | case InstalledEntryType::Update: | ||
| 1403 | return tr("Update"); | ||
| 1404 | case InstalledEntryType::AddOnContent: | ||
| 1405 | return tr("DLC"); | ||
| 1406 | default: | ||
| 1407 | return QString{}; | ||
| 1408 | } | ||
| 1409 | }(); | ||
| 1410 | |||
| 1411 | if (QMessageBox::question( | ||
| 1412 | this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), | ||
| 1413 | QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { | ||
| 1414 | return; | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | switch (type) { | ||
| 1418 | case InstalledEntryType::Game: | ||
| 1419 | RemoveBaseContent(program_id, entry_type); | ||
| 1420 | [[fallthrough]]; | ||
| 1421 | case InstalledEntryType::Update: | ||
| 1422 | RemoveUpdateContent(program_id, entry_type); | ||
| 1423 | if (type != InstalledEntryType::Game) { | ||
| 1424 | break; | ||
| 1425 | } | ||
| 1426 | [[fallthrough]]; | ||
| 1427 | case InstalledEntryType::AddOnContent: | ||
| 1428 | RemoveAddOnContent(program_id, entry_type); | ||
| 1429 | break; | ||
| 1430 | } | ||
| 1431 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + | ||
| 1432 | "game_list"); | ||
| 1433 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { | ||
| 1437 | const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); | ||
| 1438 | const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || | ||
| 1439 | fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); | ||
| 1440 | |||
| 1441 | if (res) { | ||
| 1442 | QMessageBox::information(this, tr("Successfully Removed"), | ||
| 1443 | tr("Successfully removed the installed base game.")); | ||
| 1444 | } else { | ||
| 1445 | QMessageBox::warning( | ||
| 1446 | this, tr("Error Removing %1").arg(entry_type), | ||
| 1447 | tr("The base game is not installed in the NAND and cannot be removed.")); | ||
| 1448 | } | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) { | ||
| 1452 | const auto update_id = program_id | 0x800; | ||
| 1453 | const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); | ||
| 1454 | const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || | ||
| 1455 | fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id); | ||
| 1456 | |||
| 1457 | if (res) { | ||
| 1458 | QMessageBox::information(this, tr("Successfully Removed"), | ||
| 1459 | tr("Successfully removed the installed update.")); | ||
| 1460 | } else { | ||
| 1461 | QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), | ||
| 1462 | tr("There is no update installed for this title.")); | ||
| 1463 | } | ||
| 1464 | } | ||
| 1465 | |||
| 1466 | void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { | ||
| 1467 | u32 count{}; | ||
| 1468 | const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); | ||
| 1469 | const auto dlc_entries = Core::System::GetInstance().GetContentProvider().ListEntriesFilter( | ||
| 1470 | FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | ||
| 1471 | |||
| 1472 | for (const auto& entry : dlc_entries) { | ||
| 1473 | if ((entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id) { | ||
| 1474 | const auto res = | ||
| 1475 | fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) || | ||
| 1476 | fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id); | ||
| 1477 | if (res) { | ||
| 1478 | ++count; | ||
| 1479 | } | ||
| 1480 | } | ||
| 1481 | } | ||
| 1482 | |||
| 1483 | if (count == 0) { | ||
| 1484 | QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), | ||
| 1485 | tr("There are no DLC installed for this title.")); | ||
| 1486 | return; | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | QMessageBox::information(this, tr("Successfully Removed"), | ||
| 1490 | tr("Successfully removed %1 installed DLC.").arg(count)); | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) { | ||
| 1494 | const QString question = [this, target] { | ||
| 1495 | switch (target) { | ||
| 1496 | case GameListRemoveTarget::ShaderCache: | ||
| 1497 | return tr("Delete Transferable Shader Cache?"); | ||
| 1498 | case GameListRemoveTarget::CustomConfiguration: | ||
| 1499 | return tr("Remove Custom Game Configuration?"); | ||
| 1500 | default: | ||
| 1501 | return QString{}; | ||
| 1502 | } | ||
| 1503 | }(); | ||
| 1504 | |||
| 1505 | if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No, | ||
| 1506 | QMessageBox::No) != QMessageBox::Yes) { | ||
| 1507 | return; | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | switch (target) { | ||
| 1511 | case GameListRemoveTarget::ShaderCache: | ||
| 1512 | RemoveTransferableShaderCache(program_id); | ||
| 1513 | break; | ||
| 1514 | case GameListRemoveTarget::CustomConfiguration: | ||
| 1515 | RemoveCustomConfiguration(program_id); | ||
| 1516 | break; | ||
| 1517 | } | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { | ||
| 1521 | const QString shader_dir = | ||
| 1522 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); | ||
| 1523 | const QString transferable_shader_cache_folder_path = | ||
| 1524 | shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); | ||
| 1525 | const QString transferable_shader_cache_file_path = | ||
| 1526 | transferable_shader_cache_folder_path + QDir::separator() + | ||
| 1527 | QString::fromStdString(fmt::format("{:016X}.bin", program_id)); | ||
| 1528 | |||
| 1529 | if (!QFile::exists(transferable_shader_cache_file_path)) { | ||
| 1530 | QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), | ||
| 1531 | tr("A shader cache for this title does not exist.")); | ||
| 1532 | return; | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | if (QFile::remove(transferable_shader_cache_file_path)) { | ||
| 1536 | QMessageBox::information(this, tr("Successfully Removed"), | ||
| 1537 | tr("Successfully removed the transferable shader cache.")); | ||
| 1538 | } else { | ||
| 1539 | QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), | ||
| 1540 | tr("Failed to remove the transferable shader cache.")); | ||
| 1541 | } | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | void GMainWindow::RemoveCustomConfiguration(u64 program_id) { | ||
| 1545 | const QString config_dir = | ||
| 1546 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir)); | ||
| 1547 | const QString custom_config_file_path = | ||
| 1548 | config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); | ||
| 1549 | |||
| 1550 | if (!QFile::exists(custom_config_file_path)) { | ||
| 1551 | QMessageBox::warning(this, tr("Error Removing Custom Configuration"), | ||
| 1552 | tr("A custom configuration for this title does not exist.")); | ||
| 1553 | return; | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | if (QFile::remove(custom_config_file_path)) { | ||
| 1557 | QMessageBox::information(this, tr("Successfully Removed"), | ||
| 1558 | tr("Successfully removed the custom game configuration.")); | ||
| 1559 | } else { | ||
| 1560 | QMessageBox::warning(this, tr("Error Removing Custom Configuration"), | ||
| 1561 | tr("Failed to remove the custom game configuration.")); | ||
| 1562 | } | ||
| 1563 | } | ||
| 1564 | |||
| 1397 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { | 1565 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { |
| 1398 | const auto failed = [this] { | 1566 | const auto failed = [this] { |
| 1399 | QMessageBox::warning(this, tr("RomFS Extraction Failed!"), | 1567 | QMessageBox::warning(this, tr("RomFS Extraction Failed!"), |
| @@ -1655,7 +1823,7 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1655 | 1823 | ||
| 1656 | ui.action_Install_File_NAND->setEnabled(false); | 1824 | ui.action_Install_File_NAND->setEnabled(false); |
| 1657 | 1825 | ||
| 1658 | install_progress = new QProgressDialog(QStringLiteral(""), tr("Cancel"), 0, total_size, this); | 1826 | install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); |
| 1659 | install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & | 1827 | install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & |
| 1660 | ~Qt::WindowMaximizeButtonHint); | 1828 | ~Qt::WindowMaximizeButtonHint); |
| 1661 | install_progress->setAttribute(Qt::WA_DeleteOnClose, true); | 1829 | install_progress->setAttribute(Qt::WA_DeleteOnClose, true); |
| @@ -1705,18 +1873,18 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1705 | install_progress->close(); | 1873 | install_progress->close(); |
| 1706 | 1874 | ||
| 1707 | const QString install_results = | 1875 | const QString install_results = |
| 1708 | (new_files.isEmpty() ? QStringLiteral("") | 1876 | (new_files.isEmpty() ? QString{} |
| 1709 | : tr("%n file(s) were newly installed\n", "", new_files.size())) + | 1877 | : tr("%n file(s) were newly installed\n", "", new_files.size())) + |
| 1710 | (overwritten_files.isEmpty() | 1878 | (overwritten_files.isEmpty() |
| 1711 | ? QStringLiteral("") | 1879 | ? QString{} |
| 1712 | : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) + | 1880 | : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) + |
| 1713 | (failed_files.isEmpty() ? QStringLiteral("") | 1881 | (failed_files.isEmpty() ? QString{} |
| 1714 | : tr("%n file(s) failed to install\n", "", failed_files.size())); | 1882 | : tr("%n file(s) failed to install\n", "", failed_files.size())); |
| 1715 | 1883 | ||
| 1716 | QMessageBox::information(this, tr("Install Results"), install_results); | 1884 | QMessageBox::information(this, tr("Install Results"), install_results); |
| 1717 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 1718 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + | 1885 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + |
| 1719 | "game_list"); | 1886 | "game_list"); |
| 1887 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 1720 | ui.action_Install_File_NAND->setEnabled(true); | 1888 | ui.action_Install_File_NAND->setEnabled(true); |
| 1721 | } | 1889 | } |
| 1722 | 1890 | ||
| @@ -2153,17 +2321,28 @@ void GMainWindow::OnToggleFilterBar() { | |||
| 2153 | 2321 | ||
| 2154 | void GMainWindow::OnCaptureScreenshot() { | 2322 | void GMainWindow::OnCaptureScreenshot() { |
| 2155 | OnPauseGame(); | 2323 | OnPauseGame(); |
| 2156 | QFileDialog png_dialog(this, tr("Capture Screenshot"), UISettings::values.screenshot_path, | 2324 | |
| 2157 | tr("PNG Image (*.png)")); | 2325 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 2158 | png_dialog.setAcceptMode(QFileDialog::AcceptSave); | 2326 | const auto screenshot_path = |
| 2159 | png_dialog.setDefaultSuffix(QStringLiteral("png")); | 2327 | QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)); |
| 2160 | if (png_dialog.exec()) { | 2328 | const auto date = |
| 2161 | const QString path = png_dialog.selectedFiles().first(); | 2329 | QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); |
| 2162 | if (!path.isEmpty()) { | 2330 | QString filename = QStringLiteral("%1%2_%3.png") |
| 2163 | UISettings::values.screenshot_path = QFileInfo(path).path(); | 2331 | .arg(screenshot_path) |
| 2164 | render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path); | 2332 | .arg(title_id, 16, 16, QLatin1Char{'0'}) |
| 2333 | .arg(date); | ||
| 2334 | |||
| 2335 | #ifdef _WIN32 | ||
| 2336 | if (UISettings::values.enable_screenshot_save_as) { | ||
| 2337 | filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename, | ||
| 2338 | tr("PNG Image (*.png)")); | ||
| 2339 | if (filename.isEmpty()) { | ||
| 2340 | OnStartGame(); | ||
| 2341 | return; | ||
| 2165 | } | 2342 | } |
| 2166 | } | 2343 | } |
| 2344 | #endif | ||
| 2345 | render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, filename); | ||
| 2167 | OnStartGame(); | 2346 | OnStartGame(); |
| 2168 | } | 2347 | } |
| 2169 | 2348 | ||
| @@ -2202,8 +2381,7 @@ void GMainWindow::UpdateStatusBar() { | |||
| 2202 | 2381 | ||
| 2203 | if (shaders_building != 0) { | 2382 | if (shaders_building != 0) { |
| 2204 | shader_building_label->setText( | 2383 | shader_building_label->setText( |
| 2205 | tr("Building: %1 shader").arg(shaders_building) + | 2384 | tr("Building: %n shader(s)", "", static_cast<int>(shaders_building))); |
| 2206 | (shaders_building != 1 ? QString::fromStdString("s") : QString::fromStdString(""))); | ||
| 2207 | shader_building_label->setVisible(true); | 2385 | shader_building_label->setVisible(true); |
| 2208 | } else { | 2386 | } else { |
| 2209 | shader_building_label->setVisible(false); | 2387 | shader_building_label->setVisible(false); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index db573d606..73a44a3bf 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -32,6 +32,8 @@ class QPushButton; | |||
| 32 | class QProgressDialog; | 32 | class QProgressDialog; |
| 33 | class WaitTreeWidget; | 33 | class WaitTreeWidget; |
| 34 | enum class GameListOpenTarget; | 34 | enum class GameListOpenTarget; |
| 35 | enum class GameListRemoveTarget; | ||
| 36 | enum class InstalledEntryType; | ||
| 35 | class GameListPlaceholder; | 37 | class GameListPlaceholder; |
| 36 | 38 | ||
| 37 | namespace Core::Frontend { | 39 | namespace Core::Frontend { |
| @@ -198,6 +200,8 @@ private slots: | |||
| 198 | void OnGameListLoadFile(QString game_path); | 200 | void OnGameListLoadFile(QString game_path); |
| 199 | void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); | 201 | void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); |
| 200 | void OnTransferableShaderCacheOpenFile(u64 program_id); | 202 | void OnTransferableShaderCacheOpenFile(u64 program_id); |
| 203 | void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); | ||
| 204 | void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target); | ||
| 201 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); | 205 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); |
| 202 | void OnGameListCopyTID(u64 program_id); | 206 | void OnGameListCopyTID(u64 program_id); |
| 203 | void OnGameListNavigateToGamedbEntry(u64 program_id, | 207 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| @@ -229,6 +233,11 @@ private slots: | |||
| 229 | void OnLanguageChanged(const QString& locale); | 233 | void OnLanguageChanged(const QString& locale); |
| 230 | 234 | ||
| 231 | private: | 235 | private: |
| 236 | void RemoveBaseContent(u64 program_id, const QString& entry_type); | ||
| 237 | void RemoveUpdateContent(u64 program_id, const QString& entry_type); | ||
| 238 | void RemoveAddOnContent(u64 program_id, const QString& entry_type); | ||
| 239 | void RemoveTransferableShaderCache(u64 program_id); | ||
| 240 | void RemoveCustomConfiguration(u64 program_id); | ||
| 232 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 241 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| 233 | InstallResult InstallNSPXCI(const QString& filename); | 242 | InstallResult InstallNSPXCI(const QString& filename); |
| 234 | InstallResult InstallNCA(const QString& filename); | 243 | InstallResult InstallNCA(const QString& filename); |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index ac7b9aef6..bbfeafc55 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -66,11 +66,11 @@ struct Values { | |||
| 66 | // Discord RPC | 66 | // Discord RPC |
| 67 | bool enable_discord_presence; | 67 | bool enable_discord_presence; |
| 68 | 68 | ||
| 69 | bool enable_screenshot_save_as; | ||
| 69 | u16 screenshot_resolution_factor; | 70 | u16 screenshot_resolution_factor; |
| 70 | 71 | ||
| 71 | QString roms_path; | 72 | QString roms_path; |
| 72 | QString symbols_path; | 73 | QString symbols_path; |
| 73 | QString screenshot_path; | ||
| 74 | QString game_dir_deprecated; | 74 | QString game_dir_deprecated; |
| 75 | bool game_dir_deprecated_deepscan; | 75 | bool game_dir_deprecated_deepscan; |
| 76 | QVector<UISettings::GameDir> game_dirs; | 76 | QVector<UISettings::GameDir> game_dirs; |