mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
VideoCommon: add logic to read a GraphicsMod from configuration
This commit is contained in:
parent
f92beb611f
commit
254246b814
@ -631,6 +631,11 @@
|
||||
<ClInclude Include="VideoCommon\FreeLookCamera.h" />
|
||||
<ClInclude Include="VideoCommon\GeometryShaderGen.h" />
|
||||
<ClInclude Include="VideoCommon\GeometryShaderManager.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsMod.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModFeature.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.h" />
|
||||
<ClInclude Include="VideoCommon\GraphicsModSystem\Constants.h" />
|
||||
<ClInclude Include="VideoCommon\GXPipelineTypes.h" />
|
||||
<ClInclude Include="VideoCommon\HiresTextures.h" />
|
||||
@ -1210,6 +1215,11 @@
|
||||
<ClCompile Include="VideoCommon\FreeLookCamera.cpp" />
|
||||
<ClCompile Include="VideoCommon\GeometryShaderGen.cpp" />
|
||||
<ClCompile Include="VideoCommon\GeometryShaderManager.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsMod.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModFeature.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.cpp" />
|
||||
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.cpp" />
|
||||
<ClCompile Include="VideoCommon\HiresTextures_DDSLoader.cpp" />
|
||||
<ClCompile Include="VideoCommon\HiresTextures.cpp" />
|
||||
<ClCompile Include="VideoCommon\IndexGenerator.cpp" />
|
||||
|
@ -39,6 +39,16 @@ add_library(videocommon
|
||||
GeometryShaderGen.h
|
||||
GeometryShaderManager.cpp
|
||||
GeometryShaderManager.h
|
||||
GraphicsModSystem/Config/GraphicsMod.cpp
|
||||
GraphicsModSystem/Config/GraphicsMod.h
|
||||
GraphicsModSystem/Config/GraphicsModFeature.cpp
|
||||
GraphicsModSystem/Config/GraphicsModFeature.h
|
||||
GraphicsModSystem/Config/GraphicsModGroup.cpp
|
||||
GraphicsModSystem/Config/GraphicsModGroup.h
|
||||
GraphicsModSystem/Config/GraphicsTarget.cpp
|
||||
GraphicsModSystem/Config/GraphicsTarget.h
|
||||
GraphicsModSystem/Config/GraphicsTargetGroup.cpp
|
||||
GraphicsModSystem/Config/GraphicsTargetGroup.h
|
||||
GraphicsModSystem/Constants.h
|
||||
HiresTextures.cpp
|
||||
HiresTextures.h
|
||||
|
291
Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsMod.cpp
Normal file
291
Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsMod.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Constants.h"
|
||||
|
||||
std::optional<GraphicsModConfig> GraphicsModConfig::Create(const std::string& file_path,
|
||||
Source source)
|
||||
{
|
||||
std::string json_data;
|
||||
if (!File::ReadFileToString(file_path, json_data))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod json file '{}'", file_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
picojson::value root;
|
||||
const auto error = picojson::parse(root, json_data);
|
||||
|
||||
if (!error.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod json file '{}' due to parse error: {}",
|
||||
file_path, error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
GraphicsModConfig result;
|
||||
if (!result.DeserializeFromConfig(root))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
result.m_source = source;
|
||||
if (source == Source::User)
|
||||
{
|
||||
const std::string base_path = File::GetUserPath(D_GRAPHICSMOD_IDX);
|
||||
if (base_path.size() > file_path.size())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load graphics mod json file '{}' due to it not matching the base path: {}",
|
||||
file_path, base_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
result.m_relative_path = file_path.substr(base_path.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string base_path = File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR;
|
||||
if (base_path.size() > file_path.size())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load graphics mod json file '{}' due to it not matching the base path: {}",
|
||||
file_path, base_path);
|
||||
return std::nullopt;
|
||||
}
|
||||
result.m_relative_path = file_path.substr(base_path.size());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<GraphicsModConfig> GraphicsModConfig::Create(const picojson::object* obj)
|
||||
{
|
||||
if (!obj)
|
||||
return std::nullopt;
|
||||
|
||||
const auto source_it = obj->find("source");
|
||||
if (source_it == obj->end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string source_str = source_it->second.to_str();
|
||||
|
||||
const auto path_it = obj->find("path");
|
||||
if (path_it == obj->end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string relative_path = path_it->second.to_str();
|
||||
|
||||
if (source_str == "system")
|
||||
{
|
||||
return Create(fmt::format("{}{}{}", File::GetSysDirectory(), DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR,
|
||||
relative_path),
|
||||
Source::System);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Create(File::GetUserPath(D_GRAPHICSMOD_IDX) + relative_path, Source::User);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GraphicsModConfig::GetAbsolutePath() const
|
||||
{
|
||||
if (m_source == Source::System)
|
||||
{
|
||||
return WithUnifiedPathSeparators(fmt::format("{}{}{}", File::GetSysDirectory(),
|
||||
DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR, m_relative_path));
|
||||
}
|
||||
else
|
||||
{
|
||||
return WithUnifiedPathSeparators(File::GetUserPath(D_GRAPHICSMOD_IDX) + m_relative_path);
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphicsModConfig::DeserializeFromConfig(const picojson::value& value)
|
||||
{
|
||||
const auto& meta = value.get("meta");
|
||||
if (meta.is<picojson::object>())
|
||||
{
|
||||
const auto& title = meta.get("title");
|
||||
if (title.is<std::string>())
|
||||
{
|
||||
m_title = title.to_str();
|
||||
}
|
||||
|
||||
const auto& author = meta.get("author");
|
||||
if (author.is<std::string>())
|
||||
{
|
||||
m_author = author.to_str();
|
||||
}
|
||||
|
||||
const auto& description = meta.get("description");
|
||||
if (description.is<std::string>())
|
||||
{
|
||||
m_description = description.to_str();
|
||||
}
|
||||
}
|
||||
|
||||
const auto& groups = value.get("groups");
|
||||
if (groups.is<picojson::array>())
|
||||
{
|
||||
for (const auto& group_val : groups.get<picojson::array>())
|
||||
{
|
||||
if (!group_val.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Failed to load mod configuration file, specified group is not a json object");
|
||||
return false;
|
||||
}
|
||||
GraphicsTargetGroupConfig group;
|
||||
if (!group.DeserializeFromConfig(group_val.get<picojson::object>()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_groups.push_back(group);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& features = value.get("features");
|
||||
if (features.is<picojson::array>())
|
||||
{
|
||||
for (const auto& feature_val : features.get<picojson::array>())
|
||||
{
|
||||
if (!feature_val.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Failed to load mod configuration file, specified feature is not a json object");
|
||||
return false;
|
||||
}
|
||||
GraphicsModFeatureConfig feature;
|
||||
if (!feature.DeserializeFromConfig(feature_val.get<picojson::object>()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_features.push_back(feature);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicsModConfig::SerializeToProfile(picojson::object* obj) const
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
auto& json_obj = *obj;
|
||||
switch (m_source)
|
||||
{
|
||||
case Source::User:
|
||||
{
|
||||
json_obj["source"] = picojson::value{"user"};
|
||||
}
|
||||
break;
|
||||
case Source::System:
|
||||
{
|
||||
json_obj["source"] = picojson::value{"system"};
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
json_obj["path"] = picojson::value{m_relative_path};
|
||||
|
||||
picojson::array serialized_groups;
|
||||
for (const auto& group : m_groups)
|
||||
{
|
||||
picojson::object serialized_group;
|
||||
group.SerializeToProfile(&serialized_group);
|
||||
serialized_groups.push_back(picojson::value{serialized_group});
|
||||
}
|
||||
json_obj["groups"] = picojson::value{serialized_groups};
|
||||
|
||||
picojson::array serialized_features;
|
||||
for (const auto& feature : m_features)
|
||||
{
|
||||
picojson::object serialized_feature;
|
||||
feature.SerializeToProfile(&serialized_feature);
|
||||
serialized_features.push_back(picojson::value{serialized_feature});
|
||||
}
|
||||
json_obj["features"] = picojson::value{serialized_features};
|
||||
|
||||
json_obj["enabled"] = picojson::value{m_enabled};
|
||||
|
||||
json_obj["weight"] = picojson::value{static_cast<double>(m_weight)};
|
||||
}
|
||||
|
||||
void GraphicsModConfig::DeserializeFromProfile(const picojson::object& obj)
|
||||
{
|
||||
if (const auto it = obj.find("groups"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_groups = it->second.get<picojson::array>();
|
||||
if (serialized_groups.size() != m_groups.size())
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < serialized_groups.size(); i++)
|
||||
{
|
||||
const auto& serialized_group_val = serialized_groups[i];
|
||||
if (serialized_group_val.is<picojson::object>())
|
||||
{
|
||||
const auto& serialized_group = serialized_group_val.get<picojson::object>();
|
||||
m_groups[i].DeserializeFromProfile(serialized_group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto it = obj.find("features"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_features = it->second.get<picojson::array>();
|
||||
if (serialized_features.size() != m_features.size())
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < serialized_features.size(); i++)
|
||||
{
|
||||
const auto& serialized_feature_val = serialized_features[i];
|
||||
if (serialized_feature_val.is<picojson::object>())
|
||||
{
|
||||
const auto& serialized_feature = serialized_feature_val.get<picojson::object>();
|
||||
m_features[i].DeserializeFromProfile(serialized_feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto it = obj.find("enabled"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<bool>())
|
||||
{
|
||||
m_enabled = it->second.get<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto it = obj.find("weight"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<double>())
|
||||
{
|
||||
m_weight = static_cast<u16>(it->second.get<double>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GraphicsModConfig::operator<(const GraphicsModConfig& other) const
|
||||
{
|
||||
return m_weight < other.m_weight;
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModFeature.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTargetGroup.h"
|
||||
|
||||
struct GraphicsModConfig
|
||||
{
|
||||
std::string m_title;
|
||||
std::string m_author;
|
||||
std::string m_description;
|
||||
bool m_enabled = false;
|
||||
u16 m_weight = 0;
|
||||
std::string m_relative_path;
|
||||
|
||||
enum class Source
|
||||
{
|
||||
User,
|
||||
System
|
||||
};
|
||||
Source m_source = Source::User;
|
||||
|
||||
std::vector<GraphicsTargetGroupConfig> m_groups;
|
||||
std::vector<GraphicsModFeatureConfig> m_features;
|
||||
|
||||
static std::optional<GraphicsModConfig> Create(const std::string& file, Source source);
|
||||
static std::optional<GraphicsModConfig> Create(const picojson::object* obj);
|
||||
|
||||
std::string GetAbsolutePath() const;
|
||||
|
||||
bool DeserializeFromConfig(const picojson::value& value);
|
||||
|
||||
void SerializeToProfile(picojson::object* value) const;
|
||||
void DeserializeFromProfile(const picojson::object& value);
|
||||
|
||||
bool operator<(const GraphicsModConfig& other) const;
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModFeature.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
bool GraphicsModFeatureConfig::DeserializeFromConfig(const picojson::object& obj)
|
||||
{
|
||||
if (auto group_iter = obj.find("group"); group_iter != obj.end())
|
||||
{
|
||||
if (!group_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, specified feature's group is not a string");
|
||||
return false;
|
||||
}
|
||||
m_group = group_iter->second.get<std::string>();
|
||||
}
|
||||
|
||||
if (auto action_iter = obj.find("action"); action_iter != obj.end())
|
||||
{
|
||||
if (!action_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, specified feature's action is not a string");
|
||||
return false;
|
||||
}
|
||||
m_action = action_iter->second.get<std::string>();
|
||||
}
|
||||
|
||||
if (auto action_data_iter = obj.find("action_data"); action_data_iter != obj.end())
|
||||
{
|
||||
m_action_data = action_data_iter->second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicsModFeatureConfig::SerializeToProfile(picojson::object*) const
|
||||
{
|
||||
}
|
||||
|
||||
void GraphicsModFeatureConfig::DeserializeFromProfile(const picojson::object&)
|
||||
{
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
struct GraphicsModFeatureConfig
|
||||
{
|
||||
std::string m_group;
|
||||
std::string m_action;
|
||||
picojson::value m_action_data;
|
||||
|
||||
bool DeserializeFromConfig(const picojson::object& value);
|
||||
|
||||
void SerializeToProfile(picojson::object* value) const;
|
||||
void DeserializeFromProfile(const picojson::object& value);
|
||||
};
|
@ -0,0 +1,191 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileSearch.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Constants.h"
|
||||
#include "VideoCommon/HiresTextures.h"
|
||||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(const std::string& game_id) : m_game_id(game_id)
|
||||
{
|
||||
}
|
||||
|
||||
GraphicsModGroupConfig::~GraphicsModGroupConfig() = default;
|
||||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(const GraphicsModGroupConfig&) = default;
|
||||
|
||||
GraphicsModGroupConfig::GraphicsModGroupConfig(GraphicsModGroupConfig&&) = default;
|
||||
|
||||
GraphicsModGroupConfig& GraphicsModGroupConfig::operator=(const GraphicsModGroupConfig&) = default;
|
||||
|
||||
GraphicsModGroupConfig& GraphicsModGroupConfig::operator=(GraphicsModGroupConfig&&) = default;
|
||||
|
||||
void GraphicsModGroupConfig::Load()
|
||||
{
|
||||
const std::string file_path = GetPath();
|
||||
|
||||
std::set<std::string> known_paths;
|
||||
if (File::Exists(file_path))
|
||||
{
|
||||
std::string json_data;
|
||||
if (!File::ReadFileToString(file_path, json_data))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod group json file '{}'", file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
picojson::value root;
|
||||
const auto error = picojson::parse(root, json_data);
|
||||
|
||||
if (!error.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load graphics mod group json file '{}' due to parse error: {}",
|
||||
file_path, error);
|
||||
return;
|
||||
}
|
||||
if (!root.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load graphics mod group json file '{}' due to root not being an object!",
|
||||
file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& mods = root.get("mods");
|
||||
if (mods.is<picojson::array>())
|
||||
{
|
||||
for (const auto& mod_json : mods.get<picojson::array>())
|
||||
{
|
||||
if (mod_json.is<picojson::object>())
|
||||
{
|
||||
const auto& mod_json_obj = mod_json.get<picojson::object>();
|
||||
auto graphics_mod = GraphicsModConfig::Create(&mod_json_obj);
|
||||
if (!graphics_mod)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
graphics_mod->DeserializeFromProfile(mod_json_obj);
|
||||
|
||||
auto mod_full_path = graphics_mod->GetAbsolutePath();
|
||||
known_paths.insert(std::move(mod_full_path));
|
||||
m_graphics_mods.push_back(*graphics_mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto try_add_mod = [&known_paths, this](const std::string& dir,
|
||||
GraphicsModConfig::Source source) {
|
||||
auto file = dir + DIR_SEP + "metadata.json";
|
||||
UnifyPathSeparators(file);
|
||||
if (known_paths.find(file) != known_paths.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto mod = GraphicsModConfig::Create(file, source);
|
||||
if (mod)
|
||||
{
|
||||
m_graphics_mods.push_back(*mod);
|
||||
}
|
||||
};
|
||||
|
||||
const std::set<std::string> graphics_mod_user_directories =
|
||||
GetTextureDirectoriesWithGameId(File::GetUserPath(D_GRAPHICSMOD_IDX), m_game_id);
|
||||
|
||||
for (const auto& graphics_mod_directory : graphics_mod_user_directories)
|
||||
{
|
||||
try_add_mod(graphics_mod_directory, GraphicsModConfig::Source::User);
|
||||
}
|
||||
|
||||
const std::set<std::string> graphics_mod_system_directories = GetTextureDirectoriesWithGameId(
|
||||
File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR, m_game_id);
|
||||
|
||||
for (const auto& graphics_mod_directory : graphics_mod_system_directories)
|
||||
{
|
||||
try_add_mod(graphics_mod_directory, GraphicsModConfig::Source::System);
|
||||
}
|
||||
|
||||
std::sort(m_graphics_mods.begin(), m_graphics_mods.end());
|
||||
for (auto& mod : m_graphics_mods)
|
||||
{
|
||||
m_path_to_graphics_mod[mod.GetAbsolutePath()] = &mod;
|
||||
}
|
||||
|
||||
m_change_count++;
|
||||
}
|
||||
|
||||
void GraphicsModGroupConfig::Save() const
|
||||
{
|
||||
const std::string file_path = GetPath();
|
||||
std::ofstream json_stream;
|
||||
File::OpenFStream(json_stream, file_path, std::ios_base::out);
|
||||
if (!json_stream.is_open())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to open graphics mod group json file '{}' for writing", file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
picojson::object serialized_root;
|
||||
picojson::array serialized_mods;
|
||||
for (const auto& mod : m_graphics_mods)
|
||||
{
|
||||
picojson::object serialized_mod;
|
||||
mod.SerializeToProfile(&serialized_mod);
|
||||
serialized_mods.push_back(picojson::value{serialized_mod});
|
||||
}
|
||||
serialized_root["mods"] = picojson::value{serialized_mods};
|
||||
|
||||
const auto output = picojson::value{serialized_root}.serialize(true);
|
||||
json_stream << output;
|
||||
}
|
||||
|
||||
void GraphicsModGroupConfig::SetChangeCount(u32 change_count)
|
||||
{
|
||||
m_change_count = change_count;
|
||||
}
|
||||
|
||||
u32 GraphicsModGroupConfig::GetChangeCount() const
|
||||
{
|
||||
return m_change_count;
|
||||
}
|
||||
|
||||
const std::vector<GraphicsModConfig>& GraphicsModGroupConfig::GetMods() const
|
||||
{
|
||||
return m_graphics_mods;
|
||||
}
|
||||
|
||||
GraphicsModConfig* GraphicsModGroupConfig::GetMod(const std::string& absolute_path) const
|
||||
{
|
||||
if (const auto iter = m_path_to_graphics_mod.find(absolute_path);
|
||||
iter != m_path_to_graphics_mod.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string& GraphicsModGroupConfig::GetGameID() const
|
||||
{
|
||||
return m_game_id;
|
||||
}
|
||||
|
||||
std::string GraphicsModGroupConfig::GetPath() const
|
||||
{
|
||||
const std::string game_mod_root = File::GetUserPath(D_CONFIG_IDX) + GRAPHICSMOD_CONFIG_DIR;
|
||||
return fmt::format("{}/{}.json", game_mod_root, m_game_id);
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
struct GraphicsModConfig;
|
||||
|
||||
class GraphicsModGroupConfig
|
||||
{
|
||||
public:
|
||||
explicit GraphicsModGroupConfig(const std::string& game_id);
|
||||
~GraphicsModGroupConfig();
|
||||
|
||||
GraphicsModGroupConfig(const GraphicsModGroupConfig&);
|
||||
GraphicsModGroupConfig(GraphicsModGroupConfig&&);
|
||||
|
||||
GraphicsModGroupConfig& operator=(const GraphicsModGroupConfig&);
|
||||
GraphicsModGroupConfig& operator=(GraphicsModGroupConfig&&);
|
||||
|
||||
void Load();
|
||||
void Save() const;
|
||||
|
||||
void SetChangeCount(u32 change_count);
|
||||
u32 GetChangeCount() const;
|
||||
|
||||
const std::vector<GraphicsModConfig>& GetMods() const;
|
||||
|
||||
GraphicsModConfig* GetMod(const std::string& absolute_path) const;
|
||||
|
||||
const std::string& GetGameID() const;
|
||||
|
||||
private:
|
||||
std::string GetPath() const;
|
||||
std::string m_game_id;
|
||||
std::vector<GraphicsModConfig> m_graphics_mods;
|
||||
std::map<std::string, GraphicsModConfig*> m_path_to_graphics_mod;
|
||||
u32 m_change_count = 0;
|
||||
};
|
@ -0,0 +1,254 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTarget.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T, std::enable_if_t<std::is_base_of_v<FBTarget, T>, int> = 0>
|
||||
std::optional<T> DeserializeFBTargetFromConfig(const picojson::object& obj, std::string_view prefix)
|
||||
{
|
||||
T fb;
|
||||
const auto texture_filename_iter = obj.find("texture_filename");
|
||||
if (texture_filename_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'texture_filename' not found");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!texture_filename_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, option 'texture_filename' is not a string type");
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto texture_filename = texture_filename_iter->second.get<std::string>();
|
||||
const auto texture_filename_without_prefix = texture_filename.substr(prefix.size() + 1);
|
||||
const auto split_str_values = SplitString(texture_filename_without_prefix, '_');
|
||||
if (split_str_values.size() == 1)
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is not valid");
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto split_width_height_values = SplitString(texture_filename_without_prefix, 'x');
|
||||
if (split_width_height_values.size() != 2)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, width and height separator found more matches than expected");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t width_underscore_pos = split_width_height_values[0].find_last_of('_');
|
||||
std::string width_str;
|
||||
if (width_underscore_pos == std::string::npos)
|
||||
{
|
||||
width_str = split_width_height_values[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
width_str = split_width_height_values[0].substr(width_underscore_pos + 1);
|
||||
}
|
||||
if (!TryParse(width_str, &fb.m_width))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, width not a number");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t height_underscore_pos = split_width_height_values[1].find_first_of('_');
|
||||
if (height_underscore_pos == std::string::npos ||
|
||||
height_underscore_pos == split_width_height_values[1].size() - 1)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, underscore after height is missing or incomplete");
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string height_str = split_width_height_values[1].substr(0, height_underscore_pos);
|
||||
if (!TryParse(height_str, &fb.m_height))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, height not a number");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t format_underscore_pos =
|
||||
split_width_height_values[1].find_first_of('_', height_underscore_pos + 1);
|
||||
|
||||
std::string format_str;
|
||||
if (format_underscore_pos == std::string::npos)
|
||||
{
|
||||
format_str = split_width_height_values[1].substr(height_underscore_pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
format_str = split_width_height_values[1].substr(
|
||||
height_underscore_pos + 1, (format_underscore_pos - height_underscore_pos) - 1);
|
||||
}
|
||||
u32 format;
|
||||
if (!TryParse(format_str, &format))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, texture format is not a number");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!IsValidTextureFormat(static_cast<TextureFormat>(format)))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' is "
|
||||
"not valid, texture format is not valid");
|
||||
return std::nullopt;
|
||||
}
|
||||
fb.m_texture_format = static_cast<TextureFormat>(format);
|
||||
|
||||
return fb;
|
||||
}
|
||||
std::optional<std::string> ExtractTextureFilenameForConfig(const picojson::object& obj)
|
||||
{
|
||||
const auto texture_filename_iter = obj.find("texture_filename");
|
||||
if (texture_filename_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'texture_filename' not found");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!texture_filename_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, option 'texture_filename' is not a string type");
|
||||
return std::nullopt;
|
||||
}
|
||||
std::string texture_info = texture_filename_iter->second.get<std::string>();
|
||||
if (texture_info.find(EFB_DUMP_PREFIX) != std::string::npos)
|
||||
{
|
||||
const auto letter_c_pos = texture_info.find_first_of('n');
|
||||
if (letter_c_pos == std::string::npos)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' "
|
||||
"is an efb without a count");
|
||||
return std::nullopt;
|
||||
}
|
||||
texture_info =
|
||||
texture_info.substr(letter_c_pos - 1, texture_info.find_first_of("_", letter_c_pos));
|
||||
}
|
||||
else if (texture_info.find(XFB_DUMP_PREFIX) != std::string::npos)
|
||||
{
|
||||
const auto letter_c_pos = texture_info.find_first_of('n');
|
||||
if (letter_c_pos == std::string::npos)
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, value in 'texture_filename' "
|
||||
"is an xfb without a count");
|
||||
return std::nullopt;
|
||||
}
|
||||
texture_info =
|
||||
texture_info.substr(letter_c_pos - 1, texture_info.find_first_of("_", letter_c_pos));
|
||||
}
|
||||
return texture_info;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj)
|
||||
{
|
||||
const auto type_iter = obj.find("type");
|
||||
if (type_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'type' not found");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!type_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'type' is not a string type");
|
||||
return std::nullopt;
|
||||
}
|
||||
const std::string& type = type_iter->second.get<std::string>();
|
||||
if (type == "draw_started")
|
||||
{
|
||||
std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
|
||||
if (!texture_info.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
DrawStartedTextureTarget target;
|
||||
target.m_texture_info_string = texture_info.value();
|
||||
return target;
|
||||
}
|
||||
else if (type == "load_texture")
|
||||
{
|
||||
std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
|
||||
if (!texture_info.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
LoadTextureTarget target;
|
||||
target.m_texture_info_string = texture_info.value();
|
||||
return target;
|
||||
}
|
||||
else if (type == "efb")
|
||||
{
|
||||
return DeserializeFBTargetFromConfig<EFBTarget>(obj, EFB_DUMP_PREFIX);
|
||||
}
|
||||
else if (type == "xfb")
|
||||
{
|
||||
return DeserializeFBTargetFromConfig<XFBTarget>(obj, EFB_DUMP_PREFIX);
|
||||
}
|
||||
else if (type == "projection")
|
||||
{
|
||||
ProjectionTarget target;
|
||||
const auto texture_iter = obj.find("texture_filename");
|
||||
if (texture_iter != obj.end())
|
||||
{
|
||||
std::optional<std::string> texture_info = ExtractTextureFilenameForConfig(obj);
|
||||
if (!texture_info.has_value())
|
||||
return std::nullopt;
|
||||
target.m_texture_info_string = texture_info;
|
||||
}
|
||||
const auto value_iter = obj.find("value");
|
||||
if (value_iter == obj.end())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'value' not found");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!value_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'value' is not a string type");
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto& value_str = value_iter->second.get<std::string>();
|
||||
if (value_str == "2d")
|
||||
{
|
||||
target.m_projection_type = ProjectionType::Orthographic;
|
||||
}
|
||||
else if (value_str == "3d")
|
||||
{
|
||||
target.m_projection_type = ProjectionType::Perspective;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to load mod configuration file, option 'value' is not a valid "
|
||||
"value, valid values are: 2d, 3d");
|
||||
return std::nullopt;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Failed to load mod configuration file, option 'type' is not a valid value");
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SerializeTargetToProfile(picojson::object*, const GraphicsTargetConfig&)
|
||||
{
|
||||
// Added for consistency, no functionality as of now
|
||||
}
|
||||
|
||||
void DeserializeTargetFromProfile(const picojson::object&, GraphicsTargetConfig*)
|
||||
{
|
||||
// Added for consistency, no functionality as of now
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
|
||||
struct TextureTarget
|
||||
{
|
||||
std::string m_texture_info_string;
|
||||
};
|
||||
|
||||
struct DrawStartedTextureTarget final : public TextureTarget
|
||||
{
|
||||
};
|
||||
|
||||
struct LoadTextureTarget final : public TextureTarget
|
||||
{
|
||||
};
|
||||
|
||||
struct FBTarget
|
||||
{
|
||||
u32 m_height = 0;
|
||||
u32 m_width = 0;
|
||||
TextureFormat m_texture_format = TextureFormat::I4;
|
||||
};
|
||||
|
||||
struct EFBTarget final : public FBTarget
|
||||
{
|
||||
};
|
||||
|
||||
struct XFBTarget final : public FBTarget
|
||||
{
|
||||
};
|
||||
|
||||
struct ProjectionTarget
|
||||
{
|
||||
std::optional<std::string> m_texture_info_string;
|
||||
ProjectionType m_projection_type = ProjectionType::Perspective;
|
||||
};
|
||||
|
||||
using GraphicsTargetConfig = std::variant<DrawStartedTextureTarget, LoadTextureTarget, EFBTarget,
|
||||
XFBTarget, ProjectionTarget>;
|
||||
|
||||
std::optional<GraphicsTargetConfig> DeserializeTargetFromConfig(const picojson::object& obj);
|
||||
|
||||
void SerializeTargetToProfile(picojson::object* obj, const GraphicsTargetConfig& target);
|
||||
void DeserializeTargetFromProfile(const picojson::object& obj, GraphicsTargetConfig* target);
|
@ -0,0 +1,88 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTargetGroup.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
bool GraphicsTargetGroupConfig::DeserializeFromConfig(const picojson::object& obj)
|
||||
{
|
||||
if (auto name_iter = obj.find("name"); name_iter != obj.end())
|
||||
{
|
||||
if (!name_iter->second.is<std::string>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Failed to load mod configuration file, specified group's name is not a string");
|
||||
return false;
|
||||
}
|
||||
m_name = name_iter->second.get<std::string>();
|
||||
}
|
||||
|
||||
if (auto targets_iter = obj.find("targets"); targets_iter != obj.end())
|
||||
{
|
||||
if (!targets_iter->second.is<picojson::array>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load mod configuration file, specified group's targets is not an array");
|
||||
return false;
|
||||
}
|
||||
for (const auto& target_val : targets_iter->second.get<picojson::array>())
|
||||
{
|
||||
if (!target_val.is<picojson::object>())
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Failed to load shader configuration file, specified target is not a json object");
|
||||
return false;
|
||||
}
|
||||
const auto target = DeserializeTargetFromConfig(target_val.get<picojson::object>());
|
||||
if (!target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_targets.push_back(*target);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicsTargetGroupConfig::SerializeToProfile(picojson::object* obj) const
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
auto& json_obj = *obj;
|
||||
picojson::array serialized_targets;
|
||||
for (const auto& target : m_targets)
|
||||
{
|
||||
picojson::object serialized_target;
|
||||
SerializeTargetToProfile(&serialized_target, target);
|
||||
serialized_targets.push_back(picojson::value{serialized_target});
|
||||
}
|
||||
json_obj["targets"] = picojson::value{serialized_targets};
|
||||
}
|
||||
|
||||
void GraphicsTargetGroupConfig::DeserializeFromProfile(const picojson::object& obj)
|
||||
{
|
||||
if (const auto it = obj.find("targets"); it != obj.end())
|
||||
{
|
||||
if (it->second.is<picojson::array>())
|
||||
{
|
||||
auto serialized_targets = it->second.get<picojson::array>();
|
||||
if (serialized_targets.size() != m_targets.size())
|
||||
return;
|
||||
|
||||
for (std::size_t i = 0; i < serialized_targets.size(); i++)
|
||||
{
|
||||
const auto& serialized_target_val = serialized_targets[i];
|
||||
if (serialized_target_val.is<picojson::object>())
|
||||
{
|
||||
const auto& serialized_target = serialized_target_val.get<picojson::object>();
|
||||
DeserializeTargetFromProfile(serialized_target, &m_targets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsTarget.h"
|
||||
|
||||
struct GraphicsTargetGroupConfig
|
||||
{
|
||||
std::string m_name;
|
||||
std::vector<GraphicsTargetConfig> m_targets;
|
||||
|
||||
bool DeserializeFromConfig(const picojson::object& obj);
|
||||
|
||||
void SerializeToProfile(picojson::object* obj) const;
|
||||
void DeserializeFromProfile(const picojson::object& obj);
|
||||
};
|
Loading…
Reference in New Issue
Block a user