summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt8
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/atomic_ops.cpp37
-rw-r--r--src/common/atomic_ops.h10
-rw-r--r--src/common/common_paths.h1
-rw-r--r--src/common/concepts.h34
-rw-r--r--src/common/file_util.cpp1
-rw-r--r--src/common/file_util.h1
-rw-r--r--src/common/hex_util.cpp34
-rw-r--r--src/common/hex_util.h29
-rw-r--r--src/common/logging/backend.cpp22
-rw-r--r--src/common/logging/backend.h14
-rw-r--r--src/core/core.cpp2
-rw-r--r--src/core/cpu_manager.cpp6
-rw-r--r--src/core/crypto/aes_util.cpp21
-rw-r--r--src/core/crypto/aes_util.h9
-rw-r--r--src/core/crypto/ctr_encryption_layer.cpp9
-rw-r--r--src/core/crypto/ctr_encryption_layer.h9
-rw-r--r--src/core/crypto/key_manager.cpp10
-rw-r--r--src/core/crypto/partition_data_manager.cpp211
-rw-r--r--src/core/device_memory.cpp5
-rw-r--r--src/core/device_memory.h8
-rw-r--r--src/core/file_sys/content_archive.cpp7
-rw-r--r--src/core/file_sys/mode.h9
-rw-r--r--src/core/file_sys/nca_patch.cpp3
-rw-r--r--src/core/file_sys/patch_manager.cpp4
-rw-r--r--src/core/file_sys/registered_cache.cpp126
-rw-r--r--src/core/file_sys/registered_cache.h6
-rw-r--r--src/core/file_sys/savedata_factory.cpp33
-rw-r--r--src/core/file_sys/savedata_factory.h47
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp42
-rw-r--r--src/core/file_sys/vfs_real.cpp30
-rw-r--r--src/core/file_sys/xts_archive.cpp14
-rw-r--r--src/core/hle/ipc_helpers.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/hle_ipc.h30
-rw-r--r--src/core/hle/kernel/scheduler.cpp3
-rw-r--r--src/core/hle/service/acc/acc.cpp6
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp43
-rw-r--r--src/core/hle/service/am/am.cpp10
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp2
-rw-r--r--src/core/hle/service/bcat/module.cpp2
-rw-r--r--src/core/hle/service/es/es.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp8
-rw-r--r--src/core/hle/service/filesystem/filesystem.h6
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp73
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h6
-rw-r--r--src/core/hle/service/ldr/ldr.cpp5
-rw-r--r--src/core/hle/service/nfp/nfp.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp202
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h79
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp33
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h6
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp36
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp21
-rw-r--r--src/core/hle/service/set/set.cpp2
-rw-r--r--src/core/hle/service/sm/sm.h5
-rw-r--r--src/core/hle/service/time/time.cpp4
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp4
-rw-r--r--src/core/hle/service/vi/vi.cpp24
-rw-r--r--src/core/loader/loader.cpp52
-rw-r--r--src/core/memory.cpp10
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp226
-rw-r--r--src/core/perf_stats.cpp20
-rw-r--r--src/core/perf_stats.h11
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h1
-rw-r--r--src/tests/core/core_timing.cpp14
-rw-r--r--src/video_core/compatible_formats.h2
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/memory_manager.cpp532
-rw-r--r--src/video_core/memory_manager.h172
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_sampler_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp2
-rw-r--r--src/video_core/texture_cache/surface_params.cpp97
-rw-r--r--src/yuzu/bootmanager.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp36
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp10
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui7
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp76
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp39
-rw-r--r--src/yuzu/configuration/configure_ui.cpp26
-rw-r--r--src/yuzu/configuration/configure_ui.ui45
-rw-r--r--src/yuzu/game_list.cpp34
-rw-r--r--src/yuzu/game_list.h15
-rw-r--r--src/yuzu/game_list_worker.cpp4
-rw-r--r--src/yuzu/main.cpp226
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu/uisettings.h2
98 files changed, 1743 insertions, 1402 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7a49318aa..3282ae9d4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -159,15 +159,15 @@ macro(yuzu_find_packages)
# Capitalization matters here. We need the naming to match the generated paths from Conan
set(REQUIRED_LIBS
# Cmake Pkg Prefix Version Conan Pkg
- "Boost 1.71 boost/1.72.0"
- "Catch2 2.11 catch2/2.11.0"
+ "Boost 1.73 boost/1.73.0"
+ "Catch2 2.13 catch2/2.13.0"
"fmt 7.0 fmt/7.0.1"
# can't use until https://github.com/bincrafters/community/issues/1173
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
"lz4 1.8 lz4/1.9.2"
- "nlohmann_json 3.7 nlohmann_json/3.7.3"
+ "nlohmann_json 3.8 nlohmann_json/3.8.0"
"ZLIB 1.2 zlib/1.2.11"
- "zstd 1.4 zstd/1.4.4"
+ "zstd 1.4 zstd/1.4.5"
)
foreach(PACKAGE ${REQUIRED_LIBS})
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()
-Wno-unused-parameter
)
+ # TODO: Remove when we update to a GCC compiler that enables this
+ # by default (i.e. GCC 10 or newer).
+ if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
+ add_compile_options(-fconcepts)
+ endif()
+
if (ARCHITECTURE_x86_64)
add_compile_options("-mcx16")
endif()
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
common_funcs.h
common_paths.h
common_types.h
+ concepts.h
dynamic_library.cpp
dynamic_library.h
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 {
#if _MSC_VER
-bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) {
- u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected);
+bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
+ const u8 result =
+ _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
return result == expected;
}
-bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) {
- u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected);
+bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
+ const u16 result =
+ _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
return result == expected;
}
-bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) {
- u32 result = _InterlockedCompareExchange((long*)pointer, value, expected);
+bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
+ const u32 result =
+ _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
return result == expected;
}
-bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
- u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected);
+bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
+ const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
+ value, expected);
return result == expected;
}
-bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) {
- return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0],
- (__int64*)expected.data()) != 0;
+bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
+ return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
+ value[0],
+ reinterpret_cast<__int64*>(expected.data())) != 0;
}
#else
-bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) {
+bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
-bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) {
+bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
-bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) {
+bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
-bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
+bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
return __sync_bool_compare_and_swap(pointer, expected, value);
}
-bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) {
+bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
unsigned __int128 value_a;
unsigned __int128 expected_a;
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 @@
namespace Common {
-bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected);
-bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected);
-bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected);
-bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected);
-bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected);
+bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected);
+bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected);
+bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected);
+bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected);
+bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected);
} // 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 @@
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define DUMP_DIR "dump"
+#define SCREENSHOTS_DIR "screenshots"
#define SHADER_DIR "shader"
#define LOG_DIR "log"
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 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Common {
+
+#include <type_traits>
+
+// Check if type is like an STL container
+template <typename T>
+concept IsSTLContainer = requires(T t) {
+ typename T::value_type;
+ typename T::iterator;
+ typename T::const_iterator;
+ // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
+ t.begin();
+ t.end();
+ t.cbegin();
+ t.cend();
+ t.data();
+ t.size();
+};
+
+// TODO: Replace with std::derived_from when the <concepts> header
+// is available on all supported platforms.
+template <typename Derived, typename Base>
+concept DerivedFrom = requires {
+ std::is_base_of_v<Base, Derived>;
+ std::is_convertible_v<const volatile Derived*, const volatile Base*>;
+};
+
+} // 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) {
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
+ paths.emplace(UserPath::ScreenshotsDir, user_path + SCREENSHOTS_DIR DIR_SEP);
paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
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 {
SDMCDir,
LoadDir,
DumpDir,
+ ScreenshotsDir,
ShaderDir,
SysDataDir,
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 @@
// Refer to the license.txt file included.
#include "common/hex_util.h"
-#include "common/logging/log.h"
namespace Common {
-u8 ToHexNibble(char c1) {
- if (c1 >= 65 && c1 <= 70)
- return c1 - 55;
- if (c1 >= 97 && c1 <= 102)
- return c1 - 87;
- if (c1 >= 48 && c1 <= 57)
- return c1 - 48;
- LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
- return 0;
-}
-
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
std::vector<u8> out(str.size() / 2);
if (little_endian) {
@@ -30,26 +18,4 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
return out;
}
-std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
- if (len != 32) {
- LOG_ERROR(Common,
- "Attempting to parse string to array that is not of correct size (expected=32, "
- "actual={}).",
- len);
- return {};
- }
- return HexStringToArray<16>(str);
-}
-
-std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
- if (len != 64) {
- LOG_ERROR(Common,
- "Attempting to parse string to array that is not of correct size (expected=64, "
- "actual={}).",
- len);
- return {};
- }
- return HexStringToArray<32>(str);
-}
-
} // 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 @@
namespace Common {
-u8 ToHexNibble(char c1);
+constexpr u8 ToHexNibble(char c) {
+ if (c >= 65 && c <= 70) {
+ return c - 55;
+ }
+
+ if (c >= 97 && c <= 102) {
+ return c - 87;
+ }
+
+ return c - 48;
+}
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
template <std::size_t Size, bool le = false>
-std::array<u8, Size> HexStringToArray(std::string_view str) {
+constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
if constexpr (le) {
- for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
+ for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
+ }
} else {
- for (std::size_t i = 0; i < 2 * Size; i += 2)
+ for (std::size_t i = 0; i < 2 * Size; i += 2) {
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
+ }
}
return out;
}
@@ -48,7 +60,12 @@ std::string HexToString(const ContiguousContainer& data, bool upper = true) {
return out;
}
-std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
-std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
+constexpr std::array<u8, 16> AsArray(const char (&data)[17]) {
+ return HexStringToArray<16>(data);
+}
+
+constexpr std::array<u8, 32> AsArray(const char (&data)[65]) {
+ return HexStringToArray<32>(data);
+}
} // 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:
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string message) const {
using std::chrono::duration_cast;
+ using std::chrono::microseconds;
using std::chrono::steady_clock;
- Entry entry;
- entry.timestamp =
- duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
- entry.log_class = log_class;
- entry.log_level = log_level;
- entry.filename = filename;
- entry.line_num = line_nr;
- entry.function = function;
- entry.message = std::move(message);
-
- return entry;
+ return {
+ .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
+ .log_class = log_class,
+ .log_level = log_level,
+ .filename = filename,
+ .line_num = line_nr,
+ .function = function,
+ .message = std::move(message),
+ .final_entry = false,
+ };
}
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;
*/
struct Entry {
std::chrono::microseconds timestamp;
- Class log_class;
- Level log_level;
- const char* filename;
- unsigned int line_num;
+ Class log_class{};
+ Level log_level{};
+ const char* filename = nullptr;
+ unsigned int line_num = 0;
std::string function;
std::string message;
bool final_entry = false;
-
- Entry() = default;
- Entry(Entry&& o) = default;
-
- Entry& operator=(Entry&& o) = default;
- Entry& operator=(const Entry& o) = default;
};
/**
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 {
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
- device_memory = std::make_unique<Core::DeviceMemory>(system);
+ device_memory = std::make_unique<Core::DeviceMemory>();
is_multicore = Settings::values.use_multi_core.GetValue();
is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue();
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() {
}
std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
- return std::function<void(void*)>(GuestThreadFunction);
+ return GuestThreadFunction;
}
std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
- return std::function<void(void*)>(IdleThreadFunction);
+ return IdleThreadFunction;
}
std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
- return std::function<void(void*)>(SuspendThreadFunction);
+ return SuspendThreadFunction;
}
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
#include <mbedtls/cipher.h>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -10,8 +11,10 @@
namespace Core::Crypto {
namespace {
-std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) {
- std::vector<u8> out(0x10);
+using NintendoTweak = std::array<u8, 16>;
+
+NintendoTweak CalculateNintendoTweak(std::size_t sector_id) {
+ NintendoTweak out{};
for (std::size_t i = 0xF; i <= 0xF; --i) {
out[i] = sector_id & 0xFF;
sector_id >>= 8;
@@ -64,13 +67,6 @@ AESCipher<Key, KeySize>::~AESCipher() {
}
template <typename Key, std::size_t KeySize>
-void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
- ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) ||
- mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
- "Failed to set IV on mbedtls ciphers.");
-}
-
-template <typename Key, std::size_t KeySize>
void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const {
auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
@@ -124,6 +120,13 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8*
}
}
+template <typename Key, std::size_t KeySize>
+void AESCipher<Key, KeySize>::SetIVImpl(const u8* data, std::size_t size) {
+ ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, data, size) ||
+ mbedtls_cipher_set_iv(&ctx->decryption_context, data, size)) == 0,
+ "Failed to set IV on mbedtls ciphers.");
+}
+
template class AESCipher<Key128>;
template class AESCipher<Key256>;
} // 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 @@
#include <memory>
#include <type_traits>
-#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
@@ -32,10 +31,12 @@ class AESCipher {
public:
AESCipher(Key key, Mode mode);
-
~AESCipher();
- void SetIV(std::vector<u8> iv);
+ template <typename ContiguousContainer>
+ void SetIV(const ContiguousContainer& container) {
+ SetIVImpl(std::data(container), std::size(container));
+ }
template <typename Source, typename Dest>
void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const {
@@ -59,6 +60,8 @@ public:
std::size_t sector_size, Op op);
private:
+ void SetIVImpl(const u8* data, std::size_t size);
+
std::unique_ptr<CipherContext> ctx;
};
} // 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <cstring>
#include "common/assert.h"
#include "core/crypto/ctr_encryption_layer.h"
@@ -10,8 +11,7 @@ namespace Core::Crypto {
CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_,
std::size_t base_offset)
- : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
- iv(16, 0) {}
+ : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR) {}
std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
if (length == 0)
@@ -39,9 +39,8 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
return read + Read(data + read, length - read, offset + read);
}
-void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
- const auto length = std::min(iv_.size(), iv.size());
- iv.assign(iv_.cbegin(), iv_.cbegin() + length);
+void CTREncryptionLayer::SetIV(const IVData& iv_) {
+ iv = iv_;
}
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 @@
#pragma once
-#include <vector>
+#include <array>
+
#include "core/crypto/aes_util.h"
#include "core/crypto/encryption_layer.h"
#include "core/crypto/key_manager.h"
@@ -14,18 +15,20 @@ namespace Core::Crypto {
// Sits on top of a VirtualFile and provides CTR-mode AES decription.
class CTREncryptionLayer : public EncryptionLayer {
public:
+ using IVData = std::array<u8, 16>;
+
CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset);
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
- void SetIV(const std::vector<u8>& iv);
+ void SetIV(const IVData& iv);
private:
std::size_t base_offset;
// Must be mutable as operations modify cipher contexts.
mutable AESCipher<Key128> cipher;
- mutable std::vector<u8> iv;
+ mutable IVData iv{};
void UpdateIV(std::size_t offset) const;
};
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 {
constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
constexpr u64 FULL_TICKET_SIZE = 0x400;
-using namespace Common;
+using Common::AsArray;
-const std::array<SHA256Hash, 2> eticket_source_hashes{
- "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
- "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
+// clang-format off
+constexpr std::array eticket_source_hashes{
+ AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source
+ AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source
};
+// clang-format on
const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
{{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 @@
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
-using namespace Common;
+using Common::AsArray;
namespace Core::Crypto {
@@ -47,105 +47,123 @@ struct Package2Header {
};
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
-const std::array<SHA256Hash, 0x10> source_hashes{
- "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
- "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
- "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
- "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
- "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
- "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
- "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
- "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
- "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
- "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
- "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
- "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
- "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
- "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
- "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
- "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
+// clang-format off
+constexpr std::array source_hashes{
+ AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source
+ AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source
+ AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source
+ AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source
+ AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source
+ AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source
+ AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source
+ AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source
+ AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source
+ AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source
+ AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source
+ AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source
+ AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source
+ AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source
+ AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3
+ AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0
};
-
-const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
- "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
- "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
- "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
- "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
- "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
- "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
+// clang-format on
+
+// clang-format off
+constexpr std::array keyblob_source_hashes{
+ AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00
+ AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01
+ AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02
+ AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03
+ AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04
+ AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F
};
-
-const std::array<SHA256Hash, 0x20> master_key_hashes{
- "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
- "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
- "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
- "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
- "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
- "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
-
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
- "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
+// clang-format on
+
+// clang-format off
+constexpr std::array master_key_hashes{
+ AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00
+ AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01
+ AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02
+ AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03
+ AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04
+ AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05
+ AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06
+ AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07
+
+ AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08
+ AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09
+ AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17
+
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E
+ AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F
};
+// clang-format on
+
+static constexpr u8 CalculateMaxKeyblobSourceHash() {
+ const auto is_zero = [](const auto& data) {
+ // TODO: Replace with std::all_of whenever mingw decides to update their
+ // libraries to include the constexpr variant of it.
+ for (const auto element : data) {
+ if (element != 0) {
+ return false;
+ }
+ }
+ return true;
+ };
-static u8 CalculateMaxKeyblobSourceHash() {
for (s8 i = 0x1F; i >= 0; --i) {
- if (keyblob_source_hashes[i] != SHA256Hash{})
+ if (!is_zero(keyblob_source_hashes[i])) {
return static_cast<u8>(i + 1);
+ }
}
return 0;
@@ -346,10 +364,9 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con
}
static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
- const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
Package2Header temp = header;
AESCipher<Key128> cipher(key, Mode::CTR);
- cipher.SetIV(iv);
+ cipher.SetIV(header.header_ctr);
cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
Op::Decrypt);
if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
@@ -388,7 +405,7 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
auto c = a->ReadAllBytes();
AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
- cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
+ cipher.SetIV(header.section_ctr[1]);
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/core.h"
#include "core/device_memory.h"
-#include "core/memory.h"
namespace Core {
-DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {}
-
+DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {}
DeviceMemory::~DeviceMemory() = default;
} // 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 @@
#pragma once
-#include "common/assert.h"
-#include "common/common_funcs.h"
+#include "common/common_types.h"
#include "common/virtual_buffer.h"
namespace Core {
-class System;
-
namespace DramMemoryMap {
enum : u64 {
Base = 0x80000000ULL,
@@ -26,7 +23,7 @@ enum : u64 {
class DeviceMemory : NonCopyable {
public:
- explicit DeviceMemory(Core::System& system);
+ explicit DeviceMemory();
~DeviceMemory();
template <typename T>
@@ -45,7 +42,6 @@ public:
private:
Common::VirtualBuffer<u8> buffer;
- Core::System& system;
};
} // 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
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
starting_offset);
- std::vector<u8> iv(16);
- for (u8 i = 0; i < 8; ++i)
- iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
+ Core::Crypto::CTREncryptionLayer::IVData iv{};
+ for (std::size_t i = 0; i < 8; ++i) {
+ iv[i] = s_header.raw.section_ctr[8 - i - 1];
+ }
out->SetIV(iv);
return std::static_pointer_cast<VfsFile>(out);
}
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 @@
#pragma once
+#include "common/common_funcs.h"
#include "common/common_types.h"
namespace FileSys {
@@ -11,13 +12,11 @@ namespace FileSys {
enum class Mode : u32 {
Read = 1,
Write = 2,
- ReadWrite = 3,
+ ReadWrite = Read | Write,
Append = 4,
- WriteAppend = 6,
+ WriteAppend = Write | Append,
};
-inline u32 operator&(Mode lhs, Mode rhs) {
- return static_cast<u32>(lhs) & static_cast<u32>(rhs);
-}
+DECLARE_ENUM_FLAG_OPERATORS(Mode)
} // 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 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <array>
#include <cstddef>
#include <cstring>
@@ -66,7 +67,7 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR);
// Calculate AES IV
- std::vector<u8> iv(16);
+ std::array<u8, 16> iv{};
auto subsection_ctr = subsection.ctr;
auto offset_iv = section_offset + base_offset;
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(
}
Core::Memory::TextCheatParser parser;
- return parser.Parse(
- system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));
+ return parser.Parse(system,
+ std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
}
} // 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 {
static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
ContentRecordType type) {
- if (map.find(title_id) == map.end())
- return {};
-
- const auto& cnmt = map.at(title_id);
+ const auto cmnt_iter = map.find(title_id);
+ if (cmnt_iter == map.cend()) {
+ return std::nullopt;
+ }
- const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(),
+ const auto& cnmt = cmnt_iter->second;
+ const auto& content_records = cnmt.GetContentRecords();
+ const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
[type](const ContentRecord& rec) { return rec.type == type; });
- if (iter == cnmt.GetContentRecords().end())
- return {};
+ if (iter == content_records.cend()) {
+ return std::nullopt;
+ }
return std::make_optional(iter->nca_id);
}
@@ -467,14 +470,16 @@ VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType ty
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
const auto meta_iter = meta.find(title_id);
- if (meta_iter != meta.end())
+ if (meta_iter != meta.cend()) {
return meta_iter->second.GetTitleVersion();
+ }
const auto yuzu_meta_iter = yuzu_meta.find(title_id);
- if (yuzu_meta_iter != yuzu_meta.end())
+ if (yuzu_meta_iter != yuzu_meta.cend()) {
return yuzu_meta_iter->second.GetTitleVersion();
+ }
- return {};
+ return std::nullopt;
}
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
@@ -547,56 +552,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex
return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy);
}
-bool RegisteredCache::RemoveExistingEntry(u64 title_id) {
- const auto delete_nca = [this](const NcaID& id) {
- const auto path = GetRelativePathFromNcaID(id, false, true, false);
-
- if (dir->GetFileRelative(path) == nullptr) {
- return false;
- }
-
- Core::Crypto::SHA256Hash hash{};
- mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
- const auto dirname = fmt::format("000000{:02X}", hash[0]);
-
- const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
-
- const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
-
- return res;
- };
-
- // If an entry exists in the registered cache, remove it
- if (HasEntry(title_id, ContentRecordType::Meta)) {
- LOG_INFO(Loader,
- "Previously installed entry (v{}) for title_id={:016X} detected! "
- "Attempting to remove...",
- GetEntryVersion(title_id).value_or(0), title_id);
- // Get all the ncas associated with the current CNMT and delete them
- const auto meta_old_id =
- GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
- const auto program_id =
- GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
- const auto data_id =
- GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
- const auto control_id =
- GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
- const auto html_id =
- GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
- const auto legal_id =
- GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
-
- delete_nca(meta_old_id);
- delete_nca(program_id);
- delete_nca(data_id);
- delete_nca(control_id);
- delete_nca(html_id);
- delete_nca(legal_id);
- return true;
- }
- return false;
-}
-
InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
const auto ncas = nsp.GetNCAsCollapsed();
@@ -692,6 +647,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
}
+bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
+ const auto delete_nca = [this](const NcaID& id) {
+ const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+ const bool isFile = dir->GetFileRelative(path) != nullptr;
+ const bool isDir = dir->GetDirectoryRelative(path) != nullptr;
+
+ if (isFile) {
+ return dir->DeleteFile(path);
+ } else if (isDir) {
+ return dir->DeleteSubdirectoryRecursive(path);
+ }
+
+ return false;
+ };
+
+ // If an entry exists in the registered cache, remove it
+ if (HasEntry(title_id, ContentRecordType::Meta)) {
+ LOG_INFO(Loader,
+ "Previously installed entry (v{}) for title_id={:016X} detected! "
+ "Attempting to remove...",
+ GetEntryVersion(title_id).value_or(0), title_id);
+
+ // Get all the ncas associated with the current CNMT and delete them
+ const auto meta_old_id =
+ GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{});
+ const auto program_id =
+ GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{});
+ const auto data_id =
+ GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{});
+ const auto control_id =
+ GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{});
+ const auto html_id =
+ GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{});
+ const auto legal_id =
+ GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{});
+
+ const auto deleted_meta = delete_nca(meta_old_id);
+ const auto deleted_program = delete_nca(program_id);
+ const auto deleted_data = delete_nca(data_id);
+ const auto deleted_control = delete_nca(control_id);
+ const auto deleted_html = delete_nca(html_id);
+ const auto deleted_legal = delete_nca(legal_id);
+
+ return deleted_meta && (deleted_meta || deleted_program || deleted_data ||
+ deleted_control || deleted_html || deleted_legal);
+ }
+
+ return false;
+}
+
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
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..ec1d54f27 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -155,9 +155,6 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const override;
- // Removes an existing entry based on title id
- bool RemoveExistingEntry(u64 title_id);
-
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
@@ -172,6 +169,9 @@ public:
InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
+ // Removes an existing entry based on title id
+ bool RemoveExistingEntry(u64 title_id) const;
+
private:
template <typename T>
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";
namespace {
-void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
+void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
- "Possibly incorrect SaveDataDescriptor, type is "
+ "Possibly incorrect SaveDataAttribute, type is "
"SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).",
meta.zero_1);
}
if (meta.zero_2 != 0) {
LOG_WARNING(Service_FS,
- "Possibly incorrect SaveDataDescriptor, type is "
+ "Possibly incorrect SaveDataAttribute, type is "
"SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).",
meta.zero_2);
}
if (meta.zero_3 != 0) {
LOG_WARNING(Service_FS,
- "Possibly incorrect SaveDataDescriptor, type is "
+ "Possibly incorrect SaveDataAttribute, type is "
"SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).",
meta.zero_3);
}
@@ -41,33 +41,32 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) {
LOG_WARNING(Service_FS,
- "Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is "
+ "Possibly incorrect SaveDataAttribute, type is SystemSaveData but title_id is "
"non-zero ({:016X}).",
meta.title_id);
}
if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
LOG_WARNING(Service_FS,
- "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
+ "Possibly incorrect SaveDataAttribute, type is DeviceSaveData but user_id is "
"non-zero ({:016X}{:016X})",
meta.user_id[1], meta.user_id[0]);
}
}
-bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) {
- return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage ||
+bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataAttribute& attr) {
+ return attr.type == SaveDataType::CacheStorage || attr.type == SaveDataType::TemporaryStorage ||
(space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
- (desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) &&
- desc.title_id == 0 && desc.save_id == 0);
+ (attr.type == SaveDataType::SaveData || attr.type == SaveDataType::DeviceSaveData) &&
+ attr.title_id == 0 && attr.save_id == 0);
}
} // Anonymous namespace
-std::string SaveDataDescriptor::DebugInfo() const {
- return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
- "save_id={:016X}, "
+std::string SaveDataAttribute::DebugInfo() const {
+ return fmt::format("[title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, type={:02X}, "
"rank={}, index={}]",
- static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
+ title_id, user_id[1], user_id[0], save_id, static_cast<u8>(type),
static_cast<u8>(rank), index);
}
@@ -80,8 +79,8 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
- const SaveDataDescriptor& meta) const {
- PrintSaveDataDescriptorWarnings(meta);
+ const SaveDataAttribute& meta) const {
+ PrintSaveDataAttributeWarnings(meta);
const auto save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
@@ -98,7 +97,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
}
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
- const SaveDataDescriptor& meta) const {
+ const SaveDataAttribute& meta) const {
const auto save_directory =
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 {
TemporaryStorage = 3,
SdCardUser = 4,
ProperSystem = 100,
+ SafeMode = 101,
};
enum class SaveDataType : u8 {
@@ -30,28 +31,50 @@ enum class SaveDataType : u8 {
DeviceSaveData = 3,
TemporaryStorage = 4,
CacheStorage = 5,
+ SystemBcat = 6,
};
enum class SaveDataRank : u8 {
- Primary,
- Secondary,
+ Primary = 0,
+ Secondary = 1,
};
-struct SaveDataDescriptor {
- u64_le title_id;
+enum class SaveDataFlags : u32 {
+ None = (0 << 0),
+ KeepAfterResettingSystemSaveData = (1 << 0),
+ KeepAfterRefurbishment = (1 << 1),
+ KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2),
+ NeedsSecureDelete = (1 << 3),
+};
+
+struct SaveDataAttribute {
+ u64 title_id;
u128 user_id;
- u64_le save_id;
+ u64 save_id;
SaveDataType type;
SaveDataRank rank;
- u16_le index;
+ u16 index;
INSERT_PADDING_BYTES(4);
- u64_le zero_1;
- u64_le zero_2;
- u64_le zero_3;
+ u64 zero_1;
+ u64 zero_2;
+ u64 zero_3;
std::string DebugInfo() const;
};
-static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size.");
+static_assert(sizeof(SaveDataAttribute) == 0x40, "SaveDataAttribute has incorrect size.");
+
+struct SaveDataExtraData {
+ SaveDataAttribute attr;
+ u64 owner_id;
+ s64 timestamp;
+ SaveDataFlags flags;
+ INSERT_PADDING_BYTES(4);
+ s64 available_size;
+ s64 journal_size;
+ s64 commit_id;
+ std::array<u8, 0x190> unused;
+};
+static_assert(sizeof(SaveDataExtraData) == 0x200, "SaveDataExtraData has incorrect size.");
struct SaveDataSize {
u64 normal;
@@ -64,8 +87,8 @@ public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
- ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
- ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
+ ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
+ ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const;
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
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..d1de63f20 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
+#include <vector>
+
#include "common/swap.h"
#include "core/file_sys/system_archive/time_zone_binary.h"
#include "core/file_sys/vfs_vector.h"
@@ -615,31 +618,36 @@ static constexpr std::array<u8, 9633> LOCATION_NAMES{
0x0a};
static VirtualFile GenerateDefaultTimeZoneFile() {
- struct {
+ struct TimeZoneInfo {
s64_be at;
- INSERT_PADDING_BYTES(7);
+ std::array<u8, 7> padding1;
std::array<char, 4> time_zone_chars;
- INSERT_PADDING_BYTES(2);
+ std::array<u8, 2> padding2;
std::array<char, 6> time_zone_name;
- } time_zone_info{};
+ };
- const VirtualFile file{std::make_shared<VectorVfsFile>(
- std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(time_zone_info)),
+ VirtualFile file{std::make_shared<VectorVfsFile>(
+ std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(TimeZoneInfo)),
"GMT")};
- Service::Time::TimeZone::TzifHeader header{};
- header.magic = 0x545a6966;
- header.version = 0x32;
- header.ttis_gmt_count = 0x1;
- header.ttis_std_count = 0x1;
- header.time_count = 0x1;
- header.type_count = 0x1;
- header.char_count = 0x4;
+ const Service::Time::TimeZone::TzifHeader header{
+ .magic = 0x545a6966,
+ .version = 0x32,
+ .ttis_gmt_count = 1,
+ .ttis_std_count = 1,
+ .time_count = 1,
+ .type_count = 1,
+ .char_count = 4,
+ };
file->WriteObject(header, 0);
- time_zone_info.at = 0xf8;
- time_zone_info.time_zone_chars = {'G', 'M', 'T', '\0'};
- time_zone_info.time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'};
+ const TimeZoneInfo time_zone_info{
+ .at = 0xf8,
+ .padding1 = {},
+ .time_zone_chars = {'G', 'M', 'T', '\0'},
+ .padding2 = {},
+ .time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'},
+ };
file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader));
return file;
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) {
std::string mode_str;
// Calculate the correct open mode for the file.
- if (mode & Mode::Read && mode & Mode::Write) {
- if (mode & Mode::Append)
+ if (True(mode & Mode::Read) && True(mode & Mode::Write)) {
+ if (True(mode & Mode::Append)) {
mode_str = "a+";
- else
+ } else {
mode_str = "r+";
+ }
} else {
- if (mode & Mode::Read)
+ if (True(mode & Mode::Read)) {
mode_str = "r";
- else if (mode & Mode::Append)
+ } else if (True(mode & Mode::Append)) {
mode_str = "a";
- else if (mode & Mode::Write)
+ } else if (True(mode & Mode::Write)) {
mode_str = "w";
- else
+ } else {
UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode));
+ }
}
mode_str += "b";
@@ -73,8 +75,9 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
}
}
- if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0)
+ if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) {
FileUtil::CreateEmptyFile(path);
+ }
auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str());
cache[path] = backing;
@@ -247,11 +250,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const {
}
bool RealVfsFile::IsWritable() const {
- return (perms & Mode::WriteAppend) != 0;
+ return True(perms & Mode::WriteAppend);
}
bool RealVfsFile::IsReadable() const {
- return (perms & Mode::ReadWrite) != 0;
+ return True(perms & Mode::ReadWrite);
}
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&
path_components(FileUtil::SplitPathComponents(path)),
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {
- if (!FileUtil::Exists(path) && perms & Mode::WriteAppend)
+ if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) {
FileUtil::CreateDir(path);
+ }
}
RealVfsDirectory::~RealVfsDirectory() = default;
@@ -371,11 +375,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories()
}
bool RealVfsDirectory::IsWritable() const {
- return (perms & Mode::WriteAppend) != 0;
+ return True(perms & Mode::WriteAppend);
}
bool RealVfsDirectory::IsReadable() const {
- return (perms & Mode::ReadWrite) != 0;
+ return True(perms & Mode::ReadWrite);
}
std::string RealVfsDirectory::GetName() const {
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)
NAX::~NAX() = default;
Loader::ResultStatus NAX::Parse(std::string_view path) {
- if (file->ReadObject(header.get()) != sizeof(NAXHeader))
+ if (file == nullptr) {
+ return Loader::ResultStatus::ErrorNullFile;
+ }
+ if (file->ReadObject(header.get()) != sizeof(NAXHeader)) {
return Loader::ResultStatus::ErrorBadNAXHeader;
-
- if (header->magic != Common::MakeMagic('N', 'A', 'X', '0'))
+ }
+ if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) {
return Loader::ResultStatus::ErrorBadNAXHeader;
-
- if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size)
+ }
+ if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) {
return Loader::ResultStatus::ErrorIncorrectNAXFileSize;
+ }
keys.DeriveSDSeedLazy();
std::array<Core::Crypto::Key256, 2> sd_keys{};
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) {
template <typename T>
void ResponseBuilder::PushRaw(const T& value) {
+ static_assert(std::is_trivially_copyable_v<T>,
+ "It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
@@ -384,6 +386,8 @@ inline s32 RequestParser::Pop() {
template <typename T>
void RequestParser::PopRaw(T& value) {
+ static_assert(std::is_trivially_copyable_v<T>,
+ "It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
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
do {
current_value = monitor.ExclusiveRead32(current_core, address);
- if (current_value != value) {
+ if (current_value != static_cast<u32>(value)) {
return ERR_INVALID_STATE;
}
current_value++;
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 @@
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
+#include "common/concepts.h"
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/object.h"
@@ -193,23 +194,24 @@ public:
/* Helper function to write a buffer using the appropriate buffer descriptor
*
- * @tparam ContiguousContainer an arbitrary container that satisfies the
- * ContiguousContainer concept in the C++ standard library.
+ * @tparam T an arbitrary container that satisfies the
+ * ContiguousContainer concept in the C++ standard library or a trivially copyable type.
*
- * @param container The container to write the data of into a buffer.
+ * @param data The container/data to write into a buffer.
* @param buffer_index The buffer in particular to write to.
*/
- template <typename ContiguousContainer,
- typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>>
- std::size_t WriteBuffer(const ContiguousContainer& container,
- std::size_t buffer_index = 0) const {
- using ContiguousType = typename ContiguousContainer::value_type;
-
- static_assert(std::is_trivially_copyable_v<ContiguousType>,
- "Container to WriteBuffer must contain trivially copyable objects");
-
- return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType),
- buffer_index);
+ template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
+ std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
+ if constexpr (Common::IsSTLContainer<T>) {
+ using ContiguousType = typename T::value_type;
+ static_assert(std::is_trivially_copyable_v<ContiguousType>,
+ "Container to WriteBuffer must contain trivially copyable objects");
+ return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType),
+ buffer_index);
+ } else {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
+ return WriteBuffer(&data, sizeof(T), buffer_index);
+ }
}
/// Helper function to get the size of the input buffer
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index f93e5e4b0..fc656d613 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -131,7 +131,8 @@ u32 GlobalScheduler::SelectThreads() {
u32 cores_needing_context_switch{};
for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
Scheduler& sched = kernel.Scheduler(core);
- ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core);
+ ASSERT(top_threads[core] == nullptr ||
+ static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
if (update_thread(top_threads[core], sched)) {
cores_needing_context_switch |= (1ul << core);
}
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:
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
- std::array<u8, sizeof(ProfileData)> raw_data;
- std::memcpy(raw_data.data(), &data, sizeof(ProfileData));
- ctx.WriteBuffer(raw_data);
+ ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
@@ -333,7 +331,7 @@ protected:
std::vector<u8> buffer(size);
image.ReadBytes(buffer.data(), buffer.size());
- ctx.WriteBuffer(buffer.data(), buffer.size());
+ ctx.WriteBuffer(buffer);
rb.Push<u32>(size);
}
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() {
/// internal management of the users profiles
std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
if (user_count >= MAX_USERS) {
- return {};
+ return std::nullopt;
}
profiles[user_count] = profile;
return user_count++;
@@ -101,13 +101,14 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
[&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) {
return ERROR_USER_ALREADY_EXISTS;
}
- ProfileInfo profile;
- profile.user_uuid = uuid;
- profile.username = username;
- profile.data = {};
- profile.creation_time = 0x0;
- profile.is_open = false;
- return AddUser(profile);
+
+ return AddUser({
+ .user_uuid = uuid,
+ .username = username,
+ .creation_time = 0,
+ .data = {},
+ .is_open = false,
+ });
}
/// 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)
std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
if (index >= MAX_USERS) {
- return {};
+ return std::nullopt;
}
return profiles[index].user_uuid;
@@ -135,13 +136,13 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
/// Returns a users profile index based on their user id.
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
- return {};
+ return std::nullopt;
}
const auto iter = std::find_if(profiles.begin(), profiles.end(),
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
if (iter == profiles.end()) {
- return {};
+ return std::nullopt;
}
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
@@ -339,7 +340,13 @@ void ProfileManager::ParseUserSaveFile() {
continue;
}
- AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false});
+ AddUser({
+ .user_uuid = user.uuid,
+ .username = user.username,
+ .creation_time = user.timestamp,
+ .data = user.extra_data,
+ .is_open = false,
+ });
}
std::stable_partition(profiles.begin(), profiles.end(),
@@ -350,11 +357,13 @@ void ProfileManager::WriteUserSaveFile() {
ProfileDataRaw raw{};
for (std::size_t i = 0; i < MAX_USERS; ++i) {
- raw.users[i].username = profiles[i].username;
- raw.users[i].uuid2 = profiles[i].user_uuid;
- raw.users[i].uuid = profiles[i].user_uuid;
- raw.users[i].timestamp = profiles[i].creation_time;
- raw.users[i].extra_data = profiles[i].data;
+ raw.users[i] = {
+ .uuid = profiles[i].user_uuid,
+ .uuid2 = profiles[i].user_uuid,
+ .timestamp = profiles[i].creation_time,
+ .username = profiles[i].username,
+ .extra_data = profiles[i].data,
+ };
}
const auto raw_path =
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index ceed20609..55a1edf1a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1342,12 +1342,12 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
- FileSys::SaveDataDescriptor descriptor{};
- descriptor.title_id = system.CurrentProcess()->GetTitleID();
- descriptor.user_id = user_id;
- descriptor.type = FileSys::SaveDataType::SaveData;
+ FileSys::SaveDataAttribute attribute{};
+ attribute.title_id = system.CurrentProcess()->GetTitleID();
+ attribute.user_id = user_id;
+ attribute.type = FileSys::SaveDataType::SaveData;
const auto res = system.GetFileSystemController().CreateSaveData(
- FileSys::SaveDataSpaceId::NandUser, descriptor);
+ FileSys::SaveDataSpaceId::NandUser, attribute);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res.Code());
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 106e89743..dd80dd1dc 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -71,7 +71,7 @@ public:
stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
audio_params.channel_count, std::move(unique_name),
- [=]() { buffer_event.writable->Signal(); });
+ [this] { buffer_event.writable->Signal(); });
}
private:
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:
if (performance) {
rb.Push<u64>(*performance);
}
- ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
+ ctx.WriteBuffer(samples);
}
bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
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:
void GetImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
- ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl));
+ ctx.WriteBuffer(impl);
IPC::ResponseBuilder rb{ctx, 2};
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:
return;
}
- ctx.WriteBuffer(key.data(), key.size());
+ ctx.WriteBuffer(key);
IPC::ResponseBuilder rb{ctx, 2};
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(
}
ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
- FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const {
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const {
LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
static_cast<u8>(space), save_struct.DebugInfo());
@@ -323,15 +323,15 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
}
ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
- FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const {
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const {
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
- static_cast<u8>(space), descriptor.DebugInfo());
+ static_cast<u8>(space), attribute.DebugInfo());
if (save_data_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
- return save_data_factory->Open(space, descriptor);
+ return save_data_factory->Open(space, attribute);
}
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;
enum class SaveDataType : u8;
enum class StorageId : u8;
-struct SaveDataDescriptor;
+struct SaveDataAttribute;
struct SaveDataSize;
} // namespace FileSys
@@ -69,9 +69,9 @@ public:
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type) const;
ResultVal<FileSys::VirtualDir> CreateSaveData(
- FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
ResultVal<FileSys::VirtualDir> OpenSaveData(
- FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
+ FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const;
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
ResultVal<FileSys::VirtualDir> OpenSDMC() const;
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)
{67, nullptr, "FindSaveDataWithFilter"},
{68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
{69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"},
- {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
- {71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
+ {70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
+ {71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
{80, nullptr, "OpenSaveDataMetaFile"},
{81, nullptr, "OpenSaveDataTransferManager"},
{82, nullptr, "OpenSaveDataTransferManagerVersion2"},
@@ -812,7 +812,7 @@ void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
+ auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
@@ -826,17 +826,18 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
}
void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
- LOG_INFO(Service_FS, "called.");
+ IPC::RequestParser rp{ctx};
struct Parameters {
- FileSys::SaveDataSpaceId save_data_space_id;
- FileSys::SaveDataDescriptor descriptor;
+ FileSys::SaveDataSpaceId space_id;
+ FileSys::SaveDataAttribute attribute;
};
- IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
- auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
+ LOG_INFO(Service_FS, "called.");
+
+ auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute);
if (dir.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
@@ -844,13 +845,18 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
}
FileSys::StorageId id;
- if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
+
+ switch (parameters.space_id) {
+ case FileSys::SaveDataSpaceId::NandUser:
id = FileSys::StorageId::NandUser;
- } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
- parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
+ break;
+ case FileSys::SaveDataSpaceId::SdCardSystem:
+ case FileSys::SaveDataSpaceId::SdCardUser:
id = FileSys::StorageId::SdCard;
- } else {
+ break;
+ case FileSys::SaveDataSpaceId::NandSystem:
id = FileSys::StorageId::NandSystem;
+ break;
}
auto filesystem =
@@ -876,22 +882,31 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
}
-void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- log_mode = rp.PopEnum<LogMode>();
-
- LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
+void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_FS, "(STUBBED) called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_FS, "called");
+void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
+ Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ struct Parameters {
+ FileSys::SaveDataSpaceId space_id;
+ FileSys::SaveDataAttribute attribute;
+ };
+
+ const auto parameters = rp.PopRaw<Parameters>();
+ // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData
+ constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None);
+
+ LOG_WARNING(Service_FS, "(STUBBED) called, flags={}", flags);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.PushEnum(log_mode);
+ rb.Push(flags);
}
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
@@ -966,6 +981,24 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
}
+void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ log_mode = rp.PopEnum<LogMode>();
+
+ LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(log_mode);
+}
+
void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
const auto raw = ctx.ReadBuffer();
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:
void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
- void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
- void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
+ void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx);
+ void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
+ void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
+ void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
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:
ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress,
u64 size) const {
- for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) {
+ for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->PageTable()};
const VAddr addr{GetRandomMapRegion(page_table, size)};
const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)};
@@ -331,8 +331,7 @@ public:
ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size,
VAddr bss_addr, std::size_t bss_size, std::size_t size) const {
-
- for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) {
+ for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->PageTable()};
VAddr addr{};
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:
const u32 array_size = rp.Pop<u32>();
LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
- ctx.WriteBuffer(&device_handle, sizeof(device_handle));
+ ctx.WriteBuffer(device_handle);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -220,7 +220,7 @@ private:
tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
tag_info.tag_type = 2;
- ctx.WriteBuffer(&tag_info, sizeof(TagInfo));
+ ctx.WriteBuffer(tag_info);
rb.Push(RESULT_SUCCESS);
}
@@ -237,7 +237,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
auto amiibo = nfp_interface.GetAmiiboBuffer();
- ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info));
+ ctx.WriteBuffer(amiibo.model_info);
rb.Push(RESULT_SUCCESS);
}
@@ -283,7 +283,7 @@ private:
CommonInfo common_info{};
common_info.application_area_size = 0;
- ctx.WriteBuffer(&common_info, sizeof(CommonInfo));
+ ctx.WriteBuffer(common_info);
IPC::ResponseBuilder rb{ctx, 2};
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 @@
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
+
namespace NvErrCodes {
-enum {
- InvalidNmapHandle = -22,
-};
-}
+constexpr u32 Success{};
+constexpr u32 OutOfMemory{static_cast<u32>(-12)};
+constexpr u32 InvalidInput{static_cast<u32>(-22)};
+} // namespace NvErrCodes
nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: 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:
break;
}
- if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)
+ if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) {
return Remap(input, output);
+ }
UNIMPLEMENTED_MSG("Unimplemented ioctl command");
return 0;
@@ -59,6 +61,7 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlInitalizeEx params{};
std::memcpy(&params, input.data(), input.size());
+
LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
return 0;
@@ -67,53 +70,61 @@ u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& ou
u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocSpace params{};
std::memcpy(&params, input.data(), input.size());
+
LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
params.page_size, params.flags);
- auto& gpu = system.GPU();
- const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
- if (params.flags & 1) {
- params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1);
+ const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
+ if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) {
+ params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size);
} else {
- params.offset = gpu.MemoryManager().AllocateSpace(size, params.align);
+ params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
+ }
+
+ auto result{NvErrCodes::Success};
+ if (!params.offset) {
+ LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
+ result = NvErrCodes::OutOfMemory;
}
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return result;
}
u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
- std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry);
+ const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries);
+ LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
+ auto result{NvErrCodes::Success};
std::vector<IoctlRemapEntry> entries(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
- auto& gpu = system.GPU();
for (const auto& entry : entries) {
- LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
- entry.offset, entry.nvmap_handle, entry.pages);
- GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10;
- auto object = nvmap_dev->GetObject(entry.nvmap_handle);
+ LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
+ entry.offset, entry.nvmap_handle, entry.pages);
+
+ const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
if (!object) {
- LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
- std::memcpy(output.data(), entries.data(), output.size());
- return static_cast<u32>(NvErrCodes::InvalidNmapHandle);
+ LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
+ result = NvErrCodes::InvalidInput;
+ break;
}
- ASSERT(object->status == nvmap::Object::Status::Allocated);
+ const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10};
+ const auto size{static_cast<u64>(entry.pages) << 0x10};
+ const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10};
+ const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)};
- const u64 size = static_cast<u64>(entry.pages) << 0x10;
- ASSERT(size <= object->size);
- const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10;
-
- const GPUVAddr returned =
- gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size);
- ASSERT(returned == offset);
+ if (!addr) {
+ LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
+ result = NvErrCodes::InvalidInput;
+ break;
+ }
}
+
std::memcpy(output.data(), entries.data(), output.size());
- return 0;
+ return result;
}
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
params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
params.offset);
- if (!params.nvmap_handle) {
- return 0;
+ const auto object{nvmap_dev->GetObject(params.nvmap_handle)};
+ if (!object) {
+ LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
+ std::memcpy(output.data(), &params, output.size());
+ return NvErrCodes::InvalidInput;
}
- auto object = nvmap_dev->GetObject(params.nvmap_handle);
- ASSERT(object);
-
- // We can only map objects that have already been assigned a CPU address.
- ASSERT(object->status == nvmap::Object::Status::Allocated);
-
- ASSERT(params.buffer_offset == 0);
-
// The real nvservices doesn't make a distinction between handles and ids, and
// object can only have one handle and it will be the same as its id. Assert that this is the
// case to prevent unexpected behavior.
ASSERT(object->id == params.nvmap_handle);
-
auto& gpu = system.GPU();
- if (params.flags & 1) {
- params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size);
- } else {
- params.offset = gpu.MemoryManager().MapBufferEx(object->addr, object->size);
+ u64 page_size{params.page_size};
+ if (!page_size) {
+ page_size = object->align;
+ }
+
+ if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) {
+ if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) {
+ const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)};
+ const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)};
+
+ if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) {
+ LOG_CRITICAL(Service_NVDRV,
+ "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, "
+ "mapping_size = {}, offset={}",
+ params.flags, params.nvmap_handle, params.buffer_offset,
+ params.mapping_size, params.offset);
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvErrCodes::InvalidInput;
+ }
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvErrCodes::Success;
+ } else {
+ LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvErrCodes::InvalidInput;
+ }
}
- // Create a new mapping entry for this operation.
- ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(),
- "Offset is already mapped");
+ // We can only map objects that have already been assigned a CPU address.
+ ASSERT(object->status == nvmap::Object::Status::Allocated);
+
+ const auto physical_address{object->addr + params.buffer_offset};
+ u64 size{params.mapping_size};
+ if (!size) {
+ size = object->size;
+ }
- BufferMapping mapping{};
- mapping.nvmap_handle = params.nvmap_handle;
- mapping.offset = params.offset;
- mapping.size = object->size;
+ const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None};
+ if (is_alloc) {
+ params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size);
+ } else {
+ params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
+ }
- buffer_mappings[params.offset] = mapping;
+ auto result{NvErrCodes::Success};
+ if (!params.offset) {
+ LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
+ result = NvErrCodes::InvalidInput;
+ } else {
+ AddBufferMap(params.offset, size, physical_address, is_alloc);
+ }
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return result;
}
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
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
- const auto itr = buffer_mappings.find(params.offset);
- if (itr == buffer_mappings.end()) {
- LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset);
- // Hardware tests shows that unmapping an already unmapped buffer always returns successful
- // and doesn't fail.
- return 0;
+ if (const auto size{RemoveBufferMap(params.offset)}; size) {
+ system.GPU().MemoryManager().Unmap(params.offset, *size);
+ } else {
+ LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset);
}
- params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size);
- buffer_mappings.erase(itr->second.offset);
-
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvErrCodes::Success;
}
u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
+
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
channel = params.fd;
@@ -199,6 +238,7 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou
u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
+
LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
params.buf_size);
@@ -210,9 +250,43 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
params.regions[1].offset = 0x04000000;
params.regions[1].page_size = 0x10000;
params.regions[1].pages = 0x1bffff;
+
// TODO(ogniK): This probably can stay stubbed but should add support way way later
+
std::memcpy(output.data(), &params, output.size());
return 0;
}
+std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {
+ const auto end{buffer_mappings.upper_bound(gpu_addr)};
+ for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) {
+ if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) {
+ return iter->second;
+ }
+ }
+
+ return {};
+}
+
+void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
+ bool is_allocated) {
+ buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated};
+}
+
+std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
+ if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) {
+ std::size_t size{};
+
+ if (iter->second.IsAllocated()) {
+ size = iter->second.Size();
+ }
+
+ buffer_mappings.erase(iter);
+
+ return size;
+ }
+
+ return {};
+}
+
} // 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 @@
#pragma once
+#include <map>
#include <memory>
-#include <unordered_map>
+#include <optional>
#include <vector>
+
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -15,6 +18,13 @@ namespace Service::Nvidia::Devices {
class nvmap;
+enum class AddressSpaceFlags : u32 {
+ None = 0x0,
+ FixedOffset = 0x1,
+ Remap = 0x100,
+};
+DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags);
+
class nvhost_as_gpu final : public nvdevice {
public:
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
@@ -25,6 +35,45 @@ public:
IoctlVersion version) override;
private:
+ class BufferMap final {
+ public:
+ constexpr BufferMap() = default;
+
+ constexpr BufferMap(GPUVAddr start_addr, std::size_t size)
+ : start_addr{start_addr}, end_addr{start_addr + size} {}
+
+ constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr,
+ bool is_allocated)
+ : start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr},
+ is_allocated{is_allocated} {}
+
+ constexpr VAddr StartAddr() const {
+ return start_addr;
+ }
+
+ constexpr VAddr EndAddr() const {
+ return end_addr;
+ }
+
+ constexpr std::size_t Size() const {
+ return end_addr - start_addr;
+ }
+
+ constexpr VAddr CpuAddr() const {
+ return cpu_addr;
+ }
+
+ constexpr bool IsAllocated() const {
+ return is_allocated;
+ }
+
+ private:
+ GPUVAddr start_addr{};
+ GPUVAddr end_addr{};
+ VAddr cpu_addr{};
+ bool is_allocated{};
+ };
+
enum class IoctlCommand : u32_le {
IocInitalizeExCommand = 0x40284109,
IocAllocateSpaceCommand = 0xC0184102,
@@ -49,7 +98,7 @@ private:
struct IoctlAllocSpace {
u32_le pages;
u32_le page_size;
- u32_le flags;
+ AddressSpaceFlags flags;
INSERT_PADDING_WORDS(1);
union {
u64_le offset;
@@ -69,18 +118,18 @@ private:
static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
struct IoctlMapBufferEx {
- u32_le flags; // bit0: fixed_offset, bit2: cacheable
- u32_le kind; // -1 is default
+ AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable
+ u32_le kind; // -1 is default
u32_le nvmap_handle;
u32_le page_size; // 0 means don't care
- u64_le buffer_offset;
+ s64_le buffer_offset;
u64_le mapping_size;
- u64_le offset;
+ s64_le offset;
};
static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
struct IoctlUnmapBuffer {
- u64_le offset;
+ s64_le offset;
};
static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
@@ -106,15 +155,6 @@ private:
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
"IoctlGetVaRegions is incorrect size");
- struct BufferMapping {
- u64 offset;
- u64 size;
- u32 nvmap_handle;
- };
-
- /// Map containing the nvmap object mappings in GPU memory.
- std::unordered_map<u64, BufferMapping> buffer_mappings;
-
u32 channel{};
u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
@@ -125,7 +165,14 @@ private:
u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
+ std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
+ void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
+ std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
+
std::shared_ptr<nvmap> nvmap_dev;
+
+ // This is expected to be ordered, therefore we must use a map, not unordered_map
+ std::map<GPUVAddr, BufferMap> buffer_mappings;
};
} // 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 {
};
}
-nvmap::nvmap(Core::System& system) : nvdevice(system) {}
+nvmap::nvmap(Core::System& system) : nvdevice(system) {
+ // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
+ // represent this.
+ CreateObject(0);
+}
+
nvmap::~nvmap() = default;
VAddr nvmap::GetObjectAddress(u32 handle) const {
@@ -50,6 +55,21 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<
return 0;
}
+u32 nvmap::CreateObject(u32 size) {
+ // Create a new nvmap object and obtain a handle to it.
+ auto object = std::make_shared<Object>();
+ object->id = next_id++;
+ object->size = size;
+ object->status = Object::Status::Created;
+ object->refcount = 1;
+
+ const u32 handle = next_handle++;
+
+ handles.insert_or_assign(handle, std::move(object));
+
+ return handle;
+}
+
u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -59,17 +79,8 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_ERROR(Service_NVDRV, "Size is 0");
return static_cast<u32>(NvErrCodes::InvalidValue);
}
- // Create a new nvmap object and obtain a handle to it.
- auto object = std::make_shared<Object>();
- object->id = next_id++;
- object->size = params.size;
- object->status = Object::Status::Created;
- object->refcount = 1;
-
- u32 handle = next_handle++;
- handles[handle] = std::move(object);
- params.handle = handle;
+ params.handle = CreateObject(params.size);
std::memcpy(output.data(), &params, sizeof(params));
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:
private:
/// Id to use for the next handle that is created.
- u32 next_handle = 1;
+ u32 next_handle = 0;
/// Id to use for the next object that is created.
- u32 next_id = 1;
+ u32 next_id = 0;
/// Mapping of currently allocated handles to the objects they represent.
std::unordered_map<u32, std::shared_ptr<Object>> handles;
@@ -119,6 +119,8 @@ private:
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
+ u32 CreateObject(u32 size);
+
u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
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) {
if (ctrl.must_delay) {
ctrl.fresh_call = false;
- ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout,
- [=](std::shared_ptr<Kernel::Thread> thread,
- Kernel::HLERequestContext& ctx,
- Kernel::ThreadWakeupReason reason) {
- IoctlCtrl ctrl2{ctrl};
- std::vector<u8> tmp_output = output;
- std::vector<u8> tmp_output2 = output2;
- u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
- tmp_output2, ctrl2, version);
- ctx.WriteBuffer(tmp_output, 0);
- if (version == IoctlVersion::Version3) {
- ctx.WriteBuffer(tmp_output2, 1);
- }
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push(result);
- },
- nvdrv->GetEventWriteable(ctrl.event_id));
+ ctx.SleepClientThread(
+ "NVServices::DelayedResponse", ctrl.timeout,
+ [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
+ Kernel::ThreadWakeupReason reason) {
+ IoctlCtrl ctrl2{ctrl};
+ std::vector<u8> tmp_output = output;
+ std::vector<u8> tmp_output2 = output2;
+ const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
+ tmp_output2, ctrl2, version);
+ ctx_.WriteBuffer(tmp_output, 0);
+ if (version == IoctlVersion::Version3) {
+ ctx_.WriteBuffer(tmp_output2, 1);
+ }
+ IPC::ResponseBuilder rb{ctx_, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(ioctl_result);
+ },
+ nvdrv->GetEventWriteable(ctrl.event_id));
} else {
ctx.WriteBuffer(output);
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;
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
- Buffer buffer{};
- buffer.slot = slot;
- buffer.igbp_buffer = igbp_buffer;
- buffer.status = Buffer::Status::Free;
free_buffers.push_back(slot);
+ queue.push_back({
+ .slot = slot,
+ .status = Buffer::Status::Free,
+ .igbp_buffer = igbp_buffer,
+ });
- queue.emplace_back(buffer);
buffer_wait_event.writable->Signal();
}
@@ -38,7 +38,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
u32 height) {
if (free_buffers.empty()) {
- return {};
+ return std::nullopt;
}
auto f_itr = free_buffers.begin();
@@ -69,7 +69,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue
}
if (itr == queue.end()) {
- return {};
+ return std::nullopt;
}
itr->status = Buffer::Status::Dequeued;
@@ -103,14 +103,15 @@ std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::Ac
auto itr = queue.end();
// Iterate to find a queued buffer matching the requested slot.
while (itr == queue.end() && !queue_sequence.empty()) {
- u32 slot = queue_sequence.front();
+ const u32 slot = queue_sequence.front();
itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) {
return buffer.status == Buffer::Status::Queued && buffer.slot == slot;
});
queue_sequence.pop_front();
}
- if (itr == queue.end())
- return {};
+ if (itr == queue.end()) {
+ return std::nullopt;
+ }
itr->status = Buffer::Status::Acquired;
return *itr;
}
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) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- ctx.WriteBuffer(&layout, sizeof(KeyboardLayout));
+ ctx.WriteBuffer(layout);
}
} // Anonymous namespace
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 @@
#include <type_traits>
#include <unordered_map>
+#include "common/concepts.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
@@ -56,10 +57,8 @@ public:
ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
- template <typename T>
+ template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
std::shared_ptr<T> GetService(const std::string& service_name) const {
- static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
- "Not a base of ServiceFrameworkBase");
auto service = registered_services.find(service_name);
if (service == registered_services.end()) {
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) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
+ ctx.WriteBuffer(clock_snapshot);
}
void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) {
@@ -313,7 +313,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
+ ctx.WriteBuffer(clock_snapshot);
}
void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
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) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(1); // Number of times we're returning
- ctx.WriteBuffer(&posix_time, sizeof(s64));
+ ctx.WriteBuffer(posix_time);
}
void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
@@ -164,7 +164,7 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(1); // Number of times we're returning
- ctx.WriteBuffer(&posix_time, sizeof(s64));
+ ctx.WriteBuffer(posix_time);
}
} // namespace Service::Time
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 825d11a3f..480d34725 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -548,8 +548,8 @@ private:
// Wait the current thread until a buffer becomes available
ctx.SleepClientThread(
"IHOSBinderDriver::DequeueBuffer", UINT64_MAX,
- [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
- Kernel::ThreadWakeupReason reason) {
+ [=, this](std::shared_ptr<Kernel::Thread> thread,
+ Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
const auto guard = nv_flinger->Lock();
auto& buffer_queue = nv_flinger->FindBufferQueue(id);
@@ -1199,6 +1199,23 @@ private:
}
}
+ void GetIndirectLayerImageRequiredMemoryInfo(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto width = rp.Pop<u64>();
+ const auto height = rp.Pop<u64>();
+ LOG_DEBUG(Service_VI, "called width={}, height={}", width, height);
+
+ constexpr std::size_t base_size = 0x20000;
+ constexpr std::size_t alignment = 0x1000;
+ const auto texture_size = width * height * 4;
+ const auto out_size = (texture_size + base_size - 1) / base_size * base_size;
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(out_size);
+ rb.Push(alignment);
+ }
+
static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) {
switch (mode) {
case NintendoScaleMode::None:
@@ -1243,7 +1260,8 @@ IApplicationDisplayService::IApplicationDisplayService(
{2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
{2450, nullptr, "GetIndirectLayerImageMap"},
{2451, nullptr, "GetIndirectLayerImageCropMap"},
- {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"},
+ {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
+ "GetIndirectLayerImageRequiredMemoryInfo"},
{5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
{5203, nullptr, "GetDisplayVsyncEventForDebug"},
};
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 @@
// Refer to the license.txt file included.
#include <memory>
+#include <optional>
#include <ostream>
#include <string>
+#include "common/concepts.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -21,27 +23,41 @@
namespace Loader {
-FileType IdentifyFile(FileSys::VirtualFile file) {
- FileType type;
-
-#define CHECK_TYPE(loader) \
- type = AppLoader_##loader::IdentifyType(file); \
- if (FileType::Error != type) \
- return type;
+namespace {
- CHECK_TYPE(DeconstructedRomDirectory)
- CHECK_TYPE(ELF)
- CHECK_TYPE(NSO)
- CHECK_TYPE(NRO)
- CHECK_TYPE(NCA)
- CHECK_TYPE(XCI)
- CHECK_TYPE(NAX)
- CHECK_TYPE(NSP)
- CHECK_TYPE(KIP)
+template <Common::DerivedFrom<AppLoader> T>
+std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
+ const auto file_type = T::IdentifyType(file);
+ if (file_type != FileType::Error) {
+ return file_type;
+ }
+ return std::nullopt;
+}
-#undef CHECK_TYPE
+} // namespace
- return FileType::Unknown;
+FileType IdentifyFile(FileSys::VirtualFile file) {
+ if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
+ return *romdir_type;
+ } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
+ return *elf_type;
+ } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
+ return *nso_type;
+ } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
+ return *nro_type;
+ } else if (const auto nca_type = IdentifyFileLoader<AppLoader_NCA>(file)) {
+ return *nca_type;
+ } else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) {
+ return *xci_type;
+ } else if (const auto nax_type = IdentifyFileLoader<AppLoader_NAX>(file)) {
+ return *nax_type;
+ } else if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) {
+ return *nsp_type;
+ } else if (const auto kip_type = IdentifyFileLoader<AppLoader_KIP>(file)) {
+ return *kip_type;
+ } else {
+ return FileType::Unknown;
+ }
}
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 {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer != nullptr) {
// NOTE: Avoid adding any extra logic to this fast-path block
- T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]);
+ auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]);
return Common::AtomicCompareAndSwap(pointer, data, expected);
}
@@ -720,9 +720,8 @@ struct Memory::Impl {
case Common::PageType::RasterizerCachedMemory: {
u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
system.GPU().InvalidateRegion(vaddr, sizeof(T));
- T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr);
+ auto* pointer = reinterpret_cast<volatile T*>(&host_ptr);
return Common::AtomicCompareAndSwap(pointer, data, expected);
- break;
}
default:
UNREACHABLE();
@@ -734,7 +733,7 @@ struct Memory::Impl {
u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer != nullptr) {
// NOTE: Avoid adding any extra logic to this fast-path block
- u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]);
+ auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]);
return Common::AtomicCompareAndSwap(pointer, data, expected);
}
@@ -750,9 +749,8 @@ struct Memory::Impl {
case Common::PageType::RasterizerCachedMemory: {
u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
system.GPU().InvalidateRegion(vaddr, sizeof(u128));
- u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr);
+ auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr);
return Common::AtomicCompareAndSwap(pointer, data, expected);
- break;
}
default:
UNREACHABLE();
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) {
switch (opcode_type) {
case CheatVmOpcodeType::StoreStatic: {
- StoreStaticOpcode store_static{};
// 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
// Read additional words.
const u32 second_dword = GetNextDword();
- store_static.bit_width = (first_dword >> 24) & 0xF;
- store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
- store_static.offset_register = ((first_dword >> 16) & 0xF);
- store_static.rel_address =
- (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
- store_static.value = GetNextVmInt(store_static.bit_width);
- opcode.opcode = store_static;
+ const u32 bit_width = (first_dword >> 24) & 0xF;
+
+ opcode.opcode = StoreStaticOpcode{
+ .bit_width = bit_width,
+ .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
+ .offset_register = (first_dword >> 16) & 0xF,
+ .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
+ .value = GetNextVmInt(bit_width),
+ };
} break;
case CheatVmOpcodeType::BeginConditionalBlock: {
- BeginConditionalOpcode begin_cond{};
// 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
// Read additional words.
const u32 second_dword = GetNextDword();
- begin_cond.bit_width = (first_dword >> 24) & 0xF;
- begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
- begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
- begin_cond.rel_address =
- (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
- begin_cond.value = GetNextVmInt(begin_cond.bit_width);
- opcode.opcode = begin_cond;
+ const u32 bit_width = (first_dword >> 24) & 0xF;
+
+ opcode.opcode = BeginConditionalOpcode{
+ .bit_width = bit_width,
+ .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
+ .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF),
+ .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
+ .value = GetNextVmInt(bit_width),
+ };
} break;
case CheatVmOpcodeType::EndConditionalBlock: {
// 20000000
@@ -344,12 +346,14 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
opcode.opcode = EndConditionalOpcode{};
} break;
case CheatVmOpcodeType::ControlLoop: {
- ControlLoopOpcode ctrl_loop{};
// 300R0000 VVVVVVVV
// 310R0000
// Parse register, whether loop start or loop end.
- ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
- ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
+ ControlLoopOpcode ctrl_loop{
+ .start_loop = ((first_dword >> 24) & 0xF) == 0,
+ .reg_index = (first_dword >> 20) & 0xF,
+ .num_iters = 0,
+ };
// Read number of iters if loop start.
if (ctrl_loop.start_loop) {
@@ -358,66 +362,65 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
opcode.opcode = ctrl_loop;
} break;
case CheatVmOpcodeType::LoadRegisterStatic: {
- LoadRegisterStaticOpcode ldr_static{};
// 400R0000 VVVVVVVV VVVVVVVV
// Read additional words.
- ldr_static.reg_index = ((first_dword >> 16) & 0xF);
- ldr_static.value =
- (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
- opcode.opcode = ldr_static;
+ opcode.opcode = LoadRegisterStaticOpcode{
+ .reg_index = (first_dword >> 16) & 0xF,
+ .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(),
+ };
} break;
case CheatVmOpcodeType::LoadRegisterMemory: {
- LoadRegisterMemoryOpcode ldr_memory{};
// 5TMRI0AA AAAAAAAA
// Read additional words.
const u32 second_dword = GetNextDword();
- ldr_memory.bit_width = (first_dword >> 24) & 0xF;
- ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
- ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
- ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
- ldr_memory.rel_address =
- (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
- opcode.opcode = ldr_memory;
+ opcode.opcode = LoadRegisterMemoryOpcode{
+ .bit_width = (first_dword >> 24) & 0xF,
+ .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF),
+ .reg_index = ((first_dword >> 16) & 0xF),
+ .load_from_reg = ((first_dword >> 12) & 0xF) != 0,
+ .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword,
+ };
} break;
case CheatVmOpcodeType::StoreStaticToAddress: {
- StoreStaticToAddressOpcode str_static{};
// 6T0RIor0 VVVVVVVV VVVVVVVV
// Read additional words.
- str_static.bit_width = (first_dword >> 24) & 0xF;
- str_static.reg_index = ((first_dword >> 16) & 0xF);
- str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
- str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
- str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
- str_static.value =
- (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
- opcode.opcode = str_static;
+ opcode.opcode = StoreStaticToAddressOpcode{
+ .bit_width = (first_dword >> 24) & 0xF,
+ .reg_index = (first_dword >> 16) & 0xF,
+ .increment_reg = ((first_dword >> 12) & 0xF) != 0,
+ .add_offset_reg = ((first_dword >> 8) & 0xF) != 0,
+ .offset_reg_index = (first_dword >> 4) & 0xF,
+ .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(),
+ };
} break;
case CheatVmOpcodeType::PerformArithmeticStatic: {
- PerformArithmeticStaticOpcode perform_math_static{};
// 7T0RC000 VVVVVVVV
// Read additional words.
- perform_math_static.bit_width = (first_dword >> 24) & 0xF;
- perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
- perform_math_static.math_type =
- static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF);
- perform_math_static.value = GetNextDword();
- opcode.opcode = perform_math_static;
+ opcode.opcode = PerformArithmeticStaticOpcode{
+ .bit_width = (first_dword >> 24) & 0xF,
+ .reg_index = ((first_dword >> 16) & 0xF),
+ .math_type = static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF),
+ .value = GetNextDword(),
+ };
} break;
case CheatVmOpcodeType::BeginKeypressConditionalBlock: {
- BeginKeypressConditionalOpcode begin_keypress_cond{};
// 8kkkkkkk
// Just parse the mask.
- begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
- opcode.opcode = begin_keypress_cond;
+ opcode.opcode = BeginKeypressConditionalOpcode{
+ .key_mask = first_dword & 0x0FFFFFFF,
+ };
} break;
case CheatVmOpcodeType::PerformArithmeticRegister: {
- PerformArithmeticRegisterOpcode perform_math_reg{};
// 9TCRSIs0 (VVVVVVVV (VVVVVVVV))
- perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
- perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF);
- perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
- perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
- perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
+ PerformArithmeticRegisterOpcode perform_math_reg{
+ .bit_width = (first_dword >> 24) & 0xF,
+ .math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF),
+ .dst_reg_index = (first_dword >> 16) & 0xF,
+ .src_reg_1_index = (first_dword >> 12) & 0xF,
+ .src_reg_2_index = 0,
+ .has_immediate = ((first_dword >> 8) & 0xF) != 0,
+ .value = {},
+ };
if (perform_math_reg.has_immediate) {
perform_math_reg.src_reg_2_index = 0;
perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width);
@@ -427,7 +430,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
opcode.opcode = perform_math_reg;
} break;
case CheatVmOpcodeType::StoreRegisterToAddress: {
- StoreRegisterToAddressOpcode str_register{};
// ATSRIOxa (aaaaaaaa)
// A = opcode 10
// T = bit width
@@ -439,20 +441,23 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// Relative Address
// x = offset register (for offset type 1), memory type (for offset type 3)
// a = relative address (for offset type 2+3)
- str_register.bit_width = (first_dword >> 24) & 0xF;
- str_register.str_reg_index = ((first_dword >> 20) & 0xF);
- str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
- str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
- str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF));
- str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
+ StoreRegisterToAddressOpcode str_register{
+ .bit_width = (first_dword >> 24) & 0xF,
+ .str_reg_index = (first_dword >> 20) & 0xF,
+ .addr_reg_index = (first_dword >> 16) & 0xF,
+ .increment_reg = ((first_dword >> 12) & 0xF) != 0,
+ .ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)),
+ .mem_type = MemoryAccessType::MainNso,
+ .ofs_reg_index = (first_dword >> 4) & 0xF,
+ .rel_address = 0,
+ };
switch (str_register.ofs_type) {
case StoreRegisterOffsetType::None:
case StoreRegisterOffsetType::Reg:
// Nothing more to do
break;
case StoreRegisterOffsetType::Imm:
- str_register.rel_address =
- ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
break;
case StoreRegisterOffsetType::MemReg:
str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
@@ -460,8 +465,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
case StoreRegisterOffsetType::MemImm:
case StoreRegisterOffsetType::MemImmReg:
str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
- str_register.rel_address =
- ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
break;
default:
str_register.ofs_type = StoreRegisterOffsetType::None;
@@ -470,7 +474,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
opcode.opcode = str_register;
} break;
case CheatVmOpcodeType::BeginRegisterConditionalBlock: {
- BeginRegisterConditionalOpcode begin_reg_cond{};
// C0TcSX##
// C0TcS0Ma aaaaaaaa
// C0TcS1Mr
@@ -492,11 +495,19 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// r = offset register.
// X = other register.
// V = value.
- begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
- begin_reg_cond.cond_type =
- static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
- begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
- begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF);
+
+ BeginRegisterConditionalOpcode begin_reg_cond{
+ .bit_width = (first_dword >> 20) & 0xF,
+ .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF),
+ .val_reg_index = (first_dword >> 12) & 0xF,
+ .comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF),
+ .mem_type = MemoryAccessType::MainNso,
+ .addr_reg_index = 0,
+ .other_reg_index = 0,
+ .ofs_reg_index = 0,
+ .rel_address = 0,
+ .value = {},
+ };
switch (begin_reg_cond.comp_type) {
case CompareRegisterValueType::StaticValue:
@@ -508,26 +519,25 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
case CompareRegisterValueType::MemoryRelAddr:
begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
begin_reg_cond.rel_address =
- ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
break;
case CompareRegisterValueType::MemoryOfsReg:
begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
break;
case CompareRegisterValueType::RegisterRelAddr:
- begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
+ begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF;
begin_reg_cond.rel_address =
- ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
break;
case CompareRegisterValueType::RegisterOfsReg:
- begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
- begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+ begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF;
+ begin_reg_cond.ofs_reg_index = first_dword & 0xF;
break;
}
opcode.opcode = begin_reg_cond;
} break;
case CheatVmOpcodeType::SaveRestoreRegister: {
- SaveRestoreRegisterOpcode save_restore_reg{};
// C10D0Sx0
// C1 = opcode 0xC1
// D = destination index.
@@ -535,36 +545,37 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
// a register.
// NOTE: If we add more save slots later, current encoding is backwards compatible.
- save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
- save_restore_reg.src_index = (first_dword >> 8) & 0xF;
- save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF);
- opcode.opcode = save_restore_reg;
+ opcode.opcode = SaveRestoreRegisterOpcode{
+ .dst_index = (first_dword >> 16) & 0xF,
+ .src_index = (first_dword >> 8) & 0xF,
+ .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF),
+ };
} break;
case CheatVmOpcodeType::SaveRestoreRegisterMask: {
- SaveRestoreRegisterMaskOpcode save_restore_regmask{};
// C2x0XXXX
// C2 = opcode 0xC2
// x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring.
// X = 16-bit bitmask, bit i --> save or restore register i.
- save_restore_regmask.op_type =
- static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF);
+ SaveRestoreRegisterMaskOpcode save_restore_regmask{
+ .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF),
+ .should_operate = {},
+ };
for (std::size_t i = 0; i < NumRegisters; i++) {
- save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
+ save_restore_regmask.should_operate[i] = (first_dword & (1U << i)) != 0;
}
opcode.opcode = save_restore_regmask;
} break;
case CheatVmOpcodeType::ReadWriteStaticRegister: {
- ReadWriteStaticRegisterOpcode rw_static_reg{};
// C3000XXx
// C3 = opcode 0xC3.
// XX = static register index.
// x = register index.
- rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF);
- rw_static_reg.idx = (first_dword & 0xF);
- opcode.opcode = rw_static_reg;
+ opcode.opcode = ReadWriteStaticRegisterOpcode{
+ .static_idx = (first_dword >> 4) & 0xFF,
+ .idx = first_dword & 0xF,
+ };
} break;
case CheatVmOpcodeType::DebugLog: {
- DebugLogOpcode debug_log{};
// FFFTIX##
// FFFTI0Ma aaaaaaaa
// FFFTI1Mr
@@ -583,31 +594,36 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// a = relative address.
// r = offset register.
// X = value register.
- debug_log.bit_width = (first_dword >> 16) & 0xF;
- debug_log.log_id = ((first_dword >> 12) & 0xF);
- debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF);
+ DebugLogOpcode debug_log{
+ .bit_width = (first_dword >> 16) & 0xF,
+ .log_id = (first_dword >> 12) & 0xF,
+ .val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF),
+ .mem_type = MemoryAccessType::MainNso,
+ .addr_reg_index = 0,
+ .val_reg_index = 0,
+ .ofs_reg_index = 0,
+ .rel_address = 0,
+ };
switch (debug_log.val_type) {
case DebugLogValueType::RegisterValue:
- debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
+ debug_log.val_reg_index = (first_dword >> 4) & 0xF;
break;
case DebugLogValueType::MemoryRelAddr:
debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
- debug_log.rel_address =
- ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
break;
case DebugLogValueType::MemoryOfsReg:
debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
- debug_log.ofs_reg_index = (first_dword & 0xF);
+ debug_log.ofs_reg_index = first_dword & 0xF;
break;
case DebugLogValueType::RegisterRelAddr:
- debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
- debug_log.rel_address =
- ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
+ debug_log.addr_reg_index = (first_dword >> 4) & 0xF;
+ debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword();
break;
case DebugLogValueType::RegisterOfsReg:
- debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
- debug_log.ofs_reg_index = (first_dword & 0xF);
+ debug_log.addr_reg_index = (first_dword >> 4) & 0xF;
+ debug_log.ofs_reg_index = first_dword & 0xF;
break;
}
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() {
game_frames += 1;
}
-double PerfStats::GetMeanFrametime() {
+double PerfStats::GetMeanFrametime() const {
std::lock_guard lock{object_mutex};
if (current_index <= IgnoreFrames) {
return 0;
}
+
const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
perf_history.begin() + current_index, 0.0);
- return sum / (current_index - IgnoreFrames);
+ return sum / static_cast<double>(current_index - IgnoreFrames);
}
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
@@ -94,12 +95,13 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
- PerfStatsResults results{};
- results.system_fps = static_cast<double>(system_frames) / interval;
- results.game_fps = static_cast<double>(game_frames) / interval;
- results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
- static_cast<double>(system_frames);
- results.emulation_speed = system_us_per_second.count() / 1'000'000.0;
+ const PerfStatsResults results{
+ .system_fps = static_cast<double>(system_frames) / interval,
+ .game_fps = static_cast<double>(game_frames) / interval,
+ .frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
+ static_cast<double>(system_frames),
+ .emulation_speed = system_us_per_second.count() / 1'000'000.0,
+ };
// Reset counters
reset_point = now;
@@ -111,7 +113,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
return results;
}
-double PerfStats::GetLastFrameTimeScale() {
+double PerfStats::GetLastFrameTimeScale() const {
std::lock_guard lock{object_mutex};
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 {
class PerfStats {
public:
explicit PerfStats(u64 title_id);
-
~PerfStats();
using Clock = std::chrono::high_resolution_clock;
@@ -42,18 +41,18 @@ public:
PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
/**
- * Returns the Arthimetic Mean of all frametime values stored in the performance history.
+ * Returns the arithmetic mean of all frametime values stored in the performance history.
*/
- double GetMeanFrametime();
+ double GetMeanFrametime() const;
/**
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
* useful for scaling inputs or outputs moving between the two time domains.
*/
- double GetLastFrameTimeScale();
+ double GetLastFrameTimeScale() const;
private:
- std::mutex object_mutex{};
+ mutable std::mutex object_mutex;
/// Title ID for the game that is running. 0 if there is no game running yet
u64 title_id{0};
@@ -61,7 +60,7 @@ private:
std::size_t current_index{0};
/// Stores an hour of historical frametime data useful for processing and tracking performance
/// regressions with code changes.
- std::array<double, 216000> perf_history = {};
+ std::array<double, 216000> perf_history{};
/// Point when the cumulative counters were reset
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() {
values.use_assembly_shaders.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
- values.force_30fps_mode.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
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 {
Setting<bool> use_vsync;
Setting<bool> use_assembly_shaders;
Setting<bool> use_asynchronous_shaders;
- Setting<bool> force_30fps_mode;
Setting<bool> use_fast_gpu_time;
Setting<float> bg_red;
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 022b26e6d..b35459152 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -46,20 +46,16 @@ struct ScopeInit final {
Core::Timing::CoreTiming core_timing;
};
-#pragma optimize("", off)
-
u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) {
- u64 start = core_timing.GetGlobalTimeNs().count();
- u64 placebo = 0;
+ const u64 start = core_timing.GetGlobalTimeNs().count();
+ volatile u64 placebo = 0;
for (std::size_t i = 0; i < 1000; i++) {
- placebo += core_timing.GetGlobalTimeNs().count();
+ placebo = placebo + core_timing.GetGlobalTimeNs().count();
}
- u64 end = core_timing.GetGlobalTimeNs().count();
- return (end - start);
+ const u64 end = core_timing.GetGlobalTimeNs().count();
+ return end - start;
}
-#pragma optimize("", on)
-
} // Anonymous namespace
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#pragma once
+
#include <array>
#include <bitset>
#include <cstddef>
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) {
}
MICROPROFILE_SCOPE(GPU_wait);
std::unique_lock lock{sync_mutex};
- sync_cv.wait(lock, [=]() { return syncpoints[syncpoint_id].load() >= value; });
+ sync_cv.wait(lock, [=, this] { return syncpoints[syncpoint_id].load() >= value; });
}
void GPU::IncrementSyncPoint(const u32 syncpoint_id) {
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 @@
#include "common/alignment.h"
#include "common/assert.h"
-#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/process.h"
@@ -16,121 +15,137 @@
namespace Tegra {
MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
- : rasterizer{rasterizer}, system{system} {
- page_table.Resize(address_space_width, page_bits, false);
-
- // Initialize the map with a single free region covering the entire managed space.
- VirtualMemoryArea initial_vma;
- initial_vma.size = address_space_end;
- vma_map.emplace(initial_vma.base, initial_vma);
-
- UpdatePageTableForVMA(initial_vma);
-}
+ : system{system}, rasterizer{rasterizer}, page_table(page_table_size) {}
MemoryManager::~MemoryManager() = default;
-GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
- const u64 aligned_size{Common::AlignUp(size, page_size)};
- const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
-
- AllocateMemory(gpu_addr, 0, aligned_size);
-
+GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
+ u64 remaining_size{size};
+ for (u64 offset{}; offset < size; offset += page_size) {
+ if (remaining_size < page_size) {
+ SetPageEntry(gpu_addr + offset, page_entry + offset, remaining_size);
+ } else {
+ SetPageEntry(gpu_addr + offset, page_entry + offset);
+ }
+ remaining_size -= page_size;
+ }
return gpu_addr;
}
-GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
- const u64 aligned_size{Common::AlignUp(size, page_size)};
-
- AllocateMemory(gpu_addr, 0, aligned_size);
+GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) {
+ return UpdateRange(gpu_addr, cpu_addr, size);
+}
- return gpu_addr;
+GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align) {
+ return Map(cpu_addr, *FindFreeRange(size, align), size);
}
-GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
- const u64 aligned_size{Common::AlignUp(size, page_size)};
- const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
+void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
+ if (!size) {
+ return;
+ }
- MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
- ASSERT(
- system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess());
+ // Flush and invalidate through the GPU interface, to be asynchronous if possible.
+ system.GPU().FlushAndInvalidateRegion(*GpuToCpuAddress(gpu_addr), size);
- return gpu_addr;
+ UpdateRange(gpu_addr, PageEntry::State::Unmapped, size);
}
-GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) {
- ASSERT((gpu_addr & page_mask) == 0);
+std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
+ for (u64 offset{}; offset < size; offset += page_size) {
+ if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
+ return {};
+ }
+ }
- const u64 aligned_size{Common::AlignUp(size, page_size)};
+ return UpdateRange(gpu_addr, PageEntry::State::Allocated, size);
+}
- MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
- ASSERT(
- system.CurrentProcess()->PageTable().LockForDeviceAddressSpace(cpu_addr, size).IsSuccess());
- return gpu_addr;
+GPUVAddr MemoryManager::Allocate(std::size_t size, std::size_t align) {
+ return *AllocateFixed(*FindFreeRange(size, align), size);
}
-GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
- ASSERT((gpu_addr & page_mask) == 0);
+void MemoryManager::TryLockPage(PageEntry page_entry, std::size_t size) {
+ if (!page_entry.IsValid()) {
+ return;
+ }
- const u64 aligned_size{Common::AlignUp(size, page_size)};
- const auto cpu_addr = GpuToCpuAddress(gpu_addr);
- ASSERT(cpu_addr);
+ ASSERT(system.CurrentProcess()
+ ->PageTable()
+ .LockForDeviceAddressSpace(page_entry.ToAddress(), size)
+ .IsSuccess());
+}
- // Flush and invalidate through the GPU interface, to be asynchronous if possible.
- system.GPU().FlushAndInvalidateRegion(*cpu_addr, aligned_size);
+void MemoryManager::TryUnlockPage(PageEntry page_entry, std::size_t size) {
+ if (!page_entry.IsValid()) {
+ return;
+ }
- UnmapRange(gpu_addr, aligned_size);
ASSERT(system.CurrentProcess()
->PageTable()
- .UnlockForDeviceAddressSpace(cpu_addr.value(), size)
+ .UnlockForDeviceAddressSpace(page_entry.ToAddress(), size)
.IsSuccess());
-
- return gpu_addr;
}
-GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) const {
- // Find the first Free VMA.
- const VMAHandle vma_handle{
- std::find_if(vma_map.begin(), vma_map.end(), [region_start, size](const auto& vma) {
- if (vma.second.type != VirtualMemoryArea::Type::Unmapped) {
- return false;
- }
+PageEntry MemoryManager::GetPageEntry(GPUVAddr gpu_addr) const {
+ return page_table[PageEntryIndex(gpu_addr)];
+}
- const VAddr vma_end{vma.second.base + vma.second.size};
- return vma_end > region_start && vma_end >= region_start + size;
- })};
+void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size) {
+ // TODO(bunnei): We should lock/unlock device regions. This currently causes issues due to
+ // improper tracking, but should be fixed in the future.
- if (vma_handle == vma_map.end()) {
- return {};
- }
+ //// Unlock the old page
+ // TryUnlockPage(page_table[PageEntryIndex(gpu_addr)], size);
- return std::max(region_start, vma_handle->second.base);
-}
+ //// Lock the new page
+ // TryLockPage(page_entry, size);
-bool MemoryManager::IsAddressValid(GPUVAddr addr) const {
- return (addr >> page_bits) < page_table.pointers.size();
+ page_table[PageEntryIndex(gpu_addr)] = page_entry;
}
-std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) const {
- if (!IsAddressValid(addr)) {
- return {};
+std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align) const {
+ if (!align) {
+ align = page_size;
+ } else {
+ align = Common::AlignUp(align, page_size);
}
- const VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]};
- if (cpu_addr) {
- return cpu_addr + (addr & page_mask);
+ u64 available_size{};
+ GPUVAddr gpu_addr{address_space_start};
+ while (gpu_addr + available_size < address_space_size) {
+ if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) {
+ available_size += page_size;
+
+ if (available_size >= size) {
+ return gpu_addr;
+ }
+ } else {
+ gpu_addr += available_size + page_size;
+ available_size = 0;
+
+ const auto remainder{gpu_addr % align};
+ if (remainder) {
+ gpu_addr = (gpu_addr - remainder) + align;
+ }
+ }
}
return {};
}
-template <typename T>
-T MemoryManager::Read(GPUVAddr addr) const {
- if (!IsAddressValid(addr)) {
+std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
+ const auto page_entry{GetPageEntry(gpu_addr)};
+ if (!page_entry.IsValid()) {
return {};
}
- const u8* page_pointer{GetPointer(addr)};
- if (page_pointer) {
+ return page_entry.ToAddress() + (gpu_addr & page_mask);
+}
+
+template <typename T>
+T MemoryManager::Read(GPUVAddr addr) const {
+ if (auto page_pointer{GetPointer(addr)}; page_pointer) {
// NOTE: Avoid adding any extra logic to this fast-path block
T value;
std::memcpy(&value, page_pointer, sizeof(T));
@@ -144,12 +159,7 @@ T MemoryManager::Read(GPUVAddr addr) const {
template <typename T>
void MemoryManager::Write(GPUVAddr addr, T data) {
- if (!IsAddressValid(addr)) {
- return;
- }
-
- u8* page_pointer{GetPointer(addr)};
- if (page_pointer) {
+ if (auto page_pointer{GetPointer(addr)}; page_pointer) {
// NOTE: Avoid adding any extra logic to this fast-path block
std::memcpy(page_pointer, &data, sizeof(T));
return;
@@ -167,66 +177,49 @@ template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
template void MemoryManager::Write<u64>(GPUVAddr addr, u64 data);
-u8* MemoryManager::GetPointer(GPUVAddr addr) {
- if (!IsAddressValid(addr)) {
+u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) {
+ if (!GetPageEntry(gpu_addr).IsValid()) {
return {};
}
- auto& memory = system.Memory();
-
- const VAddr page_addr{page_table.backing_addr[addr >> page_bits]};
-
- if (page_addr != 0) {
- return memory.GetPointer(page_addr + (addr & page_mask));
+ const auto address{GpuToCpuAddress(gpu_addr)};
+ if (!address) {
+ return {};
}
- LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
- return {};
+ return system.Memory().GetPointer(*address);
}
-const u8* MemoryManager::GetPointer(GPUVAddr addr) const {
- if (!IsAddressValid(addr)) {
+const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
+ if (!GetPageEntry(gpu_addr).IsValid()) {
return {};
}
- const auto& memory = system.Memory();
-
- const VAddr page_addr{page_table.backing_addr[addr >> page_bits]};
-
- if (page_addr != 0) {
- return memory.GetPointer(page_addr + (addr & page_mask));
+ const auto address{GpuToCpuAddress(gpu_addr)};
+ if (!address) {
+ return {};
}
- LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
- return {};
-}
-
-bool MemoryManager::IsBlockContinuous(const GPUVAddr start, const std::size_t size) const {
- const std::size_t inner_size = size - 1;
- const GPUVAddr end = start + inner_size;
- const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start));
- const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end));
- const auto range = static_cast<std::size_t>(host_ptr_end - host_ptr_start);
- return range == inner_size;
+ return system.Memory().GetPointer(*address);
}
-void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer,
- const std::size_t size) const {
+void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const {
std::size_t remaining_size{size};
std::size_t page_index{gpu_src_addr >> page_bits};
std::size_t page_offset{gpu_src_addr & page_mask};
- auto& memory = system.Memory();
-
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
- const VAddr src_addr{page_table.backing_addr[page_index] + page_offset};
- // Flush must happen on the rasterizer interface, such that memory is always synchronous
- // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
- rasterizer.FlushRegion(src_addr, copy_amount);
- memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
+ if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
+ const auto src_addr{*page_addr + page_offset};
+
+ // Flush must happen on the rasterizer interface, such that memory is always synchronous
+ // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
+ rasterizer.FlushRegion(src_addr, copy_amount);
+ system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
+ }
page_index++;
page_offset = 0;
@@ -241,18 +234,17 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
std::size_t page_index{gpu_src_addr >> page_bits};
std::size_t page_offset{gpu_src_addr & page_mask};
- auto& memory = system.Memory();
-
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
- const u8* page_pointer = page_table.pointers[page_index];
- if (page_pointer) {
- const VAddr src_addr{page_table.backing_addr[page_index] + page_offset};
- memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
+
+ if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
+ const auto src_addr{*page_addr + page_offset};
+ system.Memory().ReadBlockUnsafe(src_addr, dest_buffer, copy_amount);
} else {
std::memset(dest_buffer, 0, copy_amount);
}
+
page_index++;
page_offset = 0;
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
@@ -260,23 +252,23 @@ void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
}
}
-void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer,
- const std::size_t size) {
+void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
std::size_t remaining_size{size};
std::size_t page_index{gpu_dest_addr >> page_bits};
std::size_t page_offset{gpu_dest_addr & page_mask};
- auto& memory = system.Memory();
-
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
- const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset};
- // Invalidate must happen on the rasterizer interface, such that memory is always
- // synchronous when it is written (even when in asynchronous GPU mode).
- rasterizer.InvalidateRegion(dest_addr, copy_amount);
- memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
+ if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
+ const auto dest_addr{*page_addr + page_offset};
+
+ // Invalidate must happen on the rasterizer interface, such that memory is always
+ // synchronous when it is written (even when in asynchronous GPU mode).
+ rasterizer.InvalidateRegion(dest_addr, copy_amount);
+ system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
+ }
page_index++;
page_offset = 0;
@@ -286,21 +278,20 @@ void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer,
}
void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
- const std::size_t size) {
+ std::size_t size) {
std::size_t remaining_size{size};
std::size_t page_index{gpu_dest_addr >> page_bits};
std::size_t page_offset{gpu_dest_addr & page_mask};
- auto& memory = system.Memory();
-
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)};
- u8* page_pointer = page_table.pointers[page_index];
- if (page_pointer) {
- const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset};
- memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
+
+ if (const auto page_addr{GpuToCpuAddress(page_index << page_bits)}; page_addr) {
+ const auto dest_addr{*page_addr + page_offset};
+ system.Memory().WriteBlockUnsafe(dest_addr, src_buffer, copy_amount);
}
+
page_index++;
page_offset = 0;
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
}
}
-void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr,
- const std::size_t size) {
+void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size) {
std::vector<u8> tmp_buffer(size);
ReadBlock(gpu_src_addr, tmp_buffer.data(), size);
WriteBlock(gpu_dest_addr, tmp_buffer.data(), size);
}
void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr,
- const std::size_t size) {
+ std::size_t size) {
std::vector<u8> tmp_buffer(size);
ReadBlockUnsafe(gpu_src_addr, tmp_buffer.data(), size);
WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
}
bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) {
- const VAddr addr = page_table.backing_addr[gpu_addr >> page_bits];
- const std::size_t page = (addr & Core::Memory::PAGE_MASK) + size;
- return page <= Core::Memory::PAGE_SIZE;
-}
-
-void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
- VAddr backing_addr) {
- LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size,
- (base + size) * page_size);
-
- const VAddr end{base + size};
- ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
- base + page_table.pointers.size());
-
- if (memory == nullptr) {
- while (base != end) {
- page_table.pointers[base] = nullptr;
- page_table.backing_addr[base] = 0;
-
- base += 1;
- }
- } else {
- while (base != end) {
- page_table.pointers[base] = memory;
- page_table.backing_addr[base] = backing_addr;
-
- base += 1;
- memory += page_size;
- backing_addr += page_size;
- }
- }
-}
-
-void MemoryManager::MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr) {
- ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size);
- ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base);
- MapPages(base / page_size, size / page_size, target, Common::PageType::Memory, backing_addr);
-}
-
-void MemoryManager::UnmapRegion(GPUVAddr base, u64 size) {
- ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: {:016X}", size);
- ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: {:016X}", base);
- MapPages(base / page_size, size / page_size, nullptr, Common::PageType::Unmapped);
-}
-
-bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
- ASSERT(base + size == next.base);
- if (type != next.type) {
- return {};
- }
- if (type == VirtualMemoryArea::Type::Allocated && (offset + size != next.offset)) {
- return {};
- }
- if (type == VirtualMemoryArea::Type::Mapped && backing_memory + size != next.backing_memory) {
- return {};
- }
- return true;
-}
-
-MemoryManager::VMAHandle MemoryManager::FindVMA(GPUVAddr target) const {
- if (target >= address_space_end) {
- return vma_map.end();
- } else {
- return std::prev(vma_map.upper_bound(target));
- }
-}
-
-MemoryManager::VMAIter MemoryManager::Allocate(VMAIter vma_handle) {
- VirtualMemoryArea& vma{vma_handle->second};
-
- vma.type = VirtualMemoryArea::Type::Allocated;
- vma.backing_addr = 0;
- vma.backing_memory = {};
- UpdatePageTableForVMA(vma);
-
- return MergeAdjacent(vma_handle);
-}
-
-MemoryManager::VMAHandle MemoryManager::AllocateMemory(GPUVAddr target, std::size_t offset,
- u64 size) {
-
- // This is the appropriately sized VMA that will turn into our allocation.
- VMAIter vma_handle{CarveVMA(target, size)};
- VirtualMemoryArea& vma{vma_handle->second};
-
- ASSERT(vma.size == size);
-
- vma.offset = offset;
-
- return Allocate(vma_handle);
-}
-
-MemoryManager::VMAHandle MemoryManager::MapBackingMemory(GPUVAddr target, u8* memory, u64 size,
- VAddr backing_addr) {
- // This is the appropriately sized VMA that will turn into our allocation.
- VMAIter vma_handle{CarveVMA(target, size)};
- VirtualMemoryArea& vma{vma_handle->second};
-
- ASSERT(vma.size == size);
-
- vma.type = VirtualMemoryArea::Type::Mapped;
- vma.backing_memory = memory;
- vma.backing_addr = backing_addr;
- UpdatePageTableForVMA(vma);
-
- return MergeAdjacent(vma_handle);
-}
-
-void MemoryManager::UnmapRange(GPUVAddr target, u64 size) {
- VMAIter vma{CarveVMARange(target, size)};
- const VAddr target_end{target + size};
- const VMAIter end{vma_map.end()};
-
- // The comparison against the end of the range must be done using addresses since VMAs can be
- // merged during this process, causing invalidation of the iterators.
- while (vma != end && vma->second.base < target_end) {
- // Unmapped ranges return to allocated state and can be reused
- // This behavior is used by Super Mario Odyssey, Sonic Forces, and likely other games
- vma = std::next(Allocate(vma));
- }
-
- ASSERT(FindVMA(target)->second.size >= size);
-}
-
-MemoryManager::VMAIter MemoryManager::StripIterConstness(const VMAHandle& iter) {
- // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given
- // non-const access to its container.
- return vma_map.erase(iter, iter); // Erases an empty range of elements
-}
-
-MemoryManager::VMAIter MemoryManager::CarveVMA(GPUVAddr base, u64 size) {
- ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size);
- ASSERT_MSG((base & page_mask) == 0, "non-page aligned base: 0x{:016X}", base);
-
- VMAIter vma_handle{StripIterConstness(FindVMA(base))};
- if (vma_handle == vma_map.end()) {
- // Target address is outside the managed range
- return {};
- }
-
- const VirtualMemoryArea& vma{vma_handle->second};
- if (vma.type == VirtualMemoryArea::Type::Mapped) {
- // Region is already allocated
- return vma_handle;
- }
-
- const VAddr start_in_vma{base - vma.base};
- const VAddr end_in_vma{start_in_vma + size};
-
- ASSERT_MSG(end_in_vma <= vma.size, "region size 0x{:016X} is less than required size 0x{:016X}",
- vma.size, end_in_vma);
-
- if (end_in_vma < vma.size) {
- // Split VMA at the end of the allocated region
- SplitVMA(vma_handle, end_in_vma);
- }
- if (start_in_vma != 0) {
- // Split VMA at the start of the allocated region
- vma_handle = SplitVMA(vma_handle, start_in_vma);
- }
-
- return vma_handle;
-}
-
-MemoryManager::VMAIter MemoryManager::CarveVMARange(GPUVAddr target, u64 size) {
- ASSERT_MSG((size & page_mask) == 0, "non-page aligned size: 0x{:016X}", size);
- ASSERT_MSG((target & page_mask) == 0, "non-page aligned base: 0x{:016X}", target);
-
- const VAddr target_end{target + size};
- ASSERT(target_end >= target);
- ASSERT(size > 0);
-
- VMAIter begin_vma{StripIterConstness(FindVMA(target))};
- const VMAIter i_end{vma_map.lower_bound(target_end)};
- if (std::any_of(begin_vma, i_end, [](const auto& entry) {
- return entry.second.type == VirtualMemoryArea::Type::Unmapped;
- })) {
+ const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
+ if (!cpu_addr) {
return {};
}
-
- if (target != begin_vma->second.base) {
- begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base);
- }
-
- VMAIter end_vma{StripIterConstness(FindVMA(target_end))};
- if (end_vma != vma_map.end() && target_end != end_vma->second.base) {
- end_vma = SplitVMA(end_vma, target_end - end_vma->second.base);
- }
-
- return begin_vma;
-}
-
-MemoryManager::VMAIter MemoryManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
- VirtualMemoryArea& old_vma{vma_handle->second};
- VirtualMemoryArea new_vma{old_vma}; // Make a copy of the VMA
-
- // For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably
- // a bug. This restriction might be removed later.
- ASSERT(offset_in_vma < old_vma.size);
- ASSERT(offset_in_vma > 0);
-
- old_vma.size = offset_in_vma;
- new_vma.base += offset_in_vma;
- new_vma.size -= offset_in_vma;
-
- switch (new_vma.type) {
- case VirtualMemoryArea::Type::Unmapped:
- break;
- case VirtualMemoryArea::Type::Allocated:
- new_vma.offset += offset_in_vma;
- break;
- case VirtualMemoryArea::Type::Mapped:
- new_vma.backing_memory += offset_in_vma;
- break;
- }
-
- ASSERT(old_vma.CanBeMergedWith(new_vma));
-
- return vma_map.emplace_hint(std::next(vma_handle), new_vma.base, new_vma);
-}
-
-MemoryManager::VMAIter MemoryManager::MergeAdjacent(VMAIter iter) {
- const VMAIter next_vma{std::next(iter)};
- if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
- iter->second.size += next_vma->second.size;
- vma_map.erase(next_vma);
- }
-
- if (iter != vma_map.begin()) {
- VMAIter prev_vma{std::prev(iter)};
- if (prev_vma->second.CanBeMergedWith(iter->second)) {
- prev_vma->second.size += iter->second.size;
- vma_map.erase(iter);
- iter = prev_vma;
- }
- }
-
- return iter;
-}
-
-void MemoryManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
- switch (vma.type) {
- case VirtualMemoryArea::Type::Unmapped:
- UnmapRegion(vma.base, vma.size);
- break;
- case VirtualMemoryArea::Type::Allocated:
- MapMemoryRegion(vma.base, vma.size, nullptr, vma.backing_addr);
- break;
- case VirtualMemoryArea::Type::Mapped:
- MapMemoryRegion(vma.base, vma.size, vma.backing_memory, vma.backing_addr);
- break;
- }
+ const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
+ return page <= Core::Memory::PAGE_SIZE;
}
} // 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 @@
#include <map>
#include <optional>
+#include <vector>
#include "common/common_types.h"
-#include "common/page_table.h"
namespace VideoCore {
class RasterizerInterface;
@@ -20,45 +20,57 @@ class System;
namespace Tegra {
-/**
- * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space
- * with homogeneous attributes across its extents. In this particular implementation each VMA is
- * also backed by a single host memory allocation.
- */
-struct VirtualMemoryArea {
- enum class Type : u8 {
- Unmapped,
- Allocated,
- Mapped,
+class PageEntry final {
+public:
+ enum class State : u32 {
+ Unmapped = static_cast<u32>(-1),
+ Allocated = static_cast<u32>(-2),
};
- /// Virtual base address of the region.
- GPUVAddr base{};
- /// Size of the region.
- u64 size{};
- /// Memory area mapping type.
- Type type{Type::Unmapped};
- /// CPU memory mapped address corresponding to this memory area.
- VAddr backing_addr{};
- /// Offset into the backing_memory the mapping starts from.
- std::size_t offset{};
- /// Pointer backing this VMA.
- u8* backing_memory{};
-
- /// Tests if this area can be merged to the right with `next`.
- bool CanBeMergedWith(const VirtualMemoryArea& next) const;
+ constexpr PageEntry() = default;
+ constexpr PageEntry(State state) : state{state} {}
+ constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
+
+ constexpr bool IsUnmapped() const {
+ return state == State::Unmapped;
+ }
+
+ constexpr bool IsAllocated() const {
+ return state == State::Allocated;
+ }
+
+ constexpr bool IsValid() const {
+ return !IsUnmapped() && !IsAllocated();
+ }
+
+ constexpr VAddr ToAddress() const {
+ if (!IsValid()) {
+ return {};
+ }
+
+ return static_cast<VAddr>(state) << ShiftBits;
+ }
+
+ constexpr PageEntry operator+(u64 offset) {
+ // If this is a reserved value, offsets do not apply
+ if (!IsValid()) {
+ return *this;
+ }
+ return PageEntry{(static_cast<VAddr>(state) << ShiftBits) + offset};
+ }
+
+private:
+ static constexpr std::size_t ShiftBits{12};
+
+ State state{State::Unmapped};
};
+static_assert(sizeof(PageEntry) == 4, "PageEntry is too large");
class MemoryManager final {
public:
explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
~MemoryManager();
- GPUVAddr AllocateSpace(u64 size, u64 align);
- GPUVAddr AllocateSpace(GPUVAddr addr, u64 size, u64 align);
- GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
- GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size);
- GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size);
std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
template <typename T>
@@ -70,9 +82,6 @@ public:
u8* GetPointer(GPUVAddr addr);
const u8* GetPointer(GPUVAddr addr) const;
- /// Returns true if the block is continuous in host memory, false otherwise
- bool IsBlockContinuous(GPUVAddr start, std::size_t size) const;
-
/**
* ReadBlock and WriteBlock are full read and write operations over virtual
* GPU Memory. It's important to use these when GPU memory may not be continuous
@@ -98,92 +107,43 @@ public:
void CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size);
/**
- * IsGranularRange checks if a gpu region can be simply read with a pointer
+ * IsGranularRange checks if a gpu region can be simply read with a pointer.
*/
bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size);
-private:
- using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>;
- using VMAHandle = VMAMap::const_iterator;
- using VMAIter = VMAMap::iterator;
-
- bool IsAddressValid(GPUVAddr addr) const;
- void MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type,
- VAddr backing_addr = 0);
- void MapMemoryRegion(GPUVAddr base, u64 size, u8* target, VAddr backing_addr);
- void UnmapRegion(GPUVAddr base, u64 size);
-
- /// Finds the VMA in which the given address is included in, or `vma_map.end()`.
- VMAHandle FindVMA(GPUVAddr target) const;
-
- VMAHandle AllocateMemory(GPUVAddr target, std::size_t offset, u64 size);
-
- /**
- * Maps an unmanaged host memory pointer at a given address.
- *
- * @param target The guest address to start the mapping at.
- * @param memory The memory to be mapped.
- * @param size Size of the mapping in bytes.
- * @param backing_addr The base address of the range to back this mapping.
- */
- VMAHandle MapBackingMemory(GPUVAddr target, u8* memory, u64 size, VAddr backing_addr);
-
- /// Unmaps a range of addresses, splitting VMAs as necessary.
- void UnmapRange(GPUVAddr target, u64 size);
-
- /// Converts a VMAHandle to a mutable VMAIter.
- VMAIter StripIterConstness(const VMAHandle& iter);
-
- /// Marks as the specified VMA as allocated.
- VMAIter Allocate(VMAIter vma);
-
- /**
- * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing
- * the appropriate error checking.
- */
- VMAIter CarveVMA(GPUVAddr base, u64 size);
-
- /**
- * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each
- * end of the range.
- */
- VMAIter CarveVMARange(GPUVAddr base, u64 size);
-
- /**
- * Splits a VMA in two, at the specified offset.
- * @returns the right side of the split, with the original iterator becoming the left side.
- */
- VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma);
+ GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
+ GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
+ std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
+ GPUVAddr Allocate(std::size_t size, std::size_t align);
+ void Unmap(GPUVAddr gpu_addr, std::size_t size);
- /**
- * Checks for and merges the specified VMA with adjacent ones if possible.
- * @returns the merged VMA or the original if no merging was possible.
- */
- VMAIter MergeAdjacent(VMAIter vma);
+private:
+ PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
+ void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
+ GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
+ std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
- /// Updates the pages corresponding to this VMA so they match the VMA's attributes.
- void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
+ void TryLockPage(PageEntry page_entry, std::size_t size);
+ void TryUnlockPage(PageEntry page_entry, std::size_t size);
- /// Finds a free (unmapped region) of the specified size starting at the specified address.
- GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size) const;
+ static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
+ return (gpu_addr >> page_bits) & page_table_mask;
+ }
-private:
+ static constexpr u64 address_space_size = 1ULL << 40;
+ static constexpr u64 address_space_start = 1ULL << 32;
static constexpr u64 page_bits{16};
static constexpr u64 page_size{1 << page_bits};
static constexpr u64 page_mask{page_size - 1};
+ static constexpr u64 page_table_bits{24};
+ static constexpr u64 page_table_size{1 << page_table_bits};
+ static constexpr u64 page_table_mask{page_table_size - 1};
- /// Address space in bits, according to Tegra X1 TRM
- static constexpr u32 address_space_width{40};
- /// Start address for mapping, this is fairly arbitrary but must be non-zero.
- static constexpr GPUVAddr address_space_base{0x100000};
- /// End of address space, based on address space in bits.
- static constexpr GPUVAddr address_space_end{1ULL << address_space_width};
+ Core::System& system;
- Common::PageTable page_table;
- VMAMap vma_map;
VideoCore::RasterizerInterface& rasterizer;
- Core::System& system;
+ std::vector<PageEntry> page_table;
};
} // 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
if (device.UseAsynchronousShaders()) {
// Max worker threads we should allow
- constexpr auto MAX_THREADS = 2u;
- // Amount of threads we should reserve for other parts of yuzu
- constexpr auto RESERVED_THREADS = 6u;
- // Get the amount of threads we can use(this can return zero)
- const auto cpu_thread_count =
- std::max(RESERVED_THREADS, std::thread::hardware_concurrency());
- // Deduce how many "extra" threads we have to use.
- const auto max_threads_unused = cpu_thread_count - RESERVED_THREADS;
+ constexpr u32 MAX_THREADS = 4;
+ // Deduce how many threads we can use
+ const u32 threads_used = std::thread::hardware_concurrency() / 4;
// Always allow at least 1 thread regardless of our settings
- const auto max_worker_count = std::max(1u, max_threads_unused);
+ const auto max_worker_count = std::max(1U, threads_used);
// Don't use more than MAX_THREADS
const auto worker_count = std::min(max_worker_count, MAX_THREADS);
async_shaders.AllocateWorkers(worker_count);
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() {
.flags = 0,
.renderPass = *renderpass,
.attachmentCount = 1,
+ .pAttachments = nullptr,
.width = size.width,
.height = size.height,
.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
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = queue_family,
+ .queueCount = 1,
+ .pQueuePriorities = nullptr,
});
- ci.queueCount = 1;
ci.pQueuePriorities = &QUEUE_PRIORITY;
}
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
}
const Specialization specialization{
+ .base_binding = 0,
.workgroup_size = key.workgroup_size,
.shared_memory_size = key.shared_memory_size,
+ .point_size = std::nullopt,
+ .enabled_attributes = {},
+ .attribute_types = {},
+ .ndc_minus_one_to_one = false,
};
const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute,
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
std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers(
VkRenderPass renderpass) {
- FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(),
- std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()};
+ FramebufferCacheKey key{
+ .renderpass = renderpass,
+ .width = std::numeric_limits<u32>::max(),
+ .height = std::numeric_limits<u32>::max(),
+ .layers = std::numeric_limits<u32>::max(),
+ .views = {},
+ };
const auto try_push = [&key](const View& view) {
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
VkSamplerCustomBorderColorCreateInfoEXT border{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
.pNext = nullptr,
+ .customBorderColor = {},
.format = VK_FORMAT_UNDEFINED,
};
std::memcpy(&border.customBorderColor, color.data(), sizeof(color));
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index efd4bb13b..2c6f54101 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -473,6 +473,8 @@ VkImageView CachedSurfaceView::GetAttachment() {
.aspectMask = aspect_mask,
.baseMipLevel = base_level,
.levelCount = num_levels,
+ .baseArrayLayer = 0,
+ .layerCount = 0,
},
};
if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) {
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index 9e5fe2374..9a98f0e98 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
SurfaceParams params;
params.is_tiled = tic.IsTiled();
params.srgb_conversion = tic.IsSrgbConversionEnabled();
- params.block_width = params.is_tiled ? tic.BlockWidth() : 0,
- params.block_height = params.is_tiled ? tic.BlockHeight() : 0,
- params.block_depth = params.is_tiled ? tic.BlockDepth() : 0,
+ params.block_width = params.is_tiled ? tic.BlockWidth() : 0;
+ params.block_height = params.is_tiled ? tic.BlockHeight() : 0;
+ params.block_depth = params.is_tiled ? tic.BlockDepth() : 0;
params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1;
params.pixel_format = lookup_table.GetPixelFormat(
tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type);
@@ -130,14 +130,13 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl
SurfaceParams params;
params.is_tiled = tic.IsTiled();
params.srgb_conversion = tic.IsSrgbConversionEnabled();
- params.block_width = params.is_tiled ? tic.BlockWidth() : 0,
- params.block_height = params.is_tiled ? tic.BlockHeight() : 0,
- params.block_depth = params.is_tiled ? tic.BlockDepth() : 0,
+ params.block_width = params.is_tiled ? tic.BlockWidth() : 0;
+ params.block_height = params.is_tiled ? tic.BlockHeight() : 0;
+ params.block_depth = params.is_tiled ? tic.BlockDepth() : 0;
params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1;
params.pixel_format = lookup_table.GetPixelFormat(
tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type);
params.type = GetFormatType(params.pixel_format);
- params.type = GetFormatType(params.pixel_format);
params.target = ImageTypeToSurfaceTarget(entry.type);
// TODO: on 1DBuffer we should use the tic info.
if (tic.IsBuffer()) {
@@ -167,27 +166,30 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl
SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) {
const auto& regs = system.GPU().Maxwell3D().regs;
- SurfaceParams params;
- params.is_tiled = regs.zeta.memory_layout.type ==
- Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
- params.srgb_conversion = false;
- params.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U);
- params.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U);
- params.block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
- params.tile_width_spacing = 1;
- params.pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
- params.type = GetFormatType(params.pixel_format);
- params.width = regs.zeta_width;
- params.height = regs.zeta_height;
- params.pitch = 0;
- params.num_levels = 1;
- params.emulated_levels = 1;
- const bool is_layered = regs.zeta_layers > 1 && params.block_depth == 0;
- params.is_layered = is_layered;
- params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
- params.depth = is_layered ? regs.zeta_layers.Value() : 1U;
- return params;
+ const auto block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
+ const bool is_layered = regs.zeta_layers > 1 && block_depth == 0;
+ const auto pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
+
+ return {
+ .is_tiled = regs.zeta.memory_layout.type ==
+ Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear,
+ .srgb_conversion = false,
+ .is_layered = is_layered,
+ .block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U),
+ .block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U),
+ .block_depth = block_depth,
+ .tile_width_spacing = 1,
+ .width = regs.zeta_width,
+ .height = regs.zeta_height,
+ .depth = is_layered ? regs.zeta_layers.Value() : 1U,
+ .pitch = 0,
+ .num_levels = 1,
+ .emulated_levels = 1,
+ .pixel_format = pixel_format,
+ .type = GetFormatType(pixel_format),
+ .target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D,
+ };
}
SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) {
@@ -233,24 +235,29 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
SurfaceParams SurfaceParams::CreateForFermiCopySurface(
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
- SurfaceParams params{};
- params.is_tiled = !config.linear;
- params.srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB ||
- config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB;
- params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 5U) : 0,
- params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 5U) : 0,
- params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 5U) : 0,
- params.tile_width_spacing = 1;
- params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
- params.type = GetFormatType(params.pixel_format);
- params.width = config.width;
- params.height = config.height;
- params.pitch = config.pitch;
- // TODO(Rodrigo): Try to guess texture arrays from parameters
- params.target = SurfaceTarget::Texture2D;
- params.depth = 1;
- params.num_levels = 1;
- params.emulated_levels = 1;
+ const bool is_tiled = !config.linear;
+ const auto pixel_format = PixelFormatFromRenderTargetFormat(config.format);
+
+ SurfaceParams params{
+ .is_tiled = is_tiled,
+ .srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB ||
+ config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB,
+ .block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U,
+ .block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U,
+ .block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U,
+ .tile_width_spacing = 1,
+ .width = config.width,
+ .height = config.height,
+ .depth = 1,
+ .pitch = config.pitch,
+ .num_levels = 1,
+ .emulated_levels = 1,
+ .pixel_format = pixel_format,
+ .type = GetFormatType(pixel_format),
+ // TODO(Rodrigo): Try to guess texture arrays from parameters
+ .target = SurfaceTarget::Texture2D,
+ };
+
params.is_layered = params.IsLayered();
return params;
}
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
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
renderer.RequestScreenshot(
screenshot_image.bits(),
- [=] {
+ [=, this] {
const std::string std_screenshot_path = screenshot_path.toStdString();
if (screenshot_image.mirrored(false, true).save(screenshot_path)) {
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() {
UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();
UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString();
- UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString();
UISettings::values.game_dir_deprecated =
ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString();
UISettings::values.game_dir_deprecated_deepscan =
@@ -666,8 +665,6 @@ void Config::ReadRendererValues() {
QStringLiteral("use_asynchronous_shaders"), false);
ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
true);
- ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false);
-
ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0);
ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0);
ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);
@@ -675,6 +672,22 @@ void Config::ReadRendererValues() {
qt_config->endGroup();
}
+void Config::ReadScreenshotValues() {
+ qt_config->beginGroup(QStringLiteral("Screenshots"));
+
+ UISettings::values.enable_screenshot_save_as =
+ ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool();
+ FileUtil::GetUserPath(
+ FileUtil::UserPath::ScreenshotsDir,
+ qt_config
+ ->value(QStringLiteral("screenshot_path"), QString::fromStdString(FileUtil::GetUserPath(
+ FileUtil::UserPath::ScreenshotsDir)))
+ .toString()
+ .toStdString());
+
+ qt_config->endGroup();
+}
+
void Config::ReadShortcutValues() {
qt_config->beginGroup(QStringLiteral("Shortcuts"));
@@ -756,6 +769,7 @@ void Config::ReadUIValues() {
ReadUIGamelistValues();
ReadUILayoutValues();
ReadPathValues();
+ ReadScreenshotValues();
ReadShortcutValues();
UISettings::values.single_window_mode =
@@ -1085,7 +1099,6 @@ void Config::SavePathValues() {
WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path);
WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path);
- WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path);
qt_config->beginWriteArray(QStringLiteral("gamedirs"));
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
qt_config->setArrayIndex(i);
@@ -1153,9 +1166,6 @@ void Config::SaveRendererValues() {
Settings::values.use_asynchronous_shaders, false);
WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
true);
- WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode,
- false);
-
// Cast to double because Qt's written float values are not human-readable
WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0);
WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0);
@@ -1164,6 +1174,17 @@ void Config::SaveRendererValues() {
qt_config->endGroup();
}
+void Config::SaveScreenshotValues() {
+ qt_config->beginGroup(QStringLiteral("Screenshots"));
+
+ WriteSetting(QStringLiteral("enable_screenshot_save_as"),
+ UISettings::values.enable_screenshot_save_as);
+ WriteSetting(QStringLiteral("screenshot_path"),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)));
+
+ qt_config->endGroup();
+}
+
void Config::SaveShortcutValues() {
qt_config->beginGroup(QStringLiteral("Shortcuts"));
@@ -1226,6 +1247,7 @@ void Config::SaveUIValues() {
SaveUIGamelistValues();
SaveUILayoutValues();
SavePathValues();
+ SaveScreenshotValues();
SaveShortcutValues();
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:
void ReadPathValues();
void ReadCpuValues();
void ReadRendererValues();
+ void ReadScreenshotValues();
void ReadShortcutValues();
void ReadSystemValues();
void ReadUIValues();
@@ -76,6 +77,7 @@ private:
void SavePathValues();
void SaveCpuValues();
void SaveRendererValues();
+ void SaveScreenshotValues();
void SaveShortcutValues();
void SaveSystemValues();
void SaveUIValues();
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() {
ui->use_vsync->setEnabled(runtime_lock);
ui->use_assembly_shaders->setEnabled(runtime_lock);
ui->use_asynchronous_shaders->setEnabled(runtime_lock);
- ui->force_30fps_mode->setEnabled(runtime_lock);
ui->anisotropic_filtering_combobox->setEnabled(runtime_lock);
ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue());
ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
- ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue());
if (Settings::configuring_global) {
ui->gpu_accuracy->setCurrentIndex(
@@ -78,9 +76,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
if (Settings::values.use_fast_gpu_time.UsingGlobal()) {
Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked());
}
- if (Settings::values.force_30fps_mode.UsingGlobal()) {
- Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked());
- }
if (Settings::values.max_anisotropy.UsingGlobal()) {
Settings::values.max_anisotropy.SetValue(
ui->anisotropic_filtering_combobox->currentIndex());
@@ -97,8 +92,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
use_asynchronous_shaders);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
ui->use_fast_gpu_time, use_fast_gpu_time);
- ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode,
- ui->force_30fps_mode, force_30fps_mode);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
ui->anisotropic_filtering_combobox);
@@ -132,7 +125,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ui->use_asynchronous_shaders->setEnabled(
Settings::values.use_asynchronous_shaders.UsingGlobal());
ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
- ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal());
ui->anisotropic_filtering_combobox->setEnabled(
Settings::values.max_anisotropy.UsingGlobal());
@@ -149,8 +141,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
Settings::values.use_asynchronous_shaders, use_asynchronous_shaders);
ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, "use_fast_gpu_time",
Settings::values.use_fast_gpu_time, use_fast_gpu_time);
- ConfigurationShared::SetColoredTristate(ui->force_30fps_mode, "force_30fps_mode",
- Settings::values.force_30fps_mode, force_30fps_mode);
ConfigurationShared::SetColoredComboBox(
ui->gpu_accuracy, ui->label_gpu_accuracy, "label_gpu_accuracy",
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:
ConfigurationShared::CheckState use_assembly_shaders;
ConfigurationShared::CheckState use_asynchronous_shaders;
ConfigurationShared::CheckState use_fast_gpu_time;
- ConfigurationShared::CheckState force_30fps_mode;
};
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 @@
</widget>
</item>
<item>
- <widget class="QCheckBox" name="force_30fps_mode">
- <property name="text">
- <string>Force 30 FPS mode</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QCheckBox" name="use_fast_gpu_time">
<property name="text">
<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..b1850bc95 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -280,9 +280,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
button->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(button, &QPushButton::clicked, [=] {
+ connect(button, &QPushButton::clicked, [=, this] {
HandleClick(button_map[button_id],
- [=](Common::ParamPackage params) {
+ [=, this](Common::ParamPackage params) {
// Workaround for ZL & ZR for analog triggers like on XBOX controllors.
// Analog triggers (from controllers like the XBOX controller) would not
// work due to a different range of their signals (from 0 to 255 on
@@ -300,19 +300,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
},
InputCommon::Polling::DeviceType::Button);
});
- connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
- QMenu context_menu;
- context_menu.addAction(tr("Clear"), [&] {
- buttons_param[button_id].Clear();
- button_map[button_id]->setText(tr("[not set]"));
- });
- context_menu.addAction(tr("Restore Default"), [&] {
- buttons_param[button_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
- button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
- });
- context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
- });
+ connect(button, &QPushButton::customContextMenuRequested,
+ [=, this](const QPoint& menu_location) {
+ QMenu context_menu;
+ context_menu.addAction(tr("Clear"), [&] {
+ buttons_param[button_id].Clear();
+ button_map[button_id]->setText(tr("[not set]"));
+ });
+ context_menu.addAction(tr("Restore Default"), [&] {
+ buttons_param[button_id] = Common::ParamPackage{
+ InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
+ button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
+ });
+ context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
+ });
}
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
@@ -323,16 +324,16 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(analog_button, &QPushButton::clicked, [=]() {
+ connect(analog_button, &QPushButton::clicked, [=, this] {
HandleClick(analog_map_buttons[analog_id][sub_button_id],
- [=](const Common::ParamPackage& params) {
+ [=, this](const Common::ParamPackage& params) {
SetAnalogButton(params, analogs_param[analog_id],
analog_sub_buttons[sub_button_id]);
},
InputCommon::Polling::DeviceType::Button);
});
connect(analog_button, &QPushButton::customContextMenuRequested,
- [=](const QPoint& menu_location) {
+ [=, this](const QPoint& menu_location) {
QMenu context_menu;
context_menu.addAction(tr("Clear"), [&] {
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
@@ -350,32 +351,35 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
menu_location));
});
}
- connect(analog_map_stick[analog_id], &QPushButton::clicked, [=] {
+ connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] {
if (QMessageBox::information(
this, tr("Information"),
tr("After pressing OK, first move your joystick horizontally, "
"and then vertically."),
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
- HandleClick(
- analog_map_stick[analog_id],
- [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
- InputCommon::Polling::DeviceType::Analog);
+ HandleClick(analog_map_stick[analog_id],
+ [=, this](const Common::ParamPackage& params) {
+ analogs_param[analog_id] = params;
+ },
+ InputCommon::Polling::DeviceType::Analog);
}
});
- connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] {
- const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value();
- if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
- analogs_param[analog_id].Get("engine", "") == "gcpad") {
- analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
- tr("Deadzone: %1%").arg(slider_value));
- analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
- } else {
- analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
- tr("Modifier Scale: %1%").arg(slider_value));
- analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
- }
- });
+ connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged,
+ [=, this] {
+ const float slider_value =
+ analog_map_deadzone_and_modifier_slider[analog_id]->value();
+ if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
+ analogs_param[analog_id].Get("engine", "") == "gcpad") {
+ analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
+ tr("Deadzone: %1%").arg(slider_value));
+ analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
+ } else {
+ analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
+ tr("Modifier Scale: %1%").arg(slider_value));
+ analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
+ }
+ });
}
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..ea2549363 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -83,25 +83,28 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
}
button->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(button, &QPushButton::clicked, [=] {
- HandleClick(
- button_map[button_id],
- [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
- InputCommon::Polling::DeviceType::Button);
- });
- connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
- QMenu context_menu;
- context_menu.addAction(tr("Clear"), [&] {
- buttons_param[button_id].Clear();
- button_map[button_id]->setText(tr("[not set]"));
- });
- context_menu.addAction(tr("Restore Default"), [&] {
- buttons_param[button_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
- button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
- });
- context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
+ connect(button, &QPushButton::clicked, [=, this] {
+ HandleClick(button_map[button_id],
+ [=, this](const Common::ParamPackage& params) {
+ buttons_param[button_id] = params;
+ },
+ InputCommon::Polling::DeviceType::Button);
});
+ connect(button, &QPushButton::customContextMenuRequested,
+ [=, this](const QPoint& menu_location) {
+ QMenu context_menu;
+ context_menu.addAction(tr("Clear"), [&] {
+ buttons_param[button_id].Clear();
+ button_map[button_id]->setText(tr("[not set]"));
+ });
+ context_menu.addAction(tr("Restore Default"), [&] {
+ buttons_param[button_id] =
+ Common::ParamPackage{InputCommon::GenerateKeyboardParam(
+ Config::default_mouse_buttons[button_id])};
+ button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
+ });
+ context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
+ });
}
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 @@
#include <array>
#include <utility>
+#include <QFileDialog>
#include <QDirIterator>
#include "common/common_types.h"
+#include "common/file_util.h"
#include "core/settings.h"
#include "ui_configure_ui.h"
#include "yuzu/configuration/configure_ui.h"
@@ -52,9 +54,21 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur
// Update text ComboBoxes after user interaction.
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated),
- [=]() { ConfigureUi::UpdateSecondRowComboBox(); });
+ [this] { ConfigureUi::UpdateSecondRowComboBox(); });
connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated),
- [=]() { ConfigureUi::UpdateFirstRowComboBox(); });
+ [this] { ConfigureUi::UpdateFirstRowComboBox(); });
+
+ // Set screenshot path to user specification.
+ connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] {
+ const QString& filename =
+ QFileDialog::getExistingDirectory(
+ this, tr("Select Screenshots Path..."),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))) +
+ QDir::separator();
+ if (!filename.isEmpty()) {
+ ui->screenshot_path_edit->setText(filename);
+ }
+ });
}
ConfigureUi::~ConfigureUi() = default;
@@ -66,6 +80,10 @@ void ConfigureUi::ApplyConfiguration() {
UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
+
+ UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked();
+ FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir,
+ ui->screenshot_path_edit->text().toStdString());
Settings::Apply();
}
@@ -80,6 +98,10 @@ void ConfigureUi::SetConfiguration() {
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
ui->icon_size_combobox->setCurrentIndex(
ui->icon_size_combobox->findData(UISettings::values.icon_size));
+
+ ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as);
+ ui->screenshot_path_edit->setText(
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)));
}
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 @@
<rect>
<x>0</x>
<y>0</y>
- <width>300</width>
- <height>377</height>
+ <width>363</width>
+ <height>391</height>
</rect>
</property>
<property name="windowTitle">
@@ -128,6 +128,47 @@
</widget>
</item>
<item>
+ <widget class="QGroupBox" name="screenshots_GroupBox">
+ <property name="title">
+ <string>Screenshots</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QCheckBox" name="enable_screenshot_save_as">
+ <property name="text">
+ <string>Ask Where To Save Screenshots (Windows Only)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Screenshots Path: </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="screenshot_path_edit"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="screenshot_path_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<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) {
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) {
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
- QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location"));
+ QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
QAction* open_transferable_shader_cache =
context_menu.addAction(tr("Open Transferable Shader Cache"));
context_menu.addSeparator();
+ QMenu* remove_menu = context_menu.addMenu(tr("Remove"));
+ QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update"));
+ QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC"));
+ QAction* remove_shader_cache = remove_menu->addAction(tr("Remove Shader Cache"));
+ QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
+ remove_menu->addSeparator();
+ QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
context_menu.addSeparator();
QAction* properties = context_menu.addAction(tr("Properties"));
- open_save_location->setEnabled(program_id != 0);
+ open_save_location->setVisible(program_id != 0);
+ open_mod_location->setVisible(program_id != 0);
+ open_transferable_shader_cache->setVisible(program_id != 0);
+ remove_update->setVisible(program_id != 0);
+ remove_dlc->setVisible(program_id != 0);
+ remove_shader_cache->setVisible(program_id != 0);
+ remove_all_content->setVisible(program_id != 0);
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
});
- connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() {
+ connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
emit OpenFolderRequested(GameListOpenTarget::ModData, path);
});
connect(open_transferable_shader_cache, &QAction::triggered,
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
+ connect(remove_all_content, &QAction::triggered, [this, program_id]() {
+ emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Game);
+ });
+ connect(remove_update, &QAction::triggered, [this, program_id]() {
+ emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Update);
+ });
+ connect(remove_dlc, &QAction::triggered, [this, program_id]() {
+ emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent);
+ });
+ connect(remove_shader_cache, &QAction::triggered, [this, program_id]() {
+ emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache);
+ });
+ connect(remove_custom_config, &QAction::triggered, [this, program_id]() {
+ emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration);
+ });
connect(dump_romfs, &QAction::triggered,
[this, program_id, path]() { emit DumpRomFSRequested(program_id, path); });
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 {
ModData,
};
+enum class GameListRemoveTarget {
+ ShaderCache,
+ CustomConfiguration,
+};
+
+enum class InstalledEntryType {
+ Game,
+ Update,
+ AddOnContent,
+};
+
class GameList : public QWidget {
Q_OBJECT
@@ -75,6 +86,8 @@ signals:
void ShouldCancelWorker();
void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
void OpenTransferableShaderCacheRequested(u64 program_id);
+ void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type);
+ void RemoveFileRequested(u64 program_id, GameListRemoveTarget target);
void DumpRomFSRequested(u64 program_id, const std::string& game_path);
void CopyTIDRequested(u64 program_id);
void NavigateToGamedbEntryRequested(u64 program_id,
@@ -117,8 +130,6 @@ private:
friend class GameListSearchField;
};
-Q_DECLARE_METATYPE(GameListOpenTarget);
-
class GameListPlaceholder : public QWidget {
Q_OBJECT
public:
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 2018150db..239016b94 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -369,8 +369,8 @@ void GameListWorker::run() {
auto* const game_list_dir = new GameListDir(game_dir);
emit DirEntryReady(game_list_dir);
provider->ClearAllEntries();
- ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), 2,
- game_list_dir);
+ ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(),
+ game_dir.deep_scan ? 256 : 0, game_list_dir);
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(),
game_dir.deep_scan ? 256 : 0, game_list_dir);
}
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() {
renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));
renderer_status_button->setCheckable(true);
renderer_status_button->setFocusPolicy(Qt::NoFocus);
- connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) {
+ connect(renderer_status_button, &QPushButton::toggled, [this](bool checked) {
renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL"));
});
renderer_status_button->toggle();
@@ -595,7 +595,7 @@ void GMainWindow::InitializeWidgets() {
#else
renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
Settings::RendererBackend::Vulkan);
- connect(renderer_status_button, &QPushButton::clicked, [=] {
+ connect(renderer_status_button, &QPushButton::clicked, [this] {
if (emulation_running) {
return;
}
@@ -847,6 +847,9 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
&GMainWindow::OnTransferableShaderCacheOpenFile);
+ connect(game_list, &GameList::RemoveInstalledEntryRequested, this,
+ &GMainWindow::OnGameListRemoveInstalledEntry);
+ connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
@@ -1257,7 +1260,6 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
case GameListOpenTarget::SaveData: {
open_target = tr("Save Data");
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
- ASSERT(program_id != 0);
if (has_user_save) {
// User save data
@@ -1322,14 +1324,12 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str
}
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
- ASSERT(program_id != 0);
-
const QString shader_dir =
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
- const QString tranferable_shader_cache_folder_path =
+ const QString transferable_shader_cache_folder_path =
shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
const QString transferable_shader_cache_file_path =
- tranferable_shader_cache_folder_path + QDir::separator() +
+ transferable_shader_cache_folder_path + QDir::separator() +
QString::fromStdString(fmt::format("{:016X}.bin", program_id));
if (!QFile::exists(transferable_shader_cache_file_path)) {
@@ -1350,7 +1350,7 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
param << QDir::toNativeSeparators(transferable_shader_cache_file_path);
QProcess::startDetached(explorer, param);
#else
- QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path));
+ QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path));
#endif
}
@@ -1394,6 +1394,174 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
return true;
}
+void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
+ const QString entry_type = [this, type] {
+ switch (type) {
+ case InstalledEntryType::Game:
+ return tr("Contents");
+ case InstalledEntryType::Update:
+ return tr("Update");
+ case InstalledEntryType::AddOnContent:
+ return tr("DLC");
+ default:
+ return QString{};
+ }
+ }();
+
+ if (QMessageBox::question(
+ this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
+ return;
+ }
+
+ switch (type) {
+ case InstalledEntryType::Game:
+ RemoveBaseContent(program_id, entry_type);
+ [[fallthrough]];
+ case InstalledEntryType::Update:
+ RemoveUpdateContent(program_id, entry_type);
+ if (type != InstalledEntryType::Game) {
+ break;
+ }
+ [[fallthrough]];
+ case InstalledEntryType::AddOnContent:
+ RemoveAddOnContent(program_id, entry_type);
+ break;
+ }
+ FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
+ "game_list");
+ game_list->PopulateAsync(UISettings::values.game_dirs);
+}
+
+void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) {
+ const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
+ const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) ||
+ fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id);
+
+ if (res) {
+ QMessageBox::information(this, tr("Successfully Removed"),
+ tr("Successfully removed the installed base game."));
+ } else {
+ QMessageBox::warning(
+ this, tr("Error Removing %1").arg(entry_type),
+ tr("The base game is not installed in the NAND and cannot be removed."));
+ }
+}
+
+void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) {
+ const auto update_id = program_id | 0x800;
+ const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
+ const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) ||
+ fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id);
+
+ if (res) {
+ QMessageBox::information(this, tr("Successfully Removed"),
+ tr("Successfully removed the installed update."));
+ } else {
+ QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
+ tr("There is no update installed for this title."));
+ }
+}
+
+void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) {
+ u32 count{};
+ const auto& fs_controller = Core::System::GetInstance().GetFileSystemController();
+ const auto dlc_entries = Core::System::GetInstance().GetContentProvider().ListEntriesFilter(
+ FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
+
+ for (const auto& entry : dlc_entries) {
+ if ((entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id) {
+ const auto res =
+ fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) ||
+ fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id);
+ if (res) {
+ ++count;
+ }
+ }
+ }
+
+ if (count == 0) {
+ QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type),
+ tr("There are no DLC installed for this title."));
+ return;
+ }
+
+ QMessageBox::information(this, tr("Successfully Removed"),
+ tr("Successfully removed %1 installed DLC.").arg(count));
+}
+
+void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) {
+ const QString question = [this, target] {
+ switch (target) {
+ case GameListRemoveTarget::ShaderCache:
+ return tr("Delete Transferable Shader Cache?");
+ case GameListRemoveTarget::CustomConfiguration:
+ return tr("Remove Custom Game Configuration?");
+ default:
+ return QString{};
+ }
+ }();
+
+ if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::No) != QMessageBox::Yes) {
+ return;
+ }
+
+ switch (target) {
+ case GameListRemoveTarget::ShaderCache:
+ RemoveTransferableShaderCache(program_id);
+ break;
+ case GameListRemoveTarget::CustomConfiguration:
+ RemoveCustomConfiguration(program_id);
+ break;
+ }
+}
+
+void GMainWindow::RemoveTransferableShaderCache(u64 program_id) {
+ const QString shader_dir =
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
+ const QString transferable_shader_cache_folder_path =
+ shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable");
+ const QString transferable_shader_cache_file_path =
+ transferable_shader_cache_folder_path + QDir::separator() +
+ QString::fromStdString(fmt::format("{:016X}.bin", program_id));
+
+ if (!QFile::exists(transferable_shader_cache_file_path)) {
+ QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
+ tr("A shader cache for this title does not exist."));
+ return;
+ }
+
+ if (QFile::remove(transferable_shader_cache_file_path)) {
+ QMessageBox::information(this, tr("Successfully Removed"),
+ tr("Successfully removed the transferable shader cache."));
+ } else {
+ QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
+ tr("Failed to remove the transferable shader cache."));
+ }
+}
+
+void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
+ const QString config_dir =
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir));
+ const QString custom_config_file_path =
+ config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
+
+ if (!QFile::exists(custom_config_file_path)) {
+ QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
+ tr("A custom configuration for this title does not exist."));
+ return;
+ }
+
+ if (QFile::remove(custom_config_file_path)) {
+ QMessageBox::information(this, tr("Successfully Removed"),
+ tr("Successfully removed the custom game configuration."));
+ } else {
+ QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
+ tr("Failed to remove the custom game configuration."));
+ }
+}
+
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
const auto failed = [this] {
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
@@ -1655,7 +1823,7 @@ void GMainWindow::OnMenuInstallToNAND() {
ui.action_Install_File_NAND->setEnabled(false);
- install_progress = new QProgressDialog(QStringLiteral(""), tr("Cancel"), 0, total_size, this);
+ install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this);
install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint &
~Qt::WindowMaximizeButtonHint);
install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
@@ -1705,18 +1873,18 @@ void GMainWindow::OnMenuInstallToNAND() {
install_progress->close();
const QString install_results =
- (new_files.isEmpty() ? QStringLiteral("")
+ (new_files.isEmpty() ? QString{}
: tr("%n file(s) were newly installed\n", "", new_files.size())) +
(overwritten_files.isEmpty()
- ? QStringLiteral("")
+ ? QString{}
: tr("%n file(s) were overwritten\n", "", overwritten_files.size())) +
- (failed_files.isEmpty() ? QStringLiteral("")
+ (failed_files.isEmpty() ? QString{}
: tr("%n file(s) failed to install\n", "", failed_files.size()));
QMessageBox::information(this, tr("Install Results"), install_results);
- game_list->PopulateAsync(UISettings::values.game_dirs);
FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
"game_list");
+ game_list->PopulateAsync(UISettings::values.game_dirs);
ui.action_Install_File_NAND->setEnabled(true);
}
@@ -2153,17 +2321,28 @@ void GMainWindow::OnToggleFilterBar() {
void GMainWindow::OnCaptureScreenshot() {
OnPauseGame();
- QFileDialog png_dialog(this, tr("Capture Screenshot"), UISettings::values.screenshot_path,
- tr("PNG Image (*.png)"));
- png_dialog.setAcceptMode(QFileDialog::AcceptSave);
- png_dialog.setDefaultSuffix(QStringLiteral("png"));
- if (png_dialog.exec()) {
- const QString path = png_dialog.selectedFiles().first();
- if (!path.isEmpty()) {
- UISettings::values.screenshot_path = QFileInfo(path).path();
- render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
+
+ const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
+ const auto screenshot_path =
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir));
+ const auto date =
+ QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz"));
+ QString filename = QStringLiteral("%1%2_%3.png")
+ .arg(screenshot_path)
+ .arg(title_id, 16, 16, QLatin1Char{'0'})
+ .arg(date);
+
+#ifdef _WIN32
+ if (UISettings::values.enable_screenshot_save_as) {
+ filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename,
+ tr("PNG Image (*.png)"));
+ if (filename.isEmpty()) {
+ OnStartGame();
+ return;
}
}
+#endif
+ render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, filename);
OnStartGame();
}
@@ -2202,8 +2381,7 @@ void GMainWindow::UpdateStatusBar() {
if (shaders_building != 0) {
shader_building_label->setText(
- tr("Building: %1 shader").arg(shaders_building) +
- (shaders_building != 1 ? QString::fromStdString("s") : QString::fromStdString("")));
+ tr("Building: %n shader(s)", "", static_cast<int>(shaders_building)));
shader_building_label->setVisible(true);
} else {
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;
class QProgressDialog;
class WaitTreeWidget;
enum class GameListOpenTarget;
+enum class GameListRemoveTarget;
+enum class InstalledEntryType;
class GameListPlaceholder;
namespace Core::Frontend {
@@ -198,6 +200,8 @@ private slots:
void OnGameListLoadFile(QString game_path);
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
void OnTransferableShaderCacheOpenFile(u64 program_id);
+ void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type);
+ void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target);
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
void OnGameListCopyTID(u64 program_id);
void OnGameListNavigateToGamedbEntry(u64 program_id,
@@ -229,6 +233,11 @@ private slots:
void OnLanguageChanged(const QString& locale);
private:
+ void RemoveBaseContent(u64 program_id, const QString& entry_type);
+ void RemoveUpdateContent(u64 program_id, const QString& entry_type);
+ void RemoveAddOnContent(u64 program_id, const QString& entry_type);
+ void RemoveTransferableShaderCache(u64 program_id);
+ void RemoveCustomConfiguration(u64 program_id);
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
InstallResult InstallNSPXCI(const QString& filename);
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 {
// Discord RPC
bool enable_discord_presence;
+ bool enable_screenshot_save_as;
u16 screenshot_resolution_factor;
QString roms_path;
QString symbols_path;
- QString screenshot_path;
QString game_dir_deprecated;
bool game_dir_deprecated_deepscan;
QVector<UISettings::GameDir> game_dirs;