From 5940361b812711c7014c9ace40bd7e385e37223c Mon Sep 17 00:00:00 2001
From: Khangaroo <khang06@users.noreply.github.com>
Date: Tue, 6 Aug 2019 12:24:07 -0400
Subject: [PATCH] new-line that clang-format didn't fix

address some comments
---
 .../configuration/configure_input.cpp         |  36 ++--
 src/common/detached_tasks.cpp                 |   3 +-
 src/common/texture.cpp                        |  20 +--
 src/common/texture.h                          |   4 +
 src/core/core.cpp                             |  62 +++++--
 src/core/core.h                               |   2 +
 src/core/custom_tex_cache.cpp                 |  12 +-
 src/core/custom_tex_cache.h                   |  15 +-
 src/core/hle/service/cecd/cecd.cpp            |   3 +-
 src/core/movie.h                              |   4 +-
 src/input_common/udp/client.cpp               |   6 +-
 src/tests/core/arm/arm_test_common.cpp        |   3 +-
 src/tests/core/hle/kernel/hle_ipc.cpp         |   6 +-
 src/tests/core/memory/memory.cpp              |   3 +-
 .../renderer_opengl/gl_rasterizer_cache.cpp   | 162 ++++++++++--------
 .../renderer_opengl/gl_rasterizer_cache.h     |   7 +
 16 files changed, 210 insertions(+), 138 deletions(-)

diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp
index 8cac708b8..642d5f14a 100644
--- a/src/citra_qt/configuration/configure_input.cpp
+++ b/src/citra_qt/configuration/configure_input.cpp
@@ -161,16 +161,15 @@ ConfigureInput::ConfigureInput(QWidget* parent)
             continue;
         button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
         connect(button_map[button_id], &QPushButton::clicked, [=]() {
-            HandleClick(
-                button_map[button_id],
-                [=](const Common::ParamPackage& params) {
-                    buttons_param[button_id] = params;
-                    // If the user closes the dialog, the changes are reverted in
-                    // `GMainWindow::OnConfigure()`
-                    ApplyConfiguration();
-                    Settings::SaveProfile(ui->profile->currentIndex());
-                },
-                InputCommon::Polling::DeviceType::Button);
+            HandleClick(button_map[button_id],
+                        [=](const Common::ParamPackage& params) {
+                            buttons_param[button_id] = params;
+                            // If the user closes the dialog, the changes are reverted in
+                            // `GMainWindow::OnConfigure()`
+                            ApplyConfiguration();
+                            Settings::SaveProfile(ui->profile->currentIndex());
+                        },
+                        InputCommon::Polling::DeviceType::Button);
         });
         connect(button_map[button_id], &QPushButton::customContextMenuRequested,
                 [=](const QPoint& menu_location) {
@@ -199,15 +198,14 @@ ConfigureInput::ConfigureInput(QWidget* parent)
             analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
                 Qt::CustomContextMenu);
             connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::clicked, [=]() {
-                HandleClick(
-                    analog_map_buttons[analog_id][sub_button_id],
-                    [=](const Common::ParamPackage& params) {
-                        SetAnalogButton(params, analogs_param[analog_id],
-                                        analog_sub_buttons[sub_button_id]);
-                        ApplyConfiguration();
-                        Settings::SaveProfile(ui->profile->currentIndex());
-                    },
-                    InputCommon::Polling::DeviceType::Button);
+                HandleClick(analog_map_buttons[analog_id][sub_button_id],
+                            [=](const Common::ParamPackage& params) {
+                                SetAnalogButton(params, analogs_param[analog_id],
+                                                analog_sub_buttons[sub_button_id]);
+                                ApplyConfiguration();
+                                Settings::SaveProfile(ui->profile->currentIndex());
+                            },
+                            InputCommon::Polling::DeviceType::Button);
             });
             connect(analog_map_buttons[analog_id][sub_button_id],
                     &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index f2b4939df..f268d6021 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -34,7 +34,8 @@ void DetachedTasks::AddTask(std::function<void()> task) {
         std::unique_lock lock{instance->mutex};
         --instance->count;
         std::notify_all_at_thread_exit(instance->cv, std::move(lock));
-    }).detach();
+    })
+        .detach();
 }
 
 } // namespace Common
diff --git a/src/common/texture.cpp b/src/common/texture.cpp
index 0729aeba1..2ec939288 100644
--- a/src/common/texture.cpp
+++ b/src/common/texture.cpp
@@ -1,3 +1,8 @@
+// Copyright 2019 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
 #include <vector>
 #include "common/assert.h"
 #include "common/common_types.h"
@@ -6,17 +11,12 @@ namespace Common {
 void FlipRGBA8Texture(std::vector<u8>& tex, u64 width, u64 height) {
     ASSERT(tex.size() == width * height * 4);
     const u64 line_size = width * 4;
-    u8* temp_row = new u8[line_size];
-    u32 offset_1;
-    u32 offset_2;
     for (u64 line = 0; line < height / 2; line++) {
-        offset_1 = line * line_size;
-        offset_2 = (height - line - 1) * line_size;
+        const u32 offset_1 = line * line_size;
+        const u32 offset_2 = (height - line - 1) * line_size;
         // Swap lines
-        std::memcpy(temp_row, &tex[offset_1], line_size);
-        std::memcpy(&tex[offset_1], &tex[offset_2], line_size);
-        std::memcpy(&tex[offset_2], temp_row, line_size);
+        std::swap_ranges(tex.begin() + offset_1, tex.begin() + offset_1 + line_size,
+                         tex.begin() + offset_2);
     }
-    delete[] temp_row;
 }
-} // namespace Common
\ No newline at end of file
+} // namespace Common
diff --git a/src/common/texture.h b/src/common/texture.h
index b58338123..bdb03e8b4 100644
--- a/src/common/texture.h
+++ b/src/common/texture.h
@@ -1,3 +1,7 @@
+// Copyright 2019 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
 #pragma once
 
 #include <vector>
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 64f41865b..2bdceb659 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -18,14 +18,11 @@
 #include "core/cheats/cheats.h"
 #include "core/core.h"
 #include "core/core_timing.h"
-<<<<<<< HEAD
 #include "core/dumping/backend.h"
 #ifdef ENABLE_FFMPEG_VIDEO_DUMPER
 #include "core/dumping/ffmpeg_backend.h"
 #endif
-=======
 #include "core/custom_tex_cache.h"
->>>>>>> 387a49d7... fix crashes, add custom texture cache, load textures from load directory
 #include "core/gdbstub/gdbstub.h"
 #include "core/hle/kernel/client_port.h"
 #include "core/hle/kernel/kernel.h"
@@ -102,6 +99,48 @@ System::ResultStatus System::SingleStep() {
     return RunLoop(false);
 }
 
+void System::PreloadCustomTextures() {
+    // Custom textures are currently stored as
+    // load/textures/[TitleID]/tex1_[width]x[height]_[64-bit hash]_[format].png
+    const std::string load_path =
+        fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
+                    Kernel().GetCurrentProcess()->codeset->program_id);
+
+    if (FileUtil::Exists(load_path)) {
+        FileUtil::FSTEntry texture_files;
+        FileUtil::ScanDirectoryTree(load_path, texture_files);
+        for (const auto& file : texture_files.children) {
+            if (file.isDirectory)
+                continue;
+            if (file.virtualName.substr(0, 5) != "tex1_")
+                continue;
+
+            u32 width;
+            u32 height;
+            u64 hash;
+            u32 format; // unused
+            // TODO: more modern way of doing this
+            if (std::sscanf(file.virtualName.c_str(), "tex1_%ux%u_%llX_%u.png", &width, &height,
+                            &hash, &format) == 4) {
+                u32 png_width;
+                u32 png_height;
+                std::vector<u8> decoded_png;
+
+                u32 lodepng_ret =
+                    lodepng::decode(decoded_png, png_width, png_height, file.physicalName);
+                if (lodepng_ret) {
+                    LOG_CRITICAL(Render_OpenGL, "Failed to preload custom texture: {}",
+                                 lodepng_error_text(lodepng_ret));
+                } else {
+                    LOG_INFO(Render_OpenGL, "Preloaded custom texture from {}", file.physicalName);
+                    Common::FlipRGBA8Texture(decoded_png, png_width, png_height);
+                    custom_tex_cache->CacheTexture(hash, decoded_png, png_width, png_height);
+                }
+            }
+        }
+    }
+}
+
 System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
     app_loader = Loader::GetLoader(filepath);
     if (!app_loader) {
@@ -152,18 +191,13 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
         }
     }
     cheat_engine = std::make_unique<Cheats::CheatEngine>(*this);
-<<<<<<< HEAD
     u64 title_id{0};
     if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
         LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
                   static_cast<u32>(load_result));
     }
     perf_stats = std::make_unique<PerfStats>(title_id);
-=======
     custom_tex_cache = std::make_unique<Core::CustomTexCache>();
-<<<<<<< HEAD
->>>>>>> 387a49d7... fix crashes, add custom texture cache, load textures from load directory
-=======
     if (Settings::values.preload_textures) {
         // Custom textures are currently stored as
         // load/textures/[TitleID]/tex1_[width]x[height]_[64-bit hash]_[format].png
@@ -206,7 +240,8 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
             }
         }
     }
->>>>>>> 015582b2... implement custom texture preload
+    if (Settings::values.preload_textures)
+        PreloadCustomTextures();
     status = ResultStatus::Success;
     m_emu_window = &emu_window;
     m_filepath = filepath;
@@ -242,8 +277,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
 
     timing = std::make_unique<Timing>();
 
-    kernel = std::make_unique<Kernel::KernelSystem>(
-        *memory, *timing, [this] { PrepareReschedule(); }, system_mode);
+    kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing,
+                                                    [this] { PrepareReschedule(); }, system_mode);
 
     if (Settings::values.use_cpu_jit) {
 #ifdef ARCHITECTURE_x86_64
@@ -345,21 +380,20 @@ const Cheats::CheatEngine& System::CheatEngine() const {
     return *cheat_engine;
 }
 
-<<<<<<< HEAD
 VideoDumper::Backend& System::VideoDumper() {
     return *video_dumper;
 }
 
 const VideoDumper::Backend& System::VideoDumper() const {
     return *video_dumper;
-=======
+}
+
 Core::CustomTexCache& System::CustomTexCache() {
     return *custom_tex_cache;
 }
 
 const Core::CustomTexCache& System::CustomTexCache() const {
     return *custom_tex_cache;
->>>>>>> 387a49d7... fix crashes, add custom texture cache, load textures from load directory
 }
 
 void System::RegisterMiiSelector(std::shared_ptr<Frontend::MiiSelector> mii_selector) {
diff --git a/src/core/core.h b/src/core/core.h
index 5cd9c5ea6..e3050e29a 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -223,6 +223,8 @@ public:
     /// Gets a const reference to the custom texture cache system
     const Core::CustomTexCache& CustomTexCache() const;
 
+    /// Handles loading all custom textures from disk into cache.
+    void PreloadCustomTextures();
     FrameLimiter frame_limiter;
 
     void SetStatus(ResultStatus new_status, const char* details = nullptr) {
diff --git a/src/core/custom_tex_cache.cpp b/src/core/custom_tex_cache.cpp
index ed8eabafd..ace998c76 100644
--- a/src/core/custom_tex_cache.cpp
+++ b/src/core/custom_tex_cache.cpp
@@ -1,10 +1,18 @@
+// Copyright 2019 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
 #include <stdexcept>
 #include <vector>
 #include "common/common_types.h"
 #include "custom_tex_cache.h"
 
 namespace Core {
-const bool CustomTexCache::IsTextureDumped(const u64 hash) {
+CustomTexCache::CustomTexCache() {}
+
+CustomTexCache::~CustomTexCache() {}
+
+bool CustomTexCache::IsTextureDumped(u64 hash) const {
     return dumped_textures.find(hash) != dumped_textures.end();
 }
 
@@ -12,7 +20,7 @@ void CustomTexCache::SetTextureDumped(const u64 hash) {
     dumped_textures[hash] = true;
 }
 
-const bool CustomTexCache::IsTextureCached(const u64 hash) {
+bool CustomTexCache::IsTextureCached(u64 hash) const {
     return custom_textures.find(hash) != custom_textures.end();
 }
 
diff --git a/src/core/custom_tex_cache.h b/src/core/custom_tex_cache.h
index a6c226fe6..4cae738dd 100644
--- a/src/core/custom_tex_cache.h
+++ b/src/core/custom_tex_cache.h
@@ -1,3 +1,7 @@
+// Copyright 2019 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
 #pragma once
 
 #include <unordered_map>
@@ -14,12 +18,15 @@ struct CustomTexInfo {
 // TODO: think of a better name for this class...
 class CustomTexCache {
 public:
-    const bool IsTextureDumped(const u64 hash);
-    void SetTextureDumped(const u64 hash);
+    CustomTexCache();
+    ~CustomTexCache();
 
-    const bool IsTextureCached(const u64 hash);
+    bool IsTextureDumped(u64 hash) const;
+    void SetTextureDumped(u64 hash);
+
+    bool IsTextureCached(u64 hash) const;
     const CustomTexInfo& LookupTexture(const u64 hash);
-    void CacheTexture(const u64 hash, const std::vector<u8>& tex, u32 width, u32 height);
+    void CacheTexture(u64 hash, const std::vector<u8>& tex, u32 width, u32 height);
 
 private:
     std::unordered_map<u64, bool> dumped_textures;
diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp
index cc7565d2a..281f48026 100644
--- a/src/core/hle/service/cecd/cecd.cpp
+++ b/src/core/hle/service/cecd/cecd.cpp
@@ -1340,8 +1340,7 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_
     case CecDataPathType::MboxData:
     case CecDataPathType::MboxIcon:
     case CecDataPathType::MboxTitle:
-    default: {
-    }
+    default: {}
     }
 }
 
diff --git a/src/core/movie.h b/src/core/movie.h
index b9f1ee478..f1be86946 100644
--- a/src/core/movie.h
+++ b/src/core/movie.h
@@ -41,8 +41,8 @@ public:
         return s_instance;
     }
 
-    void StartPlayback(
-        const std::string& movie_file, std::function<void()> completion_callback = [] {});
+    void StartPlayback(const std::string& movie_file,
+                       std::function<void()> completion_callback = [] {});
     void StartRecording(const std::string& movie_file);
 
     /// Prepare to override the clock before playing back movies
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index f0f76493e..887436550 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -217,7 +217,8 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
             success_callback();
         else
             failure_callback();
-    }).detach();
+    })
+        .detach();
 }
 
 CalibrationConfigurationJob::CalibrationConfigurationJob(
@@ -268,7 +269,8 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
         complete_event.Wait();
         socket.Stop();
         worker_thread.join();
-    }).detach();
+    })
+        .detach();
 }
 
 CalibrationConfigurationJob::~CalibrationConfigurationJob() {
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 88566d7f2..dbbc21c8c 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -17,8 +17,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
 
     timing = std::make_unique<Core::Timing>();
     memory = std::make_unique<Memory::MemorySystem>();
-    kernel = std::make_unique<Kernel::KernelSystem>(
-        *memory, *timing, [] {}, 0);
+    kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0);
 
     kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0)));
     page_table = &kernel->GetCurrentProcess()->vm_manager.page_table;
diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp
index 367c3b7ea..fb549f829 100644
--- a/src/tests/core/hle/kernel/hle_ipc.cpp
+++ b/src/tests/core/hle/kernel/hle_ipc.cpp
@@ -23,8 +23,7 @@ static std::shared_ptr<Object> MakeObject(Kernel::KernelSystem& kernel) {
 TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
     Core::Timing timing;
     Memory::MemorySystem memory;
-    Kernel::KernelSystem kernel(
-        memory, timing, [] {}, 0);
+    Kernel::KernelSystem kernel(memory, timing, [] {}, 0);
     auto [server, client] = kernel.CreateSessionPair();
     HLERequestContext context(kernel, std::move(server), nullptr);
 
@@ -236,8 +235,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
 TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
     Core::Timing timing;
     Memory::MemorySystem memory;
-    Kernel::KernelSystem kernel(
-        memory, timing, [] {}, 0);
+    Kernel::KernelSystem kernel(memory, timing, [] {}, 0);
     auto [server, client] = kernel.CreateSessionPair();
     HLERequestContext context(kernel, std::move(server), nullptr);
 
diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp
index b33a08adf..4a6d54bf7 100644
--- a/src/tests/core/memory/memory.cpp
+++ b/src/tests/core/memory/memory.cpp
@@ -13,8 +13,7 @@
 TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") {
     Core::Timing timing;
     Memory::MemorySystem memory;
-    Kernel::KernelSystem kernel(
-        memory, timing, [] {}, 0);
+    Kernel::KernelSystem kernel(memory, timing, [] {}, 0);
     SECTION("these regions should not be mapped on an empty process") {
         auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0));
         CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index b88ab7848..f0e8c3d27 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -856,6 +856,81 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
     }
 }
 
+bool CachedSurface::LoadCustomTextures(u64 tex_hash, Core::CustomTexInfo& tex_info,
+                                       Common::Rectangle<u32>& custom_rect) {
+    bool result = false;
+    auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache();
+    const std::string load_path =
+        fmt::format("{}textures/{:016X}/tex1_{}x{}_{:016X}_{}.png",
+                    FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
+                    Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id,
+                    width, height, tex_hash, static_cast<u32>(pixel_format));
+
+    if (!custom_tex_cache.IsTextureCached(tex_hash)) {
+        if (FileUtil::Exists(load_path)) {
+            u32 lodepng_ret =
+                lodepng::decode(tex_info.tex, tex_info.width, tex_info.height, load_path);
+            if (lodepng_ret) {
+                LOG_CRITICAL(Render_OpenGL, "Failed to load custom texture: {}",
+                             lodepng_error_text(lodepng_ret));
+            } else {
+                LOG_INFO(Render_OpenGL, "Loaded custom texture from {}", load_path);
+                Common::FlipRGBA8Texture(tex_info.tex, tex_info.width, tex_info.height);
+                custom_tex_cache.CacheTexture(tex_hash, tex_info.tex, tex_info.width,
+                                              tex_info.height);
+                result = true;
+            }
+        }
+    } else {
+        tex_info = custom_tex_cache.LookupTexture(tex_hash);
+        result = true;
+    }
+
+    if (result) {
+        custom_rect.left = (custom_rect.left / width) * tex_info.width;
+        custom_rect.top = (custom_rect.top / height) * tex_info.height;
+        custom_rect.right = (custom_rect.right / width) * tex_info.width;
+        custom_rect.bottom = (custom_rect.bottom / height) * tex_info.height;
+    }
+
+    return result;
+}
+
+bool CachedSurface::GetDumpPath(u64 tex_hash, std::string& path) {
+    auto& custom_tex_cache = Core::System::GetInstance().CustomTexCache();
+    path =
+        fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
+                    Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id);
+    if (!FileUtil::CreateFullPath(path)) {
+        LOG_ERROR(Render, "Unable to create {}", path);
+        return false;
+    }
+
+    path += fmt::format("tex1_{}x{}_{:016X}_{}.png", width, height, tex_hash,
+                        static_cast<u32>(pixel_format));
+    if (!custom_tex_cache.IsTextureDumped(tex_hash) && !FileUtil::Exists(path)) {
+        custom_tex_cache.SetTextureDumped(tex_hash);
+        return true;
+    }
+    return false;
+}
+
+void CachedSurface::DumpTexture(GLuint target_tex, const std::string& dump_path) {
+    // Dump texture to RGBA8 and encode as PNG
+    LOG_INFO(Render_OpenGL, "Dumping texture to {}", dump_path);
+    std::vector<u8> decoded_texture;
+    decoded_texture.resize(width * height * 4);
+    glBindTexture(GL_TEXTURE_2D, target_tex);
+    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &decoded_texture[0]);
+    glBindTexture(GL_TEXTURE_2D, 0);
+    Common::FlipRGBA8Texture(decoded_texture, width, height);
+    u32 png_error = lodepng::encode(dump_path, decoded_texture, width, height);
+    if (png_error) {
+        LOG_CRITICAL(Render_OpenGL, "Failed to save decoded texture! {}",
+                     lodepng_error_text(png_error));
+    }
+}
+
 MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
 void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint read_fb_handle,
                                     GLuint draw_fb_handle) {
@@ -871,9 +946,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint r
     bool dump_tex = false;
     bool use_custom_tex = false;
     std::string dump_path; // Has to be declared here for logging later
-    std::vector<u8> decoded_png;
-    u32 png_width = 0;
-    u32 png_height = 0;
+    Core::CustomTexInfo custom_tex_info;
     u64 tex_hash = 0;
     Common::Rectangle custom_rect =
         rect; // Required for rect to function properly with custom textures
@@ -881,56 +954,11 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint r
     if (Settings::values.dump_textures || Settings::values.custom_textures)
         tex_hash = Common::ComputeHash64(gl_buffer.get(), gl_buffer_size);
 
-    if (Settings::values.custom_textures) {
-        const std::string load_path = fmt::format(
-            "{}textures/{:016X}/tex1_{}x{}_{:016X}_{}.png",
-            FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
-            Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id, width,
-            height, tex_hash, static_cast<u32>(pixel_format));
+    if (Settings::values.custom_textures)
+        use_custom_tex = LoadCustomTextures(tex_hash, custom_tex_info, custom_rect);
 
-        if (!custom_tex_cache.IsTextureCached(tex_hash)) {
-            if (FileUtil::Exists(load_path)) {
-                u32 lodepng_ret = lodepng::decode(decoded_png, png_width, png_height, load_path);
-                if (lodepng_ret)
-                    LOG_CRITICAL(Render_OpenGL, "Failed to load custom texture: {}",
-                                 lodepng_error_text(lodepng_ret));
-                else {
-                    LOG_INFO(Render_OpenGL, "Loaded custom texture from {}", load_path);
-                    Common::FlipRGBA8Texture(decoded_png, png_width, png_height);
-                    custom_tex_cache.CacheTexture(tex_hash, decoded_png, png_width, png_height);
-                    use_custom_tex = true;
-                }
-            }
-        } else {
-            const auto custom_tex_info = custom_tex_cache.LookupTexture(tex_hash);
-            decoded_png = custom_tex_info.tex;
-            png_width = custom_tex_info.width;
-            png_height = custom_tex_info.height;
-            use_custom_tex = true;
-        }
-
-        if (png_width && png_height) {
-            custom_rect.left = (custom_rect.left / width) * png_width;
-            custom_rect.top = (custom_rect.top / height) * png_height;
-            custom_rect.right = (custom_rect.right / width) * png_width;
-            custom_rect.bottom = (custom_rect.bottom / height) * png_height;
-        }
-    }
-
-    if (Settings::values.dump_textures && !use_custom_tex) {
-        dump_path = fmt::format(
-            "{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
-            Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->program_id);
-        if (!FileUtil::CreateFullPath(dump_path))
-            LOG_ERROR(Render, "Unable to create {}", dump_path);
-
-        dump_path += fmt::format("tex1_{}x{}_{:016X}_{}.png", width, height, tex_hash,
-                                 static_cast<u32>(pixel_format));
-        if (!custom_tex_cache.IsTextureDumped(tex_hash) && !FileUtil::Exists(dump_path)) {
-            custom_tex_cache.SetTextureDumped(tex_hash);
-            dump_tex = true;
-        }
-    }
+    if (Settings::values.dump_textures && !use_custom_tex)
+        dump_tex = GetDumpPath(tex_hash, dump_path);
 
     // Load data from memory to the surface
     GLint x0 = static_cast<GLint>(custom_rect.left);
@@ -950,7 +978,7 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint r
         unscaled_tex.Create();
         if (use_custom_tex) {
             AllocateSurfaceTexture(unscaled_tex.handle, GetFormatTuple(PixelFormat::RGBA8),
-                                   png_width, png_height);
+                                   custom_tex_info.width, custom_tex_info.height);
         } else {
             AllocateSurfaceTexture(unscaled_tex.handle, tuple, custom_rect.GetWidth(),
                                    custom_rect.GetHeight());
@@ -975,36 +1003,22 @@ void CachedSurface::UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint r
                         &gl_buffer[buffer_offset]);
     } else {
         if (res_scale == 1) {
-            AllocateSurfaceTexture(texture.handle, GetFormatTuple(PixelFormat::RGBA8), png_width,
-                                   png_height);
+            AllocateSurfaceTexture(texture.handle, GetFormatTuple(PixelFormat::RGBA8),
+                                   custom_tex_info.width, custom_tex_info.height);
             cur_state.texture_units[0].texture_2d = texture.handle;
             cur_state.Apply();
         }
         // always going to be using rgba8
-        glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(png_width));
+        glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(custom_tex_info.width));
 
         glActiveTexture(GL_TEXTURE0);
-        glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, png_width, png_height, GL_RGBA, GL_UNSIGNED_BYTE,
-                        decoded_png.data());
+        glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, custom_tex_info.width, custom_tex_info.height,
+                        GL_RGBA, GL_UNSIGNED_BYTE, custom_tex_info.tex.data());
     }
 
     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-    if (dump_tex) {
-        // Dump texture to RGBA8 and encode as PNG
-        LOG_INFO(Render_OpenGL, "Dumping texture to {}", dump_path);
-        std::vector<u8> decoded_texture;
-        decoded_texture.resize(width * height * 4);
-        glBindTexture(GL_TEXTURE_2D, target_tex);
-        glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &decoded_texture[0]);
-        glBindTexture(GL_TEXTURE_2D, 0);
-        Common::FlipRGBA8Texture(decoded_texture, width, height);
-        u32 png_error = lodepng::encode(dump_path, decoded_texture, width, height);
-        if (png_error) {
-            LOG_CRITICAL(Render_OpenGL, "Failed to save decoded texture! {}",
-                         lodepng_error_text(png_error));
-        }
-        custom_tex_cache.SetTextureDumped(tex_hash);
-    }
+    if (dump_tex)
+        DumpTexture(target_tex, dump_path);
 
     cur_state.texture_units[0].texture_2d = old_tex;
     cur_state.Apply();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 962cbceb6..4d8f2d8bc 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -25,6 +25,7 @@
 #include "common/common_funcs.h"
 #include "common/common_types.h"
 #include "common/math_util.h"
+#include "core/custom_tex_cache.h"
 #include "core/hw/gpu.h"
 #include "video_core/regs_framebuffer.h"
 #include "video_core/regs_texturing.h"
@@ -377,6 +378,12 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface
     void LoadGLBuffer(PAddr load_start, PAddr load_end);
     void FlushGLBuffer(PAddr flush_start, PAddr flush_end);
 
+    // Custom texture loading and dumping
+    bool LoadCustomTextures(u64 tex_hash, Core::CustomTexInfo& tex_info,
+                            Common::Rectangle<u32>& custom_rect);
+    bool GetDumpPath(u64 tex_hash, std::string& path);
+    void DumpTexture(GLuint target_tex, const std::string& dump_path);
+
     // Upload/Download data in gl_buffer in/to this surface's texture
     void UploadGLTexture(const Common::Rectangle<u32>& rect, GLuint read_fb_handle,
                          GLuint draw_fb_handle);