mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Custom textures rewrite (#6452)
* common: Add thread pool from yuzu * Is really useful for asynchronous operations like shader compilation and custom textures, will be used in following PRs * core: Improve ImageInterface * Provide a default implementation so frontends don't have to duplicate code registering the lodepng version * Add a dds version too which we will use in the next commit * rasterizer_cache: Rewrite custom textures * There's just too much to talk about here, look at the PR description for more details * rasterizer_cache: Implement basic pack configuration file * custom_tex_manager: Flip dumped textures * custom_tex_manager: Optimize custom texture hashing * If no convertions are needed then we can hash the decoded data directly removing the needed for duplicate decode * custom_tex_manager: Implement asynchronous texture loading * The file loading and decoding is offloaded into worker threads, while the upload itself still occurs in the main thread to avoid having to manage shared contexts * Address review comments * custom_tex_manager: Introduce custom material support * video_core: Move custom textures to separate directory * Also split the files to make the code cleaner * gl_texture_runtime: Generate mipmaps for material * custom_tex_manager: Prevent memory overflow when preloading * externals: Add dds-ktx as submodule * string_util: Return vector from SplitString * No code benefits from passing it as an argument * custom_textures: Use json config file * gl_rasterizer: Only bind material for unit 0 * Address review comments
This commit is contained in:
		
							parent
							
								
									d16dce6d99
								
							
						
					
					
						commit
						06f3c90cfb
					
				
					 87 changed files with 2154 additions and 544 deletions
				
			
		|  | @ -36,8 +36,6 @@ add_library(core STATIC | |||
|     core.h | ||||
|     core_timing.cpp | ||||
|     core_timing.h | ||||
|     custom_tex_cache.cpp | ||||
|     custom_tex_cache.h | ||||
|     dumping/backend.cpp | ||||
|     dumping/backend.h | ||||
|     file_sys/archive_backend.cpp | ||||
|  | @ -109,6 +107,7 @@ add_library(core STATIC | |||
|     frontend/emu_window.h | ||||
|     frontend/framebuffer_layout.cpp | ||||
|     frontend/framebuffer_layout.h | ||||
|     frontend/image_interface.cpp | ||||
|     frontend/image_interface.h | ||||
|     frontend/input.h | ||||
|     frontend/mic.cpp | ||||
|  | @ -481,7 +480,8 @@ endif() | |||
| create_target_directory_groups(core) | ||||
| 
 | ||||
| target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) | ||||
| target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt::fmt open_source_archives Boost::serialization Boost::iostreams) | ||||
| target_link_libraries(core PRIVATE Boost::boost Boost::serialization Boost::iostreams) | ||||
| target_link_libraries(core PUBLIC dds-ktx PRIVATE cryptopp fmt::fmt lodepng open_source_archives) | ||||
| set_target_properties(core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO}) | ||||
| 
 | ||||
| if (ENABLE_WEB_SERVICE) | ||||
|  |  | |||
|  | @ -216,13 +216,12 @@ GatewayCheat::GatewayCheat(std::string name_, std::vector<CheatLine> cheat_lines | |||
| GatewayCheat::GatewayCheat(std::string name_, std::string code, std::string comments_) | ||||
|     : name(std::move(name_)), comments(std::move(comments_)) { | ||||
| 
 | ||||
|     std::vector<std::string> code_lines; | ||||
|     Common::SplitString(code, '\n', code_lines); | ||||
|     const auto code_lines = Common::SplitString(code, '\n'); | ||||
| 
 | ||||
|     std::vector<CheatLine> temp_cheat_lines; | ||||
|     for (std::size_t i = 0; i < code_lines.size(); ++i) { | ||||
|         if (!code_lines[i].empty()) | ||||
|             temp_cheat_lines.emplace_back(code_lines[i]); | ||||
|     for (const std::string& line : code_lines) { | ||||
|         if (!line.empty()) | ||||
|             temp_cheat_lines.emplace_back(line); | ||||
|     } | ||||
|     cheat_lines = std::move(temp_cheat_lines); | ||||
| } | ||||
|  | @ -464,10 +463,10 @@ std::string GatewayCheat::ToString() const { | |||
|         result += EnabledText; | ||||
|         result += '\n'; | ||||
|     } | ||||
|     std::vector<std::string> comment_lines; | ||||
|     Common::SplitString(comments, '\n', comment_lines); | ||||
|     for (const auto& comment_line : comment_lines) | ||||
|     const auto comment_lines = Common::SplitString(comments, '\n'); | ||||
|     for (const auto& comment_line : comment_lines) { | ||||
|         result += "*" + comment_line + '\n'; | ||||
|     } | ||||
|     result += GetCode() + '\n'; | ||||
|     return result; | ||||
| } | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ | |||
| #include "core/dumping/ffmpeg_backend.h" | ||||
| #endif | ||||
| #include "common/settings.h" | ||||
| #include "core/custom_tex_cache.h" | ||||
| #include "core/frontend/image_interface.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/global.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
|  | @ -48,6 +48,7 @@ | |||
| #include "core/movie.h" | ||||
| #include "core/rpc/rpc_server.h" | ||||
| #include "network/network.h" | ||||
| #include "video_core/custom_textures/custom_tex_manager.h" | ||||
| #include "video_core/renderer_base.h" | ||||
| #include "video_core/video_core.h" | ||||
| 
 | ||||
|  | @ -318,16 +319,15 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st | |||
|                   static_cast<u32>(load_result)); | ||||
|     } | ||||
|     perf_stats = std::make_unique<PerfStats>(title_id); | ||||
|     custom_tex_cache = std::make_unique<Core::CustomTexCache>(); | ||||
| 
 | ||||
|     if (Settings::values.custom_textures) { | ||||
|         const u64 program_id = Kernel().GetCurrentProcess()->codeset->program_id; | ||||
|         FileUtil::CreateFullPath(fmt::format( | ||||
|             "{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), program_id)); | ||||
|         custom_tex_cache->FindCustomTextures(program_id); | ||||
|         custom_tex_manager->FindCustomTextures(); | ||||
|     } | ||||
|     if (Settings::values.preload_textures) { | ||||
|         custom_tex_cache->PreloadTextures(*GetImageInterface()); | ||||
|         custom_tex_manager->PreloadTextures(); | ||||
|     } | ||||
|     if (Settings::values.dump_textures) { | ||||
|         custom_tex_manager->WriteConfig(); | ||||
|     } | ||||
| 
 | ||||
|     status = ResultStatus::Success; | ||||
|  | @ -432,6 +432,12 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, | |||
|     video_dumper = std::make_unique<VideoDumper::NullBackend>(); | ||||
| #endif | ||||
| 
 | ||||
|     if (!registered_image_interface) { | ||||
|         registered_image_interface = std::make_shared<Frontend::ImageInterface>(); | ||||
|     } | ||||
| 
 | ||||
|     custom_tex_manager = std::make_unique<VideoCore::CustomTexManager>(*this); | ||||
| 
 | ||||
|     VideoCore::Init(emu_window, secondary_window, *this); | ||||
| 
 | ||||
|     LOG_DEBUG(Core, "Initialized OK"); | ||||
|  | @ -505,12 +511,12 @@ const VideoDumper::Backend& System::VideoDumper() const { | |||
|     return *video_dumper; | ||||
| } | ||||
| 
 | ||||
| Core::CustomTexCache& System::CustomTexCache() { | ||||
|     return *custom_tex_cache; | ||||
| VideoCore::CustomTexManager& System::CustomTexManager() { | ||||
|     return *custom_tex_manager; | ||||
| } | ||||
| 
 | ||||
| const Core::CustomTexCache& System::CustomTexCache() const { | ||||
|     return *custom_tex_cache; | ||||
| const VideoCore::CustomTexManager& System::CustomTexManager() const { | ||||
|     return *custom_tex_manager; | ||||
| } | ||||
| 
 | ||||
| void System::RegisterMiiSelector(std::shared_ptr<Frontend::MiiSelector> mii_selector) { | ||||
|  |  | |||
|  | @ -10,10 +10,8 @@ | |||
| #include <string> | ||||
| #include <boost/serialization/version.hpp> | ||||
| #include "common/common_types.h" | ||||
| #include "core/custom_tex_cache.h" | ||||
| #include "core/frontend/applets/mii_selector.h" | ||||
| #include "core/frontend/applets/swkbd.h" | ||||
| #include "core/frontend/image_interface.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/perf_stats.h" | ||||
|  | @ -23,7 +21,8 @@ class ARM_Interface; | |||
| 
 | ||||
| namespace Frontend { | ||||
| class EmuWindow; | ||||
| } | ||||
| class ImageInterface; | ||||
| } // namespace Frontend
 | ||||
| 
 | ||||
| namespace Memory { | ||||
| class MemorySystem; | ||||
|  | @ -59,8 +58,9 @@ class Backend; | |||
| } | ||||
| 
 | ||||
| namespace VideoCore { | ||||
| class CustomTexManager; | ||||
| class RendererBase; | ||||
| } | ||||
| } // namespace VideoCore
 | ||||
| 
 | ||||
| namespace Core { | ||||
| 
 | ||||
|  | @ -253,10 +253,10 @@ public: | |||
|     [[nodiscard]] const Cheats::CheatEngine& CheatEngine() const; | ||||
| 
 | ||||
|     /// Gets a reference to the custom texture cache system
 | ||||
|     [[nodiscard]] Core::CustomTexCache& CustomTexCache(); | ||||
|     [[nodiscard]] VideoCore::CustomTexManager& CustomTexManager(); | ||||
| 
 | ||||
|     /// Gets a const reference to the custom texture cache system
 | ||||
|     [[nodiscard]] const Core::CustomTexCache& CustomTexCache() const; | ||||
|     [[nodiscard]] const VideoCore::CustomTexManager& CustomTexManager() const; | ||||
| 
 | ||||
|     /// Gets a reference to the video dumper backend
 | ||||
|     [[nodiscard]] VideoDumper::Backend& VideoDumper(); | ||||
|  | @ -362,7 +362,7 @@ private: | |||
|     std::unique_ptr<VideoDumper::Backend> video_dumper; | ||||
| 
 | ||||
|     /// Custom texture cache system
 | ||||
|     std::unique_ptr<Core::CustomTexCache> custom_tex_cache; | ||||
|     std::unique_ptr<VideoCore::CustomTexManager> custom_tex_manager; | ||||
| 
 | ||||
|     /// Image interface
 | ||||
|     std::shared_ptr<Frontend::ImageInterface> registered_image_interface; | ||||
|  |  | |||
|  | @ -1,109 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| #include "common/file_util.h" | ||||
| #include "common/texture.h" | ||||
| #include "core.h" | ||||
| #include "core/custom_tex_cache.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| CustomTexCache::CustomTexCache() = default; | ||||
| 
 | ||||
| CustomTexCache::~CustomTexCache() = default; | ||||
| 
 | ||||
| bool CustomTexCache::IsTextureDumped(u64 hash) const { | ||||
|     return dumped_textures.count(hash); | ||||
| } | ||||
| 
 | ||||
| void CustomTexCache::SetTextureDumped(const u64 hash) { | ||||
|     dumped_textures.insert(hash); | ||||
| } | ||||
| 
 | ||||
| bool CustomTexCache::IsTextureCached(u64 hash) const { | ||||
|     return custom_textures.count(hash); | ||||
| } | ||||
| 
 | ||||
| const CustomTexInfo& CustomTexCache::LookupTexture(u64 hash) const { | ||||
|     return custom_textures.at(hash); | ||||
| } | ||||
| 
 | ||||
| void CustomTexCache::CacheTexture(u64 hash, const std::vector<u8>& tex, u32 width, u32 height) { | ||||
|     custom_textures[hash] = {width, height, tex}; | ||||
| } | ||||
| 
 | ||||
| void CustomTexCache::AddTexturePath(u64 hash, const std::string& path) { | ||||
|     if (custom_texture_paths.count(hash)) | ||||
|         LOG_ERROR(Core, "Textures {} and {} conflict!", custom_texture_paths[hash].path, path); | ||||
|     else | ||||
|         custom_texture_paths[hash] = {path, hash}; | ||||
| } | ||||
| 
 | ||||
| void CustomTexCache::FindCustomTextures(u64 program_id) { | ||||
|     // Custom textures are currently stored as
 | ||||
|     // [TitleID]/tex1_[width]x[height]_[64-bit hash]_[format].png
 | ||||
| 
 | ||||
|     const std::string load_path = fmt::format( | ||||
|         "{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), program_id); | ||||
| 
 | ||||
|     if (FileUtil::Exists(load_path)) { | ||||
|         FileUtil::FSTEntry texture_dir; | ||||
|         std::vector<FileUtil::FSTEntry> textures; | ||||
|         // 64 nested folders should be plenty for most cases
 | ||||
|         FileUtil::ScanDirectoryTree(load_path, texture_dir, 64); | ||||
|         FileUtil::GetAllFilesFromNestedEntries(texture_dir, textures); | ||||
| 
 | ||||
|         for (const auto& file : textures) { | ||||
|             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) { | ||||
|                 AddTexturePath(hash, file.physicalName); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CustomTexCache::PreloadTextures(Frontend::ImageInterface& image_interface) { | ||||
|     for (const auto& path : custom_texture_paths) { | ||||
|         const auto& path_info = path.second; | ||||
|         Core::CustomTexInfo tex_info; | ||||
|         if (image_interface.DecodePNG(tex_info.tex, tex_info.width, tex_info.height, | ||||
|                                       path_info.path)) { | ||||
|             // Make sure the texture size is a power of 2
 | ||||
|             std::bitset<32> width_bits(tex_info.width); | ||||
|             std::bitset<32> height_bits(tex_info.height); | ||||
|             if (width_bits.count() == 1 && height_bits.count() == 1) { | ||||
|                 LOG_DEBUG(Render_OpenGL, "Loaded custom texture from {}", path_info.path); | ||||
|                 Common::FlipRGBA8Texture(tex_info.tex, tex_info.width, tex_info.height); | ||||
|                 CacheTexture(path_info.hash, tex_info.tex, tex_info.width, tex_info.height); | ||||
|             } else { | ||||
|                 LOG_ERROR(Render_OpenGL, "Texture {} size is not a power of 2", path_info.path); | ||||
|             } | ||||
|         } else { | ||||
|             LOG_ERROR(Render_OpenGL, "Failed to load custom texture {}", path_info.path); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool CustomTexCache::CustomTextureExists(u64 hash) const { | ||||
|     return custom_texture_paths.count(hash); | ||||
| } | ||||
| 
 | ||||
| const CustomTexPathInfo& CustomTexCache::LookupTexturePathInfo(u64 hash) const { | ||||
|     return custom_texture_paths.at(hash); | ||||
| } | ||||
| 
 | ||||
| bool CustomTexCache::IsTexturePathMapEmpty() const { | ||||
|     return custom_texture_paths.size() == 0; | ||||
| } | ||||
| } // namespace Core
 | ||||
|  | @ -1,55 +0,0 @@ | |||
| // Copyright 2019 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <unordered_set> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Frontend { | ||||
| class ImageInterface; | ||||
| } // namespace Frontend
 | ||||
| 
 | ||||
| namespace Core { | ||||
| struct CustomTexInfo { | ||||
|     u32 width; | ||||
|     u32 height; | ||||
|     std::vector<u8> tex; | ||||
| }; | ||||
| 
 | ||||
| // This is to avoid parsing the filename multiple times
 | ||||
| struct CustomTexPathInfo { | ||||
|     std::string path; | ||||
|     u64 hash; | ||||
| }; | ||||
| 
 | ||||
| // TODO: think of a better name for this class...
 | ||||
| class CustomTexCache { | ||||
| public: | ||||
|     explicit CustomTexCache(); | ||||
|     ~CustomTexCache(); | ||||
| 
 | ||||
|     bool IsTextureDumped(u64 hash) const; | ||||
|     void SetTextureDumped(u64 hash); | ||||
| 
 | ||||
|     bool IsTextureCached(u64 hash) const; | ||||
|     const CustomTexInfo& LookupTexture(u64 hash) const; | ||||
|     void CacheTexture(u64 hash, const std::vector<u8>& tex, u32 width, u32 height); | ||||
| 
 | ||||
|     void AddTexturePath(u64 hash, const std::string& path); | ||||
|     void FindCustomTextures(u64 program_id); | ||||
|     void PreloadTextures(Frontend::ImageInterface& image_interface); | ||||
|     bool CustomTextureExists(u64 hash) const; | ||||
|     const CustomTexPathInfo& LookupTexturePathInfo(u64 hash) const; | ||||
|     bool IsTexturePathMapEmpty() const; | ||||
| 
 | ||||
| private: | ||||
|     std::unordered_set<u64> dumped_textures; | ||||
|     std::unordered_map<u64, CustomTexInfo> custom_textures; | ||||
|     std::unordered_map<u64, CustomTexPathInfo> custom_texture_paths; | ||||
| }; | ||||
| } // namespace Core
 | ||||
|  | @ -813,8 +813,7 @@ std::vector<FormatInfo> ListFormats() { | |||
|     void* data = nullptr; // For libavformat to save the iteration state
 | ||||
|     while ((current = av_muxer_iterate(&data))) { | ||||
| #endif | ||||
|         std::vector<std::string> extensions; | ||||
|         Common::SplitString(ToStdString(current->extensions), ',', extensions); | ||||
|         const auto extensions = Common::SplitString(ToStdString(current->extensions), ','); | ||||
| 
 | ||||
|         std::set<AVCodecID> supported_video_codecs; | ||||
|         std::set<AVCodecID> supported_audio_codecs; | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ PathParser::PathParser(const Path& path) { | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     Common::SplitString(path_string, '/', path_sequence); | ||||
|     path_sequence = Common::SplitString(path_string, '/'); | ||||
| 
 | ||||
|     auto begin = path_sequence.begin(); | ||||
|     auto end = path_sequence.end(); | ||||
|  |  | |||
							
								
								
									
										65
									
								
								src/core/frontend/image_interface.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/core/frontend/image_interface.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| // Copyright 2023 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #define DDSKTX_IMPLEMENT | ||||
| #include <dds-ktx.h> | ||||
| #include <lodepng.h> | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/frontend/image_interface.h" | ||||
| 
 | ||||
| namespace Frontend { | ||||
| 
 | ||||
| bool ImageInterface::DecodePNG(std::vector<u8>& dst, u32& width, u32& height, | ||||
|                                std::span<const u8> src) { | ||||
|     const u32 lodepng_ret = lodepng::decode(dst, width, height, src.data(), src.size()); | ||||
|     if (lodepng_ret) { | ||||
|         LOG_ERROR(Frontend, "Failed to decode because {}", lodepng_error_text(lodepng_ret)); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool ImageInterface::DecodeDDS(std::vector<u8>& dst, u32& width, u32& height, ddsktx_format& format, | ||||
|                                std::span<const u8> src) { | ||||
|     ddsktx_texture_info tc{}; | ||||
|     const int size = static_cast<int>(src.size()); | ||||
| 
 | ||||
|     if (!ddsktx_parse(&tc, src.data(), size, nullptr)) { | ||||
|         LOG_ERROR(Frontend, "Failed to decode"); | ||||
|         return false; | ||||
|     } | ||||
|     width = tc.width; | ||||
|     height = tc.height; | ||||
|     format = tc.format; | ||||
| 
 | ||||
|     ddsktx_sub_data sub_data{}; | ||||
|     ddsktx_get_sub(&tc, &sub_data, src.data(), size, 0, 0, 0); | ||||
| 
 | ||||
|     dst.resize(sub_data.size_bytes); | ||||
|     std::memcpy(dst.data(), sub_data.buff, sub_data.size_bytes); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool ImageInterface::EncodePNG(const std::string& path, u32 width, u32 height, | ||||
|                                std::span<const u8> src) { | ||||
|     std::vector<u8> out; | ||||
|     const u32 lodepng_ret = lodepng::encode(out, src.data(), width, height); | ||||
|     if (lodepng_ret) { | ||||
|         LOG_ERROR(Frontend, "Failed to encode {} because {}", path, | ||||
|                   lodepng_error_text(lodepng_ret)); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     FileUtil::IOFile file{path, "wb"}; | ||||
|     if (file.WriteBytes(out.data(), out.size()) != out.size()) { | ||||
|         LOG_ERROR(Frontend, "Failed to save encode to path {}", path); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace Frontend
 | ||||
|  | @ -4,21 +4,26 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <span> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <dds-ktx.h> | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Frontend { | ||||
| 
 | ||||
| /**
 | ||||
|  * Utility class that provides image decoding/encoding to the custom texture manager. | ||||
|  * Can be optionally overriden by frontends to provide a custom implementation. | ||||
|  */ | ||||
| class ImageInterface { | ||||
| public: | ||||
|     virtual ~ImageInterface() = default; | ||||
| 
 | ||||
|     // Error logging should be handled by the frontend
 | ||||
|     virtual bool DecodePNG(std::vector<u8>& dst, u32& width, u32& height, | ||||
|                            const std::string& path) = 0; | ||||
|     virtual bool EncodePNG(const std::string& path, const std::vector<u8>& src, u32 width, | ||||
|                            u32 height) = 0; | ||||
|     virtual bool DecodePNG(std::vector<u8>& dst, u32& width, u32& height, std::span<const u8> src); | ||||
|     virtual bool DecodeDDS(std::vector<u8>& dst, u32& width, u32& height, ddsktx_format& format, | ||||
|                            std::span<const u8> src); | ||||
|     virtual bool EncodePNG(const std::string& path, u32 width, u32 height, std::span<const u8> src); | ||||
| }; | ||||
| 
 | ||||
| } // namespace Frontend
 | ||||
|  |  | |||
|  | @ -136,8 +136,7 @@ void HandleHioReply(const u8* const command_buffer, const u32 command_length) { | |||
|     } | ||||
| 
 | ||||
|     const std::string command_str{command_pos, command_buffer + command_length}; | ||||
|     std::vector<std::string> command_parts; | ||||
|     Common::SplitString(command_str, ',', command_parts); | ||||
|     const auto command_parts = Common::SplitString(command_str, ','); | ||||
| 
 | ||||
|     if (command_parts.empty() || command_parts.size() > 3) { | ||||
|         LOG_WARNING(Debug_GDBStub, "Unexpected reply packet size: {}", command_parts); | ||||
|  |  | |||
|  | @ -458,8 +458,7 @@ void LoadPresetKeys() { | |||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         std::vector<std::string> parts; | ||||
|         Common::SplitString(line, '=', parts); | ||||
|         const auto parts = Common::SplitString(line, '='); | ||||
|         if (parts.size() != 2) { | ||||
|             LOG_ERROR(HW_AES, "Failed to parse {}", line); | ||||
|             continue; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue