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();  }  | 
