diff options
28 files changed, 261 insertions, 187 deletions
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp index d72d67994..cee8b12dd 100644 --- a/src/audio_core/time_stretch.cpp +++ b/src/audio_core/time_stretch.cpp @@ -10,8 +10,7 @@ namespace AudioCore { -TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) - : m_sample_rate(sample_rate), m_channel_count(channel_count) { +TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} { m_sound_touch.setChannels(channel_count); m_sound_touch.setSampleRate(sample_rate); m_sound_touch.setPitch(1.0); diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h index decd760f1..bb2270b96 100644 --- a/src/audio_core/time_stretch.h +++ b/src/audio_core/time_stretch.h @@ -27,7 +27,6 @@ public: private: u32 m_sample_rate; - u32 m_channel_count; soundtouch::SoundTouch m_sound_touch; double m_stretch_ratio = 1.0; }; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 9f5918851..6d5218465 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -196,6 +196,7 @@ void FileBackend::Write(const Entry& entry) { SUB(Service, NFP) \ SUB(Service, NIFM) \ SUB(Service, NIM) \ + SUB(Service, NPNS) \ SUB(Service, NS) \ SUB(Service, NVDRV) \ SUB(Service, PCIE) \ @@ -204,10 +205,12 @@ void FileBackend::Write(const Entry& entry) { SUB(Service, PM) \ SUB(Service, PREPO) \ SUB(Service, PSC) \ + SUB(Service, PSM) \ SUB(Service, SET) \ SUB(Service, SM) \ SUB(Service, SPL) \ SUB(Service, SSL) \ + SUB(Service, TCAP) \ SUB(Service, Time) \ SUB(Service, USB) \ SUB(Service, VI) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index c9161155a..d4ec31ec3 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -83,6 +83,7 @@ enum class Class : ClassType { Service_NFP, ///< The NFP service Service_NIFM, ///< The NIFM (Network interface) service Service_NIM, ///< The NIM service + Service_NPNS, ///< The NPNS service Service_NS, ///< The NS services Service_NVDRV, ///< The NVDRV (Nvidia driver) service Service_PCIE, ///< The PCIe service @@ -96,6 +97,7 @@ enum class Class : ClassType { Service_SM, ///< The SM (Service manager) service Service_SPL, ///< The SPL service Service_SSL, ///< The SSL service + Service_TCAP, ///< The TCAP service. Service_Time, ///< The time service Service_USB, ///< The USB (Universal Serial Bus) service Service_VI, ///< The VI (Video interface) service diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index fd0786068..fefc3c747 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -713,7 +713,6 @@ void KeyManager::DeriveBase() { const auto sbk = GetKey(S128KeyType::SecureBoot); const auto tsec = GetKey(S128KeyType::TSEC); - const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master)); for (size_t i = 0; i < revisions.size(); ++i) { if (!revisions[i]) diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index bfe50da73..3824c74e0 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo std::vector<u8> temp(std::min(block_size, src->GetSize())); for (std::size_t i = 0; i < src->GetSize(); i += block_size) { const auto read = std::min(block_size, src->GetSize() - i); - const auto block = src->Read(temp.data(), read, i); - if (dest->Write(temp.data(), read, i) != read) + if (src->Read(temp.data(), read, i) != read) { return false; + } + + if (dest->Write(temp.data(), read, i) != read) { + return false; + } } return true; diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 06f7d1b15..3cac1b4ff 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <random> -#include <boost/optional.hpp> + #include "common/file_util.h" #include "core/hle/service/acc/profile_manager.h" #include "core/settings.h" @@ -58,11 +58,11 @@ ProfileManager::~ProfileManager() { /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the /// internal management of the users profiles -boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { +std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { if (user_count >= MAX_USERS) { - return boost::none; + return {}; } - profiles[user_count] = user; + profiles[user_count] = profile; return user_count++; } @@ -81,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { /// Helper function to register a user to the system ResultCode ProfileManager::AddUser(const ProfileInfo& user) { - if (AddToProfiles(user) == boost::none) { + if (!AddToProfiles(user)) { return ERROR_TOO_MANY_USERS; } return RESULT_SUCCESS; @@ -126,37 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) return CreateNewUser(uuid, username_output); } -boost::optional<UUID> ProfileManager::GetUser(std::size_t index) const { - if (index >= MAX_USERS) - return boost::none; +std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { + if (index >= MAX_USERS) { + return {}; + } + return profiles[index].user_uuid; } /// Returns a users profile index based on their user id. -boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { +std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { if (!uuid) { - return boost::none; + return {}; } - auto iter = std::find_if(profiles.begin(), profiles.end(), - [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); + + const auto iter = std::find_if(profiles.begin(), profiles.end(), + [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); if (iter == profiles.end()) { - return boost::none; + return {}; } + return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); } /// Returns a users profile index based on their profile -boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { +std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { return GetUserIndex(user.user_uuid); } /// Returns the data structure used by the switch when GetProfileBase is called on acc:* -bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, - ProfileBase& profile) const { - if (index == boost::none || index >= MAX_USERS) { +bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { + if (!index || index >= MAX_USERS) { return false; } - const auto& prof_info = profiles[index.get()]; + const auto& prof_info = profiles[*index]; profile.user_uuid = prof_info.user_uuid; profile.username = prof_info.username; profile.timestamp = prof_info.creation_time; @@ -165,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, /// Returns the data structure used by the switch when GetProfileBase is called on acc:* bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { - auto idx = GetUserIndex(uuid); + const auto idx = GetUserIndex(uuid); return GetProfileBase(idx, profile); } @@ -192,7 +195,7 @@ std::size_t ProfileManager::GetOpenUserCount() const { /// Checks if a user id exists in our profile manager bool ProfileManager::UserExists(UUID uuid) const { - return (GetUserIndex(uuid) != boost::none); + return GetUserIndex(uuid) != std::nullopt; } bool ProfileManager::UserExistsIndex(std::size_t index) const { @@ -203,21 +206,23 @@ bool ProfileManager::UserExistsIndex(std::size_t index) const { /// Opens a specific user void ProfileManager::OpenUser(UUID uuid) { - auto idx = GetUserIndex(uuid); - if (idx == boost::none) { + const auto idx = GetUserIndex(uuid); + if (!idx) { return; } - profiles[idx.get()].is_open = true; + + profiles[*idx].is_open = true; last_opened_user = uuid; } /// Closes a specific user void ProfileManager::CloseUser(UUID uuid) { - auto idx = GetUserIndex(uuid); - if (idx == boost::none) { + const auto idx = GetUserIndex(uuid); + if (!idx) { return; } - profiles[idx.get()].is_open = false; + + profiles[*idx].is_open = false; } /// Gets all valid user ids on the system @@ -247,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const { } /// Return the users profile base and the unknown arbitary data. -bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, +bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const { if (GetProfileBase(index, profile)) { - data = profiles[index.get()].data; + data = profiles[*index].data; return true; } return false; @@ -259,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const { - auto idx = GetUserIndex(uuid); + const auto idx = GetUserIndex(uuid); return GetProfileBaseAndData(idx, profile, data); } @@ -277,8 +282,8 @@ bool ProfileManager::CanSystemRegisterUser() const { } bool ProfileManager::RemoveUser(UUID uuid) { - auto index = GetUserIndex(uuid); - if (index == boost::none) { + const auto index = GetUserIndex(uuid); + if (!index) { return false; } @@ -289,8 +294,8 @@ bool ProfileManager::RemoveUser(UUID uuid) { } bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { - auto index = GetUserIndex(uuid); - if (profile_new.user_uuid == UUID(INVALID_UUID) || index == boost::none) { + const auto index = GetUserIndex(uuid); + if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) { return false; } diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 235208d56..1cd2e51b2 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -5,8 +5,8 @@ #pragma once #include <array> +#include <optional> -#include "boost/optional.hpp" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/result.h" @@ -96,13 +96,13 @@ public: ResultCode AddUser(const ProfileInfo& user); ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); ResultCode CreateNewUser(UUID uuid, const std::string& username); - boost::optional<UUID> GetUser(std::size_t index) const; - boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; - boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; - bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; + std::optional<UUID> GetUser(std::size_t index) const; + std::optional<std::size_t> GetUserIndex(const UUID& uuid) const; + std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; + bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; bool GetProfileBase(UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; - bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, + bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, @@ -120,16 +120,16 @@ public: bool CanSystemRegisterUser() const; bool RemoveUser(UUID uuid); - bool SetProfileBase(UUID uuid, const ProfileBase& profile); + bool SetProfileBase(UUID uuid, const ProfileBase& profile_new); private: void ParseUserSaveFile(); void WriteUserSaveFile(); + std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); + bool RemoveProfileAtIndex(std::size_t index); std::array<ProfileInfo, MAX_USERS> profiles{}; std::size_t user_count = 0; - boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); - bool RemoveProfileAtIndex(std::size_t index); UUID last_opened_user{INVALID_UUID}; }; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4ed66d817..59aafd616 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -743,7 +743,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { Account::ProfileManager profile_manager{}; const auto uuid = profile_manager.GetUser(Settings::values.current_user); - ASSERT(uuid != boost::none); + ASSERT(uuid != std::nullopt); params.current_user = uuid->uuid; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 428069df2..54305cf05 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -24,8 +24,8 @@ namespace Service::AOC { constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; -static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { - return (aoc & DLC_BASE_TITLE_ID_MASK) == base; +static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { + return (title_id & DLC_BASE_TITLE_ID_MASK) == base; } static std::vector<u64> AccumulateAOCTitleIDs() { @@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); rb.Push<u32>(static_cast<u32>( std::count_if(add_on_content.begin(), add_on_content.end(), - [¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); + [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); } void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index b06e65a77..4b4d1324f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -108,9 +108,10 @@ void Controller_NPad::OnInit() { styleset_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); - if (!IsControllerActivated()) + if (!IsControllerActivated()) { return; - std::size_t controller{}; + } + if (style.raw == 0) { // We want to support all controllers style.handheld.Assign(1); diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 7d95816fe..c716a462b 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -74,10 +74,6 @@ double PerfStats::GetLastFrameTimeScale() { } void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { - // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher - // values increase the time needed to recover and limit framerate again after spikes. - constexpr microseconds MAX_LAG_TIME_US = 25000us; - if (!Settings::values.use_frame_limit) { return; } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index bca014a4a..78ba29fc1 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -155,7 +155,6 @@ void Maxwell3D::ProcessQueryGet() { ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, "Units other than CROP are unimplemented"); - u32 value = Memory::Read32(*address); u64 result = 0; // TODO(Subv): Support the other query variables diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 6cd08d28b..af7756266 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -79,6 +79,7 @@ union Attribute { constexpr explicit Attribute(u64 value) : value(value) {} enum class Index : u64 { + PointSize = 6, Position = 7, Attribute_0 = 8, Attribute_31 = 39, diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 9c8925383..591ec7998 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -78,6 +78,29 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { } } +std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { + const u32 compression_factor{GetCompressionFactor(pixel_format)}; + const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; + u32 m_depth = (layer_only ? 1U : depth); + u32 m_width = std::max(1U, width / compression_factor); + u32 m_height = std::max(1U, height / compression_factor); + std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, + m_depth, block_height, block_depth); + u32 m_block_height = block_height; + u32 m_block_depth = block_depth; + std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size + for (u32 i = 1; i < max_mip_level; i++) { + m_width = std::max(1U, m_width / 2); + m_height = std::max(1U, m_height / 2); + m_depth = std::max(1U, m_depth / 2); + m_block_height = std::max(1U, m_block_height / 2); + m_block_depth = std::max(1U, m_block_depth / 2); + size += Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_depth, + m_block_height, m_block_depth); + } + return is_tiled ? Common::AlignUp(size, block_size_bytes) : size; +} + /*static*/ SurfaceParams SurfaceParams::CreateForTexture( const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { SurfaceParams params{}; @@ -124,6 +147,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { break; } + params.is_layered = SurfaceTargetIsLayered(params.target); params.max_mip_level = config.tic.max_mip_level + 1; params.rt = {}; @@ -150,6 +174,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.target = SurfaceTarget::Texture2D; params.depth = 1; params.max_mip_level = 0; + params.is_layered = false; // Render target specific parameters, not used for caching params.rt.index = static_cast<u32>(index); @@ -182,6 +207,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.target = SurfaceTarget::Texture2D; params.depth = 1; params.max_mip_level = 0; + params.is_layered = false; params.rt = {}; params.InitCacheParameters(zeta_address); @@ -361,10 +387,11 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d } } -static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), - SurfaceParams::MaxPixelFormat> - morton_to_gl_fns = { - // clang-format off +using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), + SurfaceParams::MaxPixelFormat>; + +static constexpr GLConversionArray morton_to_gl_fns = { + // clang-format off MortonCopy<true, PixelFormat::ABGR8U>, MortonCopy<true, PixelFormat::ABGR8S>, MortonCopy<true, PixelFormat::ABGR8UI>, @@ -418,13 +445,11 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32FS8>, - // clang-format on + // clang-format on }; -static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), - SurfaceParams::MaxPixelFormat> - gl_to_morton_fns = { - // clang-format off +static constexpr GLConversionArray gl_to_morton_fns = { + // clang-format off MortonCopy<false, PixelFormat::ABGR8U>, MortonCopy<false, PixelFormat::ABGR8S>, MortonCopy<false, PixelFormat::ABGR8UI>, @@ -479,9 +504,35 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, MortonCopy<false, PixelFormat::Z24S8>, MortonCopy<false, PixelFormat::S8Z24>, MortonCopy<false, PixelFormat::Z32FS8>, - // clang-format on + // clang-format on }; +void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, + std::vector<u8>& gl_buffer) { + u32 depth = params.depth; + if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { + // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. + depth = 1U; + } + if (params.is_layered) { + u64 offset = 0; + u64 offset_gl = 0; + u64 layer_size = params.LayerMemorySize(); + u64 gl_size = params.LayerSizeGL(); + for (u32 i = 0; i < depth; i++) { + functions[static_cast<std::size_t>(params.pixel_format)]( + params.width, params.block_height, params.height, params.block_depth, 1, + gl_buffer.data() + offset_gl, gl_size, params.addr + offset); + offset += layer_size; + offset_gl += gl_size; + } + } else { + functions[static_cast<std::size_t>(params.pixel_format)]( + params.width, params.block_height, params.height, params.block_depth, depth, + gl_buffer.data(), gl_buffer.size(), params.addr); + } +} + static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { @@ -881,21 +932,10 @@ void CachedSurface::LoadGLBuffer() { gl_buffer.resize(params.size_in_bytes_gl); if (params.is_tiled) { - u32 depth = params.depth; - u32 block_depth = params.block_depth; - ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast<u32>(params.target)); - if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { - // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. - depth = 1U; - block_depth = 1U; - } - - morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( - params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), - gl_buffer.size(), params.addr); + SwizzleFunc(morton_to_gl_fns, params, gl_buffer); } else { const auto texture_src_data{Memory::GetPointer(params.addr)}; const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; @@ -929,19 +969,10 @@ void CachedSurface::FlushGLBuffer() { const u8* const texture_src_data = Memory::GetPointer(params.addr); ASSERT(texture_src_data); if (params.is_tiled) { - u32 depth = params.depth; - u32 block_depth = params.block_depth; - ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast<u32>(params.target)); - if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { - // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. - depth = 1U; - } - gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( - params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), - gl_buffer.size(), GetAddr()); + SwizzleFunc(gl_to_morton_fns, params, gl_buffer); } else { std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); } @@ -1179,7 +1210,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface) { const auto& src_params{src_surface->GetSurfaceParams()}; const auto& dst_params{dst_surface->GetSurfaceParams()}; - FlushRegion(src_params.addr, dst_params.size_in_bytes); + FlushRegion(src_params.addr, dst_params.MemorySize()); LoadSurface(dst_surface); } @@ -1221,44 +1252,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, CopySurface(old_surface, new_surface, copy_pbo.handle); } break; + case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceParams::SurfaceTarget::Texture3D: AccurateCopySurface(old_surface, new_surface); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: { - if (old_params.rt.array_mode != 1) { - // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this - // yet (array rendering used as a cubemap texture). - LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode); - UNREACHABLE(); - return new_surface; - } - - // This seems to be used for render-to-cubemap texture - ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected"); - ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected"); - ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented"); - - // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels. - // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild. - const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)}; - - for (std::size_t index = 0; index < new_params.depth; ++index) { - Surface face_surface{TryGetReservedSurface(old_params)}; - ASSERT_MSG(face_surface, "Unexpected"); - - if (is_blit) { - BlitSurface(face_surface, new_surface, read_framebuffer.handle, - draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index, - new_params.rt.index, index); - } else { - CopySurface(face_surface, new_surface, copy_pbo.handle, - face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index); - } - - old_params.addr += byte_stride; - } - break; - } default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast<u32>(new_params.target)); @@ -1266,7 +1263,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, } return new_surface; -} +} // namespace OpenGL Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { return TryGet(addr); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0dd0d90a3..50a7ab47d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -168,6 +168,23 @@ struct SurfaceParams { } } + static bool SurfaceTargetIsLayered(SurfaceTarget target) { + switch (target) { + case SurfaceTarget::Texture1D: + case SurfaceTarget::Texture2D: + case SurfaceTarget::Texture3D: + return false; + case SurfaceTarget::Texture1DArray: + case SurfaceTarget::Texture2DArray: + case SurfaceTarget::TextureCubemap: + return true; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); + UNREACHABLE(); + return false; + } + } + /** * Gets the compression factor for the specified PixelFormat. This applies to just the * "compressed width" and "compressed height", not the overall compression factor of a @@ -742,6 +759,25 @@ struct SurfaceParams { return size_in_bytes_gl / 6; } + /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps. + std::size_t MemorySize() const { + std::size_t size = InnerMemorySize(is_layered); + if (is_layered) + return size * depth; + return size; + } + + /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including + /// mipmaps. + std::size_t LayerMemorySize() const { + return InnerMemorySize(true); + } + + /// Returns the size of a layer of this surface in OpenGL. + std::size_t LayerSizeGL() const { + return SizeInBytesRaw(true) / depth; + } + /// Creates SurfaceParams from a texture configuration static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry); @@ -782,6 +818,7 @@ struct SurfaceParams { u32 unaligned_height; SurfaceTarget target; u32 max_mip_level; + bool is_layered; // Parameters used for caching VAddr addr; @@ -797,6 +834,9 @@ struct SurfaceParams { u32 layer_stride; u32 base_layer; } rt; + +private: + std::size_t InnerMemorySize(bool layer_only = false) const; }; }; // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index fe4d1bd83..81ffb24e4 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -6,6 +6,7 @@ #include <set> #include <string> #include <string_view> +#include <unordered_set> #include <boost/optional.hpp> #include <fmt/format.h> @@ -276,7 +277,8 @@ public: GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, const Tegra::Shader::Header& header) - : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} { + : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header}, + fixed_pipeline_output_attributes_used{} { BuildRegisterList(); BuildInputList(); } @@ -480,7 +482,12 @@ public: std::to_string(static_cast<u32>(attribute)) + ']' + GetSwizzle(elem) + " = " + src + ';'); } else { - shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); + if (attribute == Attribute::Index::PointSize) { + fixed_pipeline_output_attributes_used.insert(attribute); + shader.AddLine(dest + " = " + src + ';'); + } else { + shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); + } } } } @@ -524,6 +531,7 @@ public: /// Add declarations. void GenerateDeclarations(const std::string& suffix) { + GenerateVertex(); GenerateRegisters(suffix); GenerateInternalFlags(); GenerateInputAttrs(); @@ -683,6 +691,20 @@ private: declarations.AddNewLine(); } + void GenerateVertex() { + if (stage != Maxwell3D::Regs::ShaderStage::Vertex) + return; + declarations.AddLine("out gl_PerVertex {"); + ++declarations.scope; + declarations.AddLine("vec4 gl_Position;"); + for (auto& o : fixed_pipeline_output_attributes_used) { + if (o == Attribute::Index::PointSize) + declarations.AddLine("float gl_PointSize;"); + } + --declarations.scope; + declarations.AddLine("};"); + } + /// Generates code representing a temporary (GPR) register. std::string GetRegister(const Register& reg, unsigned elem) { if (reg == Register::ZeroIndex) { @@ -836,6 +858,8 @@ private: /// Generates code representing the declaration name of an output attribute register. std::string GetOutputAttribute(Attribute::Index attribute) { switch (attribute) { + case Attribute::Index::PointSize: + return "gl_PointSize"; case Attribute::Index::Position: return "position"; default: @@ -870,6 +894,7 @@ private: const Maxwell3D::Regs::ShaderStage& stage; const std::string& suffix; const Tegra::Shader::Header& header; + std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used; }; class GLSLGenerator { diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index e883ffb1d..dfb562706 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -19,9 +19,6 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) { out += Decompiler::GetCommonDeclarations(); out += R"( -out gl_PerVertex { - vec4 gl_Position; -}; layout (location = 0) out vec4 position; diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index f1b40e7f5..550ca856c 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -142,7 +142,6 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 blocks_on_x = div_ceil(width, block_x_elements); const u32 blocks_on_y = div_ceil(height, block_y_elements); const u32 blocks_on_z = div_ceil(depth, block_z_elements); - const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z; const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; const u32 xy_block_size = gob_size * block_height; const u32 block_size = xy_block_size * block_depth; @@ -320,13 +319,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth) { if (tiled) { - const u32 gobs_in_x = 64 / bytes_per_pixel; + const u32 gobs_in_x = 64; const u32 gobs_in_y = 8; const u32 gobs_in_z = 1; - const u32 aligned_width = Common::AlignUp(width, gobs_in_x); + const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); - return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; + return aligned_width * aligned_height * aligned_depth; } else { return width * height * depth * bytes_per_pixel; } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e8ab23326..39eef8858 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -8,7 +8,6 @@ #include "common/microprofile.h" #include "common/scm_rev.h" -#include "common/string_util.h" #include "core/core.h" #include "core/frontend/framebuffer_layout.h" #include "core/settings.h" @@ -107,9 +106,8 @@ private: GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : QWidget(parent), child(nullptr), emu_thread(emu_thread) { - std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, - Common::g_scm_branch, Common::g_scm_desc); - setWindowTitle(QString::fromStdString(window_title)); + setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") + .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); setAttribute(Qt::WA_AcceptTouchEvents); InputCommon::Init(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1fe9a7edd..d4fd60a73 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -13,11 +13,16 @@ Config::Config() { // TODO: Don't hardcode the path; let the frontend decide where to put the config files. qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; FileUtil::CreateFullPath(qt_config_loc); - qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); + qt_config = + std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); Reload(); } +Config::~Config() { + Save(); +} + const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, @@ -342,9 +347,3 @@ void Config::Reload() { void Config::Save() { SaveValues(); } - -Config::~Config() { - Save(); - - delete qt_config; -} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index cbf745ea2..9c99c1b75 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include <memory> #include <string> #include <QVariant> #include "core/settings.h" @@ -12,12 +13,6 @@ class QSettings; class Config { - QSettings* qt_config; - std::string qt_config_loc; - - void ReadValues(); - void SaveValues(); - public: Config(); ~Config(); @@ -27,4 +22,11 @@ public: static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; + +private: + void ReadValues(); + void SaveValues(); + + std::unique_ptr<QSettings> qt_config; + std::string qt_config_loc; }; diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 83cc49dfc..0bc307e99 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -153,7 +153,7 @@ void ConfigureSystem::UpdateCurrentUser() { ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); const auto& current_user = profile_manager->GetUser(Settings::values.current_user); - ASSERT(current_user != boost::none); + ASSERT(current_user != std::nullopt); const auto username = GetAccountUsername(*current_user); scene->clear(); @@ -252,7 +252,7 @@ void ConfigureSystem::AddUser() { void ConfigureSystem::RenameUser() { const auto user = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(user); - ASSERT(uuid != boost::none); + ASSERT(uuid != std::nullopt); const auto username = GetAccountUsername(*uuid); Service::Account::ProfileBase profile; @@ -292,7 +292,7 @@ void ConfigureSystem::RenameUser() { void ConfigureSystem::DeleteUser() { const auto index = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(index); - ASSERT(uuid != boost::none); + ASSERT(uuid != std::nullopt); const auto username = GetAccountUsername(*uuid); const auto confirm = @@ -320,7 +320,7 @@ void ConfigureSystem::DeleteUser() { void ConfigureSystem::SetUserImage() { const auto index = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(index); - ASSERT(uuid != boost::none); + ASSERT(uuid != std::nullopt); const auto username = GetAccountUsername(*uuid); const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp index b5c88f944..67ed0ba6d 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <map> #include <QLabel> #include <QMetaType> #include <QPushButton> @@ -32,21 +31,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const { switch (role) { case Qt::DisplayRole: { if (index.column() == 0) { - static const std::map<Tegra::DebugContext::Event, QString> map = { - {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")}, - {Tegra::DebugContext::Event::MaxwellCommandProcessed, - tr("Maxwell command processed")}, - {Tegra::DebugContext::Event::IncomingPrimitiveBatch, - tr("Incoming primitive batch")}, - {Tegra::DebugContext::Event::FinishedPrimitiveBatch, - tr("Finished primitive batch")}, - }; - - DEBUG_ASSERT(map.size() == - static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents)); - return (map.find(event) != map.end()) ? map.at(event) : QString(); + return DebugContextEventToString(event); } - break; } @@ -128,6 +114,23 @@ void BreakPointModel::OnResumed() { active_breakpoint = context->active_breakpoint; } +QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) { + switch (event) { + case Tegra::DebugContext::Event::MaxwellCommandLoaded: + return tr("Maxwell command loaded"); + case Tegra::DebugContext::Event::MaxwellCommandProcessed: + return tr("Maxwell command processed"); + case Tegra::DebugContext::Event::IncomingPrimitiveBatch: + return tr("Incoming primitive batch"); + case Tegra::DebugContext::Event::FinishedPrimitiveBatch: + return tr("Finished primitive batch"); + case Tegra::DebugContext::Event::NumEvents: + break; + } + + return tr("Unknown debug context event"); +} + GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h index 7112b87e6..fb488e38f 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h +++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h @@ -29,6 +29,8 @@ public: void OnResumed(); private: + static QString DebugContextEventToString(Tegra::DebugContext::Event event); + std::weak_ptr<Tegra::DebugContext> context_weak; bool at_breakpoint; Tegra::DebugContext::Event active_breakpoint; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 67890455a..a5a4aa432 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -16,7 +16,6 @@ #include <fmt/format.h> #include "common/common_paths.h" #include "common/common_types.h" -#include "common/file_util.h" #include "common/logging/log.h" #include "core/file_sys/patch_manager.h" #include "yuzu/compatibility_list.h" @@ -217,11 +216,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) tree_view->setContextMenuPolicy(Qt::CustomContextMenu); item_model->insertColumns(0, COLUMN_COUNT); - item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); - item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); - item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); - item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); - item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); + item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); + item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); + item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); + item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); + item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); @@ -387,9 +386,9 @@ void GameList::LoadCompatibilityList() { } void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { - if (!FileUtil::Exists(dir_path.toStdString()) || - !FileUtil::IsDirectory(dir_path.toStdString())) { - LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data()); + const QFileInfo dir_info{dir_path}; + if (!dir_info.exists() || !dir_info.isDir()) { + LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toStdString()); search_field->setFilterResult(0, 0); return; } diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 3881aba5f..3d865a12d 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -62,19 +62,24 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, FileSys::VirtualFile update_raw; loader.ReadUpdateRaw(update_raw); for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { - if (!updatable && kv.first == "Update") + const bool is_update = kv.first == "Update"; + if (!updatable && is_update) { continue; + } + + const QString type = QString::fromStdString(kv.first); if (kv.second.empty()) { - out.append(fmt::format("{}\n", kv.first).c_str()); + out.append(QStringLiteral("%1\n").arg(type)); } else { auto ver = kv.second; // Display container name for packed updates - if (ver == "PACKED" && kv.first == "Update") + if (is_update && ver == "PACKED") { ver = Loader::GetFileTypeString(loader.GetFileType()); + } - out.append(fmt::format("{} ({})\n", kv.first, ver).c_str()); + out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver))); } } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 47f494841..55508b1e1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -785,7 +785,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target ASSERT(index != -1 && index < 8); const auto user_id = manager.GetUser(index); - ASSERT(user_id != boost::none); + ASSERT(user_id != std::nullopt); path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0); |