diff options
-rwxr-xr-x | .travis/macos/build.sh | 2 | ||||
-rw-r--r-- | src/audio_core/cubeb_sink.cpp | 3 | ||||
-rw-r--r-- | src/core/crypto/key_manager.cpp | 2 | ||||
-rw-r--r-- | src/core/file_sys/patch_manager.cpp | 13 | ||||
-rw-r--r-- | src/core/hle/service/filesystem/fsp_srv.cpp | 28 | ||||
-rw-r--r-- | src/core/loader/nsp.cpp | 37 | ||||
-rw-r--r-- | src/video_core/engines/maxwell_3d.h | 17 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 21 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 3 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 95 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.h | 140 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.cpp | 28 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.h | 19 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 14 | ||||
-rw-r--r-- | src/video_core/textures/texture.h | 5 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_system.cpp | 121 |
16 files changed, 426 insertions, 122 deletions
diff --git a/.travis/macos/build.sh b/.travis/macos/build.sh index dce12099b..19fb5d445 100755 --- a/.travis/macos/build.sh +++ b/.travis/macos/build.sh @@ -2,7 +2,7 @@ set -o pipefail -export MACOSX_DEPLOYMENT_TARGET=10.13 +export MACOSX_DEPLOYMENT_TARGET=10.14 export Qt5_DIR=$(brew --prefix)/opt/qt5 export UNICORNDIR=$(pwd)/externals/unicorn export PATH="/usr/local/opt/ccache/libexec:$PATH" diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 392039688..d31a1c844 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp @@ -121,7 +121,8 @@ CubebSink::CubebSink(std::string target_device_name) { const auto collection_end{collection.device + collection.count}; const auto device{ std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { - return target_device_name == info.friendly_name; + return info.friendly_name != nullptr && + target_device_name == info.friendly_name; })}; if (device != collection_end) { output_device = device->devid; diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index fefc3c747..89ae79eb3 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -395,7 +395,7 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_ if (base.size() < begin + length) return false; return std::all_of(base.begin() + begin, base.begin() + begin + length, - [](u8 c) { return std::isdigit(c); }); + [](u8 c) { return std::isxdigit(c); }); } void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 1f4928562..cb457b987 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -61,13 +61,12 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { // Game Updates const auto update_tid = GetUpdateTitleID(title_id); const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); - if (update != nullptr) { - if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && - update->GetExeFS() != nullptr) { - LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", - FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); - exefs = update->GetExeFS(); - } + + if (update != nullptr && update->GetExeFS() != nullptr && + update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", + FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); + exefs = update->GetExeFS(); } return exefs; diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index c87721c39..c1c83a11d 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -273,8 +273,8 @@ public: {0, &IFileSystem::CreateFile, "CreateFile"}, {1, &IFileSystem::DeleteFile, "DeleteFile"}, {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, - {3, nullptr, "DeleteDirectory"}, - {4, nullptr, "DeleteDirectoryRecursively"}, + {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, + {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, {5, &IFileSystem::RenameFile, "RenameFile"}, {6, nullptr, "RenameDirectory"}, {7, &IFileSystem::GetEntryType, "GetEntryType"}, @@ -329,6 +329,30 @@ public: rb.Push(backend.CreateDirectory(name)); } + void DeleteDirectory(Kernel::HLERequestContext& ctx) { + const IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called directory {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectory(name)); + } + + void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) { + const IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called directory {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectoryRecursively(name)); + } + void RenameFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 13e57848d..080d89904 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -36,6 +36,16 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) std::tie(nacp_file, icon_file) = FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); + + if (nsp->IsExtractedType()) { + secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); + } else { + if (title_id == 0) + return; + + secondary_loader = std::make_unique<AppLoader_NCA>( + nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); + } } AppLoader_NSP::~AppLoader_NSP() = default; @@ -67,26 +77,19 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) { return ResultStatus::ErrorAlreadyLoaded; } - if (nsp->IsExtractedType()) { - secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); - } else { - if (title_id == 0) - return ResultStatus::ErrorNSPMissingProgramNCA; - - secondary_loader = std::make_unique<AppLoader_NCA>( - nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); + if (title_id == 0) + return ResultStatus::ErrorNSPMissingProgramNCA; - if (nsp->GetStatus() != ResultStatus::Success) - return nsp->GetStatus(); + if (nsp->GetStatus() != ResultStatus::Success) + return nsp->GetStatus(); - if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) - return nsp->GetProgramStatus(title_id); + if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) + return nsp->GetProgramStatus(title_id); - if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { - if (!Core::Crypto::KeyManager::KeyFileExists(false)) - return ResultStatus::ErrorMissingProductionKeyFile; - return ResultStatus::ErrorNSPMissingProgramNCA; - } + if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { + if (!Core::Crypto::KeyManager::KeyFileExists(false)) + return ResultStatus::ErrorMissingProductionKeyFile; + return ResultStatus::ErrorNSPMissingProgramNCA; } const auto result = secondary_loader->Load(process); diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 754a149fa..443affc36 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -723,7 +723,11 @@ public: StencilOp stencil_back_op_zpass; ComparisonOp stencil_back_func_func; - INSERT_PADDING_WORDS(0x17); + INSERT_PADDING_WORDS(0x4); + + u32 framebuffer_srgb; + + INSERT_PADDING_WORDS(0x12); union { BitField<2, 1, u32> coord_origin; @@ -751,7 +755,14 @@ public: }; } draw; - INSERT_PADDING_WORDS(0x6B); + INSERT_PADDING_WORDS(0xA); + + struct { + u32 enabled; + u32 index; + } primitive_restart; + + INSERT_PADDING_WORDS(0x5F); struct { u32 start_addr_high; @@ -1079,9 +1090,11 @@ ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); ASSERT_REG_POSITION(stencil_back_func_func, 0x569); +ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); ASSERT_REG_POSITION(point_coord_replace, 0x581); ASSERT_REG_POSITION(code_address, 0x582); ASSERT_REG_POSITION(draw, 0x585); +ASSERT_REG_POSITION(primitive_restart, 0x591); ASSERT_REG_POSITION(index_array, 0x5F2); ASSERT_REG_POSITION(instanced_arrays, 0x620); ASSERT_REG_POSITION(cull, 0x646); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index b472f421f..cb180b93c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -418,6 +418,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep // Bind the framebuffer surfaces state.draw.draw_framebuffer = framebuffer.handle; state.Apply(); + state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; if (using_color_fb) { if (single_color_target) { @@ -429,6 +430,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep // Assume that a surface will be written to if it is used as a framebuffer, even if // the shader doesn't actually write to it. color_surface->MarkAsModified(true, res_cache); + // Workaround for and issue in nvidia drivers + // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/ + state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion; } glFramebufferTexture2D( @@ -446,6 +450,11 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep // Assume that a surface will be written to if it is used as a framebuffer, even // if the shader doesn't actually write to it. color_surface->MarkAsModified(true, res_cache); + // Enable sRGB only for supported formats + // Workaround for and issue in nvidia drivers + // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/ + state.framebuffer_srgb.enabled |= + color_surface->GetSurfaceParams().srgb_conversion; } buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); @@ -537,7 +546,9 @@ void RasterizerOpenGL::Clear() { ConfigureFramebuffers(use_color, use_depth || use_stencil, false, regs.clear_buffers.RT.Value()); - + // Copy the sRGB setting to the clear state to avoid problem with + // specific driver implementations + clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled; clear_state.Apply(); if (use_color) { @@ -570,6 +581,7 @@ void RasterizerOpenGL::DrawArrays() { SyncBlendState(); SyncLogicOpState(); SyncCullMode(); + SyncPrimitiveRestart(); SyncDepthRange(); SyncScissorTest(); // Alpha Testing is synced on shaders. @@ -924,6 +936,13 @@ void RasterizerOpenGL::SyncCullMode() { } } +void RasterizerOpenGL::SyncPrimitiveRestart() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + + state.primitive_restart.enabled = regs.primitive_restart.enabled; + state.primitive_restart.index = regs.primitive_restart.index; +} + void RasterizerOpenGL::SyncDepthRange() { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 731a336d5..5020a5392 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -144,6 +144,9 @@ private: /// Syncs the cull mode to match the guest state void SyncCullMode(); + /// Syncs the primitve restart to match the guest state + void SyncPrimitiveRestart(); + /// Syncs the depth range to match the guest state void SyncDepthRange(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 591ec7998..b057e2efa 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -40,6 +40,10 @@ static bool IsPixelFormatASTC(PixelFormat format) { case PixelFormat::ASTC_2D_5X4: case PixelFormat::ASTC_2D_8X8: case PixelFormat::ASTC_2D_8X5: + case PixelFormat::ASTC_2D_4X4_SRGB: + case PixelFormat::ASTC_2D_5X4_SRGB: + case PixelFormat::ASTC_2D_8X8_SRGB: + case PixelFormat::ASTC_2D_8X5_SRGB: return true; default: return false; @@ -56,6 +60,14 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { return {8, 8}; case PixelFormat::ASTC_2D_8X5: return {8, 5}; + case PixelFormat::ASTC_2D_4X4_SRGB: + return {4, 4}; + case PixelFormat::ASTC_2D_5X4_SRGB: + return {5, 4}; + case PixelFormat::ASTC_2D_8X8_SRGB: + return {8, 8}; + case PixelFormat::ASTC_2D_8X5_SRGB: + return {8, 5}; default: LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); UNREACHABLE(); @@ -108,8 +120,9 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, - params.pixel_format = - PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); + params.srgb_conversion = config.tic.IsSrgbConversionEnabled(); + params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), + params.srgb_conversion); params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); params.type = GetFormatType(params.pixel_format); params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); @@ -166,6 +179,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.block_height = 1 << config.memory_layout.block_height; params.block_depth = 1 << config.memory_layout.block_depth; params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); + params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB || + config.format == Tegra::RenderTargetFormat::RGBA8_SRGB; params.component_type = ComponentTypeFromRenderTarget(config.format); params.type = GetFormatType(params.pixel_format); params.width = config.width; @@ -201,6 +216,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.pixel_format = PixelFormatFromDepthFormat(format); params.component_type = ComponentTypeFromDepthFormat(format); params.type = GetFormatType(params.pixel_format); + params.srgb_conversion = false; params.width = zeta_width; params.height = zeta_height; params.unaligned_height = zeta_height; @@ -224,6 +240,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); + params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB || + config.format == Tegra::RenderTargetFormat::RGBA8_SRGB; params.component_type = ComponentTypeFromRenderTarget(config.format); params.type = GetFormatType(params.pixel_format); params.width = config.width; @@ -289,14 +307,29 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F - {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 - {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U - {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S - {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI - {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4 + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, + false}, // RGBA8_SRGB + {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U + {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S + {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI + {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4 + {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8 + // Compressed sRGB formats + {GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT1_SRGB + {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT23_SRGB + {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT45_SRGB + {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, + ComponentType::UNorm, true}, // BC7U_SRGB + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB // Depth formats {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F @@ -361,6 +394,10 @@ static bool IsFormatBCn(PixelFormat format) { case PixelFormat::BC7U: case PixelFormat::BC6H_UF16: case PixelFormat::BC6H_SF16: + case PixelFormat::DXT1_SRGB: + case PixelFormat::DXT23_SRGB: + case PixelFormat::DXT45_SRGB: + case PixelFormat::BC7U_SRGB: return true; } return false; @@ -432,7 +469,7 @@ static constexpr GLConversionArray morton_to_gl_fns = { MortonCopy<true, PixelFormat::RG16I>, MortonCopy<true, PixelFormat::RG16S>, MortonCopy<true, PixelFormat::RGB32F>, - MortonCopy<true, PixelFormat::SRGBA8>, + MortonCopy<true, PixelFormat::RGBA8_SRGB>, MortonCopy<true, PixelFormat::RG8U>, MortonCopy<true, PixelFormat::RG8S>, MortonCopy<true, PixelFormat::RG32UI>, @@ -440,6 +477,15 @@ static constexpr GLConversionArray morton_to_gl_fns = { MortonCopy<true, PixelFormat::ASTC_2D_8X8>, MortonCopy<true, PixelFormat::ASTC_2D_8X5>, MortonCopy<true, PixelFormat::ASTC_2D_5X4>, + MortonCopy<true, PixelFormat::BGRA8_SRGB>, + MortonCopy<true, PixelFormat::DXT1_SRGB>, + MortonCopy<true, PixelFormat::DXT23_SRGB>, + MortonCopy<true, PixelFormat::DXT45_SRGB>, + MortonCopy<true, PixelFormat::BC7U_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>, MortonCopy<true, PixelFormat::Z24S8>, @@ -491,7 +537,7 @@ static constexpr GLConversionArray gl_to_morton_fns = { MortonCopy<false, PixelFormat::RG16I>, MortonCopy<false, PixelFormat::RG16S>, MortonCopy<false, PixelFormat::RGB32F>, - MortonCopy<false, PixelFormat::SRGBA8>, + MortonCopy<false, PixelFormat::RGBA8_SRGB>, MortonCopy<false, PixelFormat::RG8U>, MortonCopy<false, PixelFormat::RG8S>, MortonCopy<false, PixelFormat::RG32UI>, @@ -499,6 +545,15 @@ static constexpr GLConversionArray gl_to_morton_fns = { nullptr, nullptr, nullptr, + MortonCopy<false, PixelFormat::BGRA8_SRGB>, + MortonCopy<false, PixelFormat::DXT1_SRGB>, + MortonCopy<false, PixelFormat::DXT23_SRGB>, + MortonCopy<false, PixelFormat::DXT45_SRGB>, + MortonCopy<false, PixelFormat::BC7U_SRGB>, + nullptr, + nullptr, + nullptr, + nullptr, MortonCopy<false, PixelFormat::Z32F>, MortonCopy<false, PixelFormat::Z16>, MortonCopy<false, PixelFormat::Z24S8>, @@ -546,6 +601,8 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, OpenGLState state; state.draw.read_framebuffer = read_fb_handle; state.draw.draw_framebuffer = draw_fb_handle; + // Set sRGB enabled if the destination surfaces need it + state.framebuffer_srgb.enabled = dst_params.srgb_conversion; state.Apply(); u32 buffers{}; @@ -881,7 +938,11 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma case PixelFormat::ASTC_2D_4X4: case PixelFormat::ASTC_2D_8X8: case PixelFormat::ASTC_2D_8X5: - case PixelFormat::ASTC_2D_5X4: { + case PixelFormat::ASTC_2D_5X4: + case PixelFormat::ASTC_2D_4X4_SRGB: + case PixelFormat::ASTC_2D_8X8_SRGB: + case PixelFormat::ASTC_2D_8X5_SRGB: + case PixelFormat::ASTC_2D_5X4_SRGB: { // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. u32 block_width{}; u32 block_height{}; @@ -913,7 +974,9 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm case PixelFormat::G8R8U: case PixelFormat::G8R8S: case PixelFormat::ASTC_2D_4X4: - case PixelFormat::ASTC_2D_8X8: { + case PixelFormat::ASTC_2D_8X8: + case PixelFormat::ASTC_2D_4X4_SRGB: + case PixelFormat::ASTC_2D_8X8_SRGB: { LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", static_cast<u32>(pixel_format)); UNREACHABLE(); @@ -960,8 +1023,8 @@ void CachedSurface::FlushGLBuffer() { glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); ASSERT(!tuple.compressed); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(), - gl_buffer.data()); + glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, + static_cast<GLsizei>(gl_buffer.size()), gl_buffer.data()); glPixelStorei(GL_PACK_ROW_LENGTH, 0); ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 50a7ab47d..b4701a616 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -69,7 +69,7 @@ struct SurfaceParams { RG16I = 37, RG16S = 38, RGB32F = 39, - SRGBA8 = 40, + RGBA8_SRGB = 40, RG8U = 41, RG8S = 42, RG32UI = 43, @@ -77,19 +77,28 @@ struct SurfaceParams { ASTC_2D_8X8 = 45, ASTC_2D_8X5 = 46, ASTC_2D_5X4 = 47, + BGRA8_SRGB = 48, + DXT1_SRGB = 49, + DXT23_SRGB = 50, + DXT45_SRGB = 51, + BC7U_SRGB = 52, + ASTC_2D_4X4_SRGB = 53, + ASTC_2D_8X8_SRGB = 54, + ASTC_2D_8X5_SRGB = 55, + ASTC_2D_5X4_SRGB = 56, MaxColorFormat, // Depth formats - Z32F = 48, - Z16 = 49, + Z32F = 57, + Z16 = 58, MaxDepthFormat, // DepthStencil formats - Z24S8 = 50, - S8Z24 = 51, - Z32FS8 = 52, + Z24S8 = 59, + S8Z24 = 60, + Z32FS8 = 61, MaxDepthStencilFormat, @@ -236,7 +245,7 @@ struct SurfaceParams { 1, // RG16I 1, // RG16S 1, // RGB32F - 1, // SRGBA8 + 1, // RGBA8_SRGB 1, // RG8U 1, // RG8S 1, // RG32UI @@ -244,6 +253,15 @@ struct SurfaceParams { 4, // ASTC_2D_8X8 4, // ASTC_2D_8X5 4, // ASTC_2D_5X4 + 1, // BGRA8_SRGB + 4, // DXT1_SRGB + 4, // DXT23_SRGB + 4, // DXT45_SRGB + 4, // BC7U_SRGB + 4, // ASTC_2D_4X4_SRGB + 4, // ASTC_2D_8X8_SRGB + 4, // ASTC_2D_8X5_SRGB + 4, // ASTC_2D_5X4_SRGB 1, // Z32F 1, // Z16 1, // Z24S8 @@ -255,6 +273,77 @@ struct SurfaceParams { return compression_factor_table[static_cast<std::size_t>(format)]; } + static constexpr u32 GetDefaultBlockHeight(PixelFormat format) { + if (format == PixelFormat::Invalid) + return 0; + constexpr std::array<u32, MaxPixelFormat> block_height_table = {{ + 1, // ABGR8U + 1, // ABGR8S + 1, // ABGR8UI + 1, // B5G6R5U + 1, // A2B10G10R10U + 1, // A1B5G5R5U + 1, // R8U + 1, // R8UI + 1, // RGBA16F + 1, // RGBA16U + 1, // RGBA16UI + 1, // R11FG11FB10F + 1, // RGBA32UI + 4, // DXT1 + 4, // DXT23 + 4, // DXT45 + 4, // DXN1 + 4, // DXN2UNORM + 4, // DXN2SNORM + 4, // BC7U + 4, // BC6H_UF16 + 4, // BC6H_SF16 + 4, // ASTC_2D_4X4 + 1, // G8R8U + 1, // G8R8S + 1, // BGRA8 + 1, // RGBA32F + 1, // RG32F + 1, // R32F + 1, // R16F + 1, // R16U + 1, // R16S + 1, // R16UI + 1, // R16I + 1, // RG16 + 1, // RG16F + 1, // RG16UI + 1, // RG16I + 1, // RG16S + 1, // RGB32F + 1, // RGBA8_SRGB + 1, // RG8U + 1, // RG8S + 1, // RG32UI + 1, // R32UI + 8, // ASTC_2D_8X8 + 5, // ASTC_2D_8X5 + 4, // ASTC_2D_5X4 + 1, // BGRA8_SRGB + 4, // DXT1_SRGB + 4, // DXT23_SRGB + 4, // DXT45_SRGB + 4, // BC7U_SRGB + 4, // ASTC_2D_4X4_SRGB + 8, // ASTC_2D_8X8_SRGB + 5, // ASTC_2D_8X5_SRGB + 4, // ASTC_2D_5X4_SRGB + 1, // Z32F + 1, // Z16 + 1, // Z24S8 + 1, // S8Z24 + 1, // Z32FS8 + }}; + ASSERT(static_cast<std::size_t>(format) < block_height_table.size()); + return block_height_table[static_cast<std::size_t>(format)]; + } + static constexpr u32 GetFormatBpp(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; @@ -300,7 +389,7 @@ struct SurfaceParams { 32, // RG16I 32, // RG16S 96, // RGB32F - 32, // SRGBA8 + 32, // RGBA8_SRGB 16, // RG8U 16, // RG8S 64, // RG32UI @@ -308,6 +397,15 @@ struct SurfaceParams { 16, // ASTC_2D_8X8 32, // ASTC_2D_8X5 32, // ASTC_2D_5X4 + 32, // BGRA8_SRGB + 64, // DXT1_SRGB + 128, // DXT23_SRGB + 128, // DXT45_SRGB + 128, // BC7U + 32, // ASTC_2D_4X4_SRGB + 16, // ASTC_2D_8X8_SRGB + 32, // ASTC_2D_8X5_SRGB + 32, // ASTC_2D_5X4_SRGB 32, // Z32F 16, // Z16 32, // Z24S8 @@ -346,6 +444,7 @@ struct SurfaceParams { // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the // gamma. case Tegra::RenderTargetFormat::RGBA8_SRGB: + return PixelFormat::RGBA8_SRGB; case Tegra::RenderTargetFormat::RGBA8_UNORM: return PixelFormat::ABGR8U; case Tegra::RenderTargetFormat::RGBA8_SNORM: @@ -353,6 +452,7 @@ struct SurfaceParams { case Tegra::RenderTargetFormat::RGBA8_UINT: return PixelFormat::ABGR8UI; case Tegra::RenderTargetFormat::BGRA8_SRGB: + return PixelFormat::BGRA8_SRGB; case Tegra::RenderTargetFormat::BGRA8_UNORM: return PixelFormat::BGRA8; case Tegra::RenderTargetFormat::RGB10_A2_UNORM: @@ -416,10 +516,14 @@ struct SurfaceParams { } static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, - Tegra::Texture::ComponentType component_type) { + Tegra::Texture::ComponentType component_type, + bool is_srgb) { // TODO(Subv): Properly implement this switch (format) { case Tegra::Texture::TextureFormat::A8R8G8B8: + if (is_srgb) { + return PixelFormat::RGBA8_SRGB; + } switch (component_type) { case Tegra::Texture::ComponentType::UNORM: return PixelFormat::ABGR8U; @@ -554,11 +658,11 @@ struct SurfaceParams { case Tegra::Texture::TextureFormat::Z24S8: return PixelFormat::Z24S8; case Tegra::Texture::TextureFormat::DXT1: - return PixelFormat::DXT1; + return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; case Tegra::Texture::TextureFormat::DXT23: - return PixelFormat::DXT23; + return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23; case Tegra::Texture::TextureFormat::DXT45: - return PixelFormat::DXT45; + return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45; case Tegra::Texture::TextureFormat::DXN1: return PixelFormat::DXN1; case Tegra::Texture::TextureFormat::DXN2: @@ -572,19 +676,19 @@ struct SurfaceParams { static_cast<u32>(component_type)); UNREACHABLE(); case Tegra::Texture::TextureFormat::BC7U: - return PixelFormat::BC7U; + return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U; case Tegra::Texture::TextureFormat::BC6H_UF16: return PixelFormat::BC6H_UF16; case Tegra::Texture::TextureFormat::BC6H_SF16: return PixelFormat::BC6H_SF16; case Tegra::Texture::TextureFormat::ASTC_2D_4X4: - return PixelFormat::ASTC_2D_4X4; + return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4; case Tegra::Texture::TextureFormat::ASTC_2D_5X4: - return PixelFormat::ASTC_2D_5X4; + return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4; case Tegra::Texture::TextureFormat::ASTC_2D_8X8: - return PixelFormat::ASTC_2D_8X8; + return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; case Tegra::Texture::TextureFormat::ASTC_2D_8X5: - return PixelFormat::ASTC_2D_8X5; + return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5; case Tegra::Texture::TextureFormat::R16_G16: switch (component_type) { case Tegra::Texture::ComponentType::FLOAT: @@ -819,7 +923,7 @@ struct SurfaceParams { SurfaceTarget target; u32 max_mip_level; bool is_layered; - + bool srgb_conversion; // Parameters used for caching VAddr addr; Tegra::GPUVAddr gpu_addr; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index ba6c6919a..d8a43cc94 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -11,9 +11,10 @@ namespace OpenGL { OpenGLState OpenGLState::cur_state; - +bool OpenGLState::s_rgb_used; OpenGLState::OpenGLState() { // These all match default OpenGL values + framebuffer_srgb.enabled = false; cull.enabled = false; cull.mode = GL_BACK; cull.front_face = GL_CCW; @@ -24,6 +25,9 @@ OpenGLState::OpenGLState() { depth.depth_range_near = 0.0f; depth.depth_range_far = 1.0f; + primitive_restart.enabled = false; + primitive_restart.index = 0; + color_mask.red_enabled = GL_TRUE; color_mask.green_enabled = GL_TRUE; color_mask.blue_enabled = GL_TRUE; @@ -86,6 +90,16 @@ OpenGLState::OpenGLState() { } void OpenGLState::Apply() const { + // sRGB + if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { + if (framebuffer_srgb.enabled) { + // Track if sRGB is used + s_rgb_used = true; + glEnable(GL_FRAMEBUFFER_SRGB); + } else { + glDisable(GL_FRAMEBUFFER_SRGB); + } + } // Culling if (cull.enabled != cur_state.cull.enabled) { if (cull.enabled) { @@ -127,6 +141,18 @@ void OpenGLState::Apply() const { glDepthRange(depth.depth_range_near, depth.depth_range_far); } + // Primitive restart + if (primitive_restart.enabled != cur_state.primitive_restart.enabled) { + if (primitive_restart.enabled) { + glEnable(GL_PRIMITIVE_RESTART); + } else { + glDisable(GL_PRIMITIVE_RESTART); + } + } + if (primitive_restart.index != cur_state.primitive_restart.index) { + glPrimitiveRestartIndex(primitive_restart.index); + } + // Color mask if (color_mask.red_enabled != cur_state.color_mask.red_enabled || color_mask.green_enabled != cur_state.color_mask.green_enabled || diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index daf7eb533..9e2c573b5 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -36,6 +36,10 @@ constexpr TextureUnit ProcTexDiffLUT{9}; class OpenGLState { public: struct { + bool enabled; // GL_FRAMEBUFFER_SRGB + } framebuffer_srgb; + + struct { bool enabled; // GL_CULL_FACE GLenum mode; // GL_CULL_FACE_MODE GLenum front_face; // GL_FRONT_FACE @@ -50,6 +54,11 @@ public: } depth; struct { + bool enabled; + GLuint index; + } primitive_restart; // GL_PRIMITIVE_RESTART + + struct { GLboolean red_enabled; GLboolean green_enabled; GLboolean blue_enabled; @@ -156,7 +165,12 @@ public: static OpenGLState GetCurState() { return cur_state; } - + static bool GetsRGBUsed() { + return s_rgb_used; + } + static void ClearsRGBUsed() { + s_rgb_used = false; + } /// Apply this state as the current OpenGL state void Apply() const; @@ -171,6 +185,9 @@ public: private: static OpenGLState cur_state; + // Workaround for sRGB problems caused by + // QT not supporting srgb output + static bool s_rgb_used; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 96d916b07..90b68943d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -283,7 +283,8 @@ void RendererOpenGL::CreateRasterizer() { if (rasterizer) { return; } - + // Initialize sRGB Usage + OpenGLState::ClearsRGBUsed(); rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info); } @@ -356,13 +357,20 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, state.texture_units[0].texture = screen_info.display_texture; state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; + // Workaround brigthness problems in SMO by enabling sRGB in the final output + // if it has been used in the frame + // Needed because of this bug in QT + // QTBUG-50987 + state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed(); state.Apply(); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - + // restore default state + state.framebuffer_srgb.enabled = false; state.texture_units[0].texture = 0; state.Apply(); + // Clear sRGB state for the next frame + OpenGLState::ClearsRGBUsed(); } /** diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 5947bd2b9..d12d2ecb8 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -173,6 +173,7 @@ struct TICEntry { }; union { BitField<0, 16, u32> width_minus_1; + BitField<22, 1, u32> srgb_conversion; BitField<23, 4, TextureType> texture_type; }; union { @@ -227,6 +228,10 @@ struct TICEntry { return header_version == TICHeaderVersion::BlockLinear || header_version == TICHeaderVersion::BlockLinearColorKey; } + + bool IsSrgbConversionEnabled() const { + return srgb_conversion != 0; + } }; static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size"); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 20ffb0a9a..4b34c1e28 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -48,20 +48,40 @@ constexpr std::array<u8, 107> backup_jpeg{ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, }; -std::string GetImagePath(Service::Account::UUID uuid) { - return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + - "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; +QString GetImagePath(Service::Account::UUID uuid) { + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; + return QString::fromStdString(path); } -std::string GetAccountUsername(const Service::Account::ProfileManager& manager, - Service::Account::UUID uuid) { +QString GetAccountUsername(const Service::Account::ProfileManager& manager, + Service::Account::UUID uuid) { Service::Account::ProfileBase profile; if (!manager.GetProfileBase(uuid, profile)) { - return ""; + return {}; } - return Common::StringFromFixedZeroTerminatedBuffer( + const auto text = Common::StringFromFixedZeroTerminatedBuffer( reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); + return QString::fromStdString(text); +} + +QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { + return ConfigureSystem::tr("%1\n%2", + "%1 is the profile username, %2 is the formatted UUID (e.g. " + "00112233-4455-6677-8899-AABBCCDDEEFF))") + .arg(username, QString::fromStdString(uuid.FormatSwitch())); +} + +QPixmap GetIcon(Service::Account::UUID uuid) { + QPixmap icon{GetImagePath(uuid)}; + + if (!icon) { + icon.fill(Qt::black); + icon.loadFromData(backup_jpeg.data(), backup_jpeg.size()); + } + + return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } } // Anonymous namespace @@ -131,18 +151,6 @@ void ConfigureSystem::setConfiguration() { UpdateCurrentUser(); } -static QPixmap GetIcon(Service::Account::UUID uuid) { - const auto icon_url = QString::fromStdString(GetImagePath(uuid)); - QPixmap icon{icon_url}; - - if (!icon) { - icon.fill(Qt::black); - icon.loadFromData(backup_jpeg.data(), backup_jpeg.size()); - } - - return icon; -} - void ConfigureSystem::PopulateUserList() { const auto& profiles = profile_manager->GetAllUsers(); for (const auto& user : profiles) { @@ -154,8 +162,7 @@ void ConfigureSystem::PopulateUserList() { reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); list_items.push_back(QList<QStandardItem*>{new QStandardItem{ - GetIcon(user).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - QString::fromStdString(username + '\n' + user.FormatSwitch())}}); + GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}}); } for (const auto& item : list_items) @@ -172,7 +179,7 @@ void ConfigureSystem::UpdateCurrentUser() { scene->clear(); scene->addPixmap( GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - ui->current_user_username->setText(QString::fromStdString(username)); + ui->current_user_username->setText(username); } void ConfigureSystem::ReadSystemSettings() {} @@ -248,25 +255,23 @@ void ConfigureSystem::AddUser() { profile_manager->CreateNewUser(uuid, username.toStdString()); - item_model->appendRow(new QStandardItem{ - GetIcon(uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - QString::fromStdString(username.toStdString() + '\n' + uuid.FormatSwitch())}); + item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); } void ConfigureSystem::RenameUser() { const auto user = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(user); ASSERT(uuid != std::nullopt); - const auto username = GetAccountUsername(*profile_manager, *uuid); Service::Account::ProfileBase profile; if (!profile_manager->GetProfileBase(*uuid, profile)) return; bool ok = false; + const auto old_username = GetAccountUsername(*profile_manager, *uuid); const auto new_username = QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"), - QLineEdit::Normal, QString::fromStdString(username), &ok); + QLineEdit::Normal, old_username, &ok); if (!ok) return; @@ -284,12 +289,8 @@ void ConfigureSystem::RenameUser() { item_model->setItem( user, 0, - new QStandardItem{ - GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " - "00112233-4455-6677-8899-AABBCCDDEEFF))") - .arg(QString::fromStdString(username_std), - QString::fromStdString(uuid->FormatSwitch()))}); + new QStandardItem{GetIcon(*uuid), + FormatUserEntryText(QString::fromStdString(username_std), *uuid)}); UpdateCurrentUser(); } @@ -299,10 +300,9 @@ void ConfigureSystem::DeleteUser() { ASSERT(uuid != std::nullopt); const auto username = GetAccountUsername(*profile_manager, *uuid); - const auto confirm = - QMessageBox::question(this, tr("Confirm Delete"), - tr("You are about to delete user with name %1. Are you sure?") - .arg(QString::fromStdString(username))); + const auto confirm = QMessageBox::question( + this, tr("Confirm Delete"), + tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); if (confirm == QMessageBox::No) return; @@ -325,28 +325,47 @@ void ConfigureSystem::SetUserImage() { const auto index = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(index); ASSERT(uuid != std::nullopt); - const auto username = GetAccountUsername(*profile_manager, *uuid); const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), tr("JPEG Images (*.jpg *.jpeg)")); - if (file.isEmpty()) + if (file.isEmpty()) { return; + } + + const auto image_path = GetImagePath(*uuid); + if (QFile::exists(image_path) && !QFile::remove(image_path)) { + QMessageBox::warning( + this, tr("Error deleting image"), + tr("Error occurred attempting to overwrite previous image at: %1.").arg(image_path)); + return; + } - FileUtil::Delete(GetImagePath(*uuid)); + const auto raw_path = QString::fromStdString( + FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"); + const QFileInfo raw_info{raw_path}; + if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { + QMessageBox::warning(this, tr("Error deleting file"), + tr("Unable to delete existing file: %1.").arg(raw_path)); + return; + } - const auto raw_path = - FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; - if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) - FileUtil::Delete(raw_path); + const QString absolute_dst_path = QFileInfo{image_path}.absolutePath(); + if (!QDir{raw_path}.mkpath(absolute_dst_path)) { + QMessageBox::warning( + this, tr("Error creating user image directory"), + tr("Unable to create directory %1 for storing user images.").arg(absolute_dst_path)); + return; + } - FileUtil::CreateFullPath(GetImagePath(*uuid)); - FileUtil::Copy(file.toStdString(), GetImagePath(*uuid)); + if (!QFile::copy(file, image_path)) { + QMessageBox::warning(this, tr("Error copying user image"), + tr("Unable to copy image from %1 to %2").arg(file, image_path)); + return; + } - item_model->setItem( - index, 0, - new QStandardItem{ - GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - QString::fromStdString(username + '\n' + uuid->FormatSwitch())}); + const auto username = GetAccountUsername(*profile_manager, *uuid); + item_model->setItem(index, 0, + new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); UpdateCurrentUser(); } |