2019-08-06 17:24:07 +01:00
|
|
|
// Copyright 2019 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2019-08-17 03:34:22 +01:00
|
|
|
#include <fmt/format.h>
|
|
|
|
#include "common/file_util.h"
|
|
|
|
#include "common/texture.h"
|
|
|
|
#include "core.h"
|
2019-08-14 06:04:50 +01:00
|
|
|
#include "core/custom_tex_cache.h"
|
2019-08-06 13:43:24 +01:00
|
|
|
|
|
|
|
namespace Core {
|
2019-08-07 03:56:56 +01:00
|
|
|
CustomTexCache::CustomTexCache() = default;
|
2019-08-06 17:24:07 +01:00
|
|
|
|
2019-08-07 03:56:56 +01:00
|
|
|
CustomTexCache::~CustomTexCache() = default;
|
2019-08-06 17:24:07 +01:00
|
|
|
|
|
|
|
bool CustomTexCache::IsTextureDumped(u64 hash) const {
|
2019-08-14 06:04:50 +01:00
|
|
|
return dumped_textures.count(hash);
|
2019-08-06 13:43:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CustomTexCache::SetTextureDumped(const u64 hash) {
|
2019-08-14 06:04:50 +01:00
|
|
|
dumped_textures.insert(hash);
|
2019-08-06 13:43:24 +01:00
|
|
|
}
|
|
|
|
|
2019-08-06 17:24:07 +01:00
|
|
|
bool CustomTexCache::IsTextureCached(u64 hash) const {
|
2019-08-14 06:04:50 +01:00
|
|
|
return custom_textures.count(hash);
|
2019-08-06 13:43:24 +01:00
|
|
|
}
|
|
|
|
|
2019-08-14 06:04:50 +01:00
|
|
|
const CustomTexInfo& CustomTexCache::LookupTexture(u64 hash) const {
|
2019-08-06 13:43:24 +01:00
|
|
|
return custom_textures.at(hash);
|
|
|
|
}
|
|
|
|
|
2019-08-07 19:26:25 +01:00
|
|
|
void CustomTexCache::CacheTexture(u64 hash, const std::vector<u8>& tex, u32 width, u32 height) {
|
2019-08-06 13:43:24 +01:00
|
|
|
custom_textures[hash] = {width, height, tex};
|
|
|
|
}
|
2019-08-17 03:34:22 +01:00
|
|
|
|
|
|
|
void CustomTexCache::AddTexturePath(u64 hash, const std::string& path) {
|
|
|
|
if (custom_textures.count(hash))
|
|
|
|
LOG_ERROR(Core, "Textures {} and {} conflict!", custom_texture_paths[hash].path, path);
|
|
|
|
else
|
|
|
|
custom_texture_paths[hash] = {path, hash};
|
|
|
|
}
|
|
|
|
|
|
|
|
void CustomTexCache::FindCustomTextures() {
|
|
|
|
// 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),
|
|
|
|
Core::System::GetInstance().Kernel().GetCurrentProcess()->codeset->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() {
|
|
|
|
for (const auto& path : custom_texture_paths) {
|
|
|
|
const auto& image_interface = Core::System::GetInstance().GetImageInterface();
|
|
|
|
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
|
|
|
|
if ((ceil(log2(tex_info.width)) == floor(log2(tex_info.width))) &&
|
|
|
|
(ceil(log2(tex_info.height)) == floor(log2(tex_info.height)))) {
|
|
|
|
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;
|
|
|
|
}
|
2019-08-07 19:26:25 +01:00
|
|
|
} // namespace Core
|