GraphicsMod.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // Copyright 2022 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
  4. #include <fmt/format.h>
  5. #include "Common/CommonPaths.h"
  6. #include "Common/FileUtil.h"
  7. #include "Common/JsonUtil.h"
  8. #include "Common/Logging/Log.h"
  9. #include "Common/StringUtil.h"
  10. #include "VideoCommon/GraphicsModSystem/Constants.h"
  11. std::optional<GraphicsModConfig> GraphicsModConfig::Create(const std::string& file_path,
  12. Source source)
  13. {
  14. picojson::value root;
  15. std::string error;
  16. if (!JsonFromFile(file_path, &root, &error))
  17. {
  18. ERROR_LOG_FMT(VIDEO, "Failed to load graphics mod json file '{}' due to parse error: {}",
  19. file_path, error);
  20. return std::nullopt;
  21. }
  22. GraphicsModConfig result;
  23. if (!result.DeserializeFromConfig(root))
  24. {
  25. return std::nullopt;
  26. }
  27. result.m_source = source;
  28. if (source == Source::User)
  29. {
  30. const std::string base_path = File::GetUserPath(D_GRAPHICSMOD_IDX);
  31. if (base_path.size() > file_path.size())
  32. {
  33. ERROR_LOG_FMT(
  34. VIDEO,
  35. "Failed to load graphics mod json file '{}' due to it not matching the base path: {}",
  36. file_path, base_path);
  37. return std::nullopt;
  38. }
  39. result.m_relative_path = file_path.substr(base_path.size());
  40. }
  41. else
  42. {
  43. const std::string base_path = File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR;
  44. if (base_path.size() > file_path.size())
  45. {
  46. ERROR_LOG_FMT(
  47. VIDEO,
  48. "Failed to load graphics mod json file '{}' due to it not matching the base path: {}",
  49. file_path, base_path);
  50. return std::nullopt;
  51. }
  52. result.m_relative_path = file_path.substr(base_path.size());
  53. }
  54. return result;
  55. }
  56. std::optional<GraphicsModConfig> GraphicsModConfig::Create(const picojson::object* obj)
  57. {
  58. if (!obj)
  59. return std::nullopt;
  60. const auto source_it = obj->find("source");
  61. if (source_it == obj->end())
  62. {
  63. return std::nullopt;
  64. }
  65. const std::string source_str = source_it->second.to_str();
  66. const auto path_it = obj->find("path");
  67. if (path_it == obj->end())
  68. {
  69. return std::nullopt;
  70. }
  71. const std::string relative_path = path_it->second.to_str();
  72. if (source_str == "system")
  73. {
  74. return Create(fmt::format("{}{}{}", File::GetSysDirectory(), DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR,
  75. relative_path),
  76. Source::System);
  77. }
  78. else
  79. {
  80. return Create(File::GetUserPath(D_GRAPHICSMOD_IDX) + relative_path, Source::User);
  81. }
  82. }
  83. std::string GraphicsModConfig::GetAbsolutePath() const
  84. {
  85. if (m_source == Source::System)
  86. {
  87. return WithUnifiedPathSeparators(fmt::format("{}{}{}", File::GetSysDirectory(),
  88. DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR, m_relative_path));
  89. }
  90. else
  91. {
  92. return WithUnifiedPathSeparators(File::GetUserPath(D_GRAPHICSMOD_IDX) + m_relative_path);
  93. }
  94. }
  95. void GraphicsModConfig::SerializeToConfig(picojson::object& json_obj) const
  96. {
  97. picojson::object serialized_metadata;
  98. serialized_metadata.emplace("title", m_title);
  99. serialized_metadata.emplace("author", m_author);
  100. serialized_metadata.emplace("description", m_description);
  101. json_obj.emplace("meta", std::move(serialized_metadata));
  102. picojson::array serialized_groups;
  103. for (const auto& group : m_groups)
  104. {
  105. picojson::object serialized_group;
  106. group.SerializeToConfig(serialized_group);
  107. serialized_groups.emplace_back(std::move(serialized_group));
  108. }
  109. json_obj.emplace("groups", std::move(serialized_groups));
  110. picojson::array serialized_features;
  111. for (const auto& feature : m_features)
  112. {
  113. picojson::object serialized_feature;
  114. feature.SerializeToConfig(serialized_feature);
  115. serialized_features.emplace_back(std::move(serialized_feature));
  116. }
  117. json_obj.emplace("features", std::move(serialized_features));
  118. picojson::array serialized_assets;
  119. for (const auto& asset : m_assets)
  120. {
  121. picojson::object serialized_asset;
  122. asset.SerializeToConfig(serialized_asset);
  123. serialized_assets.emplace_back(std::move(serialized_asset));
  124. }
  125. json_obj.emplace("assets", std::move(serialized_assets));
  126. }
  127. bool GraphicsModConfig::DeserializeFromConfig(const picojson::value& value)
  128. {
  129. const auto& meta = value.get("meta");
  130. if (meta.is<picojson::object>())
  131. {
  132. const auto& title = meta.get("title");
  133. if (title.is<std::string>())
  134. {
  135. m_title = title.to_str();
  136. }
  137. const auto& author = meta.get("author");
  138. if (author.is<std::string>())
  139. {
  140. m_author = author.to_str();
  141. }
  142. const auto& description = meta.get("description");
  143. if (description.is<std::string>())
  144. {
  145. m_description = description.to_str();
  146. }
  147. }
  148. const auto& groups = value.get("groups");
  149. if (groups.is<picojson::array>())
  150. {
  151. for (const auto& group_val : groups.get<picojson::array>())
  152. {
  153. if (!group_val.is<picojson::object>())
  154. {
  155. ERROR_LOG_FMT(
  156. VIDEO, "Failed to load mod configuration file, specified group is not a json object");
  157. return false;
  158. }
  159. GraphicsTargetGroupConfig group;
  160. if (!group.DeserializeFromConfig(group_val.get<picojson::object>()))
  161. {
  162. return false;
  163. }
  164. m_groups.push_back(std::move(group));
  165. }
  166. }
  167. const auto& features = value.get("features");
  168. if (features.is<picojson::array>())
  169. {
  170. for (const auto& feature_val : features.get<picojson::array>())
  171. {
  172. if (!feature_val.is<picojson::object>())
  173. {
  174. ERROR_LOG_FMT(
  175. VIDEO, "Failed to load mod configuration file, specified feature is not a json object");
  176. return false;
  177. }
  178. GraphicsModFeatureConfig feature;
  179. if (!feature.DeserializeFromConfig(feature_val.get<picojson::object>()))
  180. {
  181. return false;
  182. }
  183. m_features.push_back(std::move(feature));
  184. }
  185. }
  186. const auto& assets = value.get("assets");
  187. if (assets.is<picojson::array>())
  188. {
  189. for (const auto& asset_val : assets.get<picojson::array>())
  190. {
  191. if (!asset_val.is<picojson::object>())
  192. {
  193. ERROR_LOG_FMT(
  194. VIDEO, "Failed to load mod configuration file, specified asset is not a json object");
  195. return false;
  196. }
  197. GraphicsModAssetConfig asset;
  198. if (!asset.DeserializeFromConfig(asset_val.get<picojson::object>()))
  199. {
  200. return false;
  201. }
  202. m_assets.push_back(std::move(asset));
  203. }
  204. }
  205. return true;
  206. }
  207. void GraphicsModConfig::SerializeToProfile(picojson::object* obj) const
  208. {
  209. if (!obj)
  210. return;
  211. auto& json_obj = *obj;
  212. switch (m_source)
  213. {
  214. case Source::User:
  215. json_obj.emplace("source", "user");
  216. break;
  217. case Source::System:
  218. json_obj.emplace("source", "system");
  219. break;
  220. }
  221. json_obj.emplace("path", m_relative_path);
  222. picojson::array serialized_groups;
  223. for (const auto& group : m_groups)
  224. {
  225. picojson::object serialized_group;
  226. group.SerializeToProfile(&serialized_group);
  227. serialized_groups.emplace_back(std::move(serialized_group));
  228. }
  229. json_obj.emplace("groups", std::move(serialized_groups));
  230. picojson::array serialized_features;
  231. for (const auto& feature : m_features)
  232. {
  233. picojson::object serialized_feature;
  234. feature.SerializeToProfile(&serialized_feature);
  235. serialized_features.emplace_back(std::move(serialized_feature));
  236. }
  237. json_obj.emplace("features", std::move(serialized_features));
  238. json_obj.emplace("enabled", m_enabled);
  239. json_obj.emplace("weight", static_cast<double>(m_weight));
  240. }
  241. void GraphicsModConfig::DeserializeFromProfile(const picojson::object& obj)
  242. {
  243. if (const auto it = obj.find("groups"); it != obj.end())
  244. {
  245. if (it->second.is<picojson::array>())
  246. {
  247. const auto& serialized_groups = it->second.get<picojson::array>();
  248. if (serialized_groups.size() != m_groups.size())
  249. return;
  250. for (std::size_t i = 0; i < serialized_groups.size(); i++)
  251. {
  252. const auto& serialized_group_val = serialized_groups[i];
  253. if (serialized_group_val.is<picojson::object>())
  254. {
  255. const auto& serialized_group = serialized_group_val.get<picojson::object>();
  256. m_groups[i].DeserializeFromProfile(serialized_group);
  257. }
  258. }
  259. }
  260. }
  261. if (const auto it = obj.find("features"); it != obj.end())
  262. {
  263. if (it->second.is<picojson::array>())
  264. {
  265. const auto& serialized_features = it->second.get<picojson::array>();
  266. if (serialized_features.size() != m_features.size())
  267. return;
  268. for (std::size_t i = 0; i < serialized_features.size(); i++)
  269. {
  270. const auto& serialized_feature_val = serialized_features[i];
  271. if (serialized_feature_val.is<picojson::object>())
  272. {
  273. const auto& serialized_feature = serialized_feature_val.get<picojson::object>();
  274. m_features[i].DeserializeFromProfile(serialized_feature);
  275. }
  276. }
  277. }
  278. }
  279. if (const auto it = obj.find("enabled"); it != obj.end())
  280. {
  281. if (it->second.is<bool>())
  282. {
  283. m_enabled = it->second.get<bool>();
  284. }
  285. }
  286. if (const auto it = obj.find("weight"); it != obj.end())
  287. {
  288. if (it->second.is<double>())
  289. {
  290. m_weight = static_cast<u16>(it->second.get<double>());
  291. }
  292. }
  293. }