[3PP] Replaced cpptoml with tomlplusplus
Also adjusted code that used cpptoml to be used with toml++ and some more changes
This commit is contained in:
parent
d246e3bc70
commit
60b31af811
|
@ -31,9 +31,9 @@
|
||||||
[submodule "third_party/capstone"]
|
[submodule "third_party/capstone"]
|
||||||
path = third_party/capstone
|
path = third_party/capstone
|
||||||
url = https://github.com/capstone-engine/capstone.git
|
url = https://github.com/capstone-engine/capstone.git
|
||||||
[submodule "third_party/cpptoml"]
|
[submodule "third_party/tomlplusplus"]
|
||||||
path = third_party/cpptoml
|
path = third_party/tomlplusplus
|
||||||
url = https://github.com/skystrife/cpptoml.git
|
url = https://github.com/marzer/tomlplusplus.git
|
||||||
[submodule "third_party/cxxopts"]
|
[submodule "third_party/cxxopts"]
|
||||||
path = third_party/cxxopts
|
path = third_party/cxxopts
|
||||||
url = https://github.com/jarro2783/cxxopts.git
|
url = https://github.com/jarro2783/cxxopts.git
|
||||||
|
|
|
@ -252,7 +252,7 @@ workspace("xenia")
|
||||||
include("third_party/dxbc.lua")
|
include("third_party/dxbc.lua")
|
||||||
include("third_party/discord-rpc.lua")
|
include("third_party/discord-rpc.lua")
|
||||||
include("third_party/cxxopts.lua")
|
include("third_party/cxxopts.lua")
|
||||||
include("third_party/cpptoml.lua")
|
include("third_party/tomlplusplus.lua")
|
||||||
include("third_party/FFmpeg/premake5.lua")
|
include("third_party/FFmpeg/premake5.lua")
|
||||||
include("third_party/fmt.lua")
|
include("third_party/fmt.lua")
|
||||||
include("third_party/glslang-spirv.lua")
|
include("third_party/glslang-spirv.lua")
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "third_party/cpptoml/include/cpptoml.h"
|
|
||||||
#include "third_party/fmt/include/fmt/chrono.h"
|
#include "third_party/fmt/include/fmt/chrono.h"
|
||||||
#include "third_party/fmt/include/fmt/format.h"
|
#include "third_party/fmt/include/fmt/format.h"
|
||||||
#include "third_party/imgui/imgui.h"
|
#include "third_party/imgui/imgui.h"
|
||||||
#include "third_party/stb/stb_image_write.h"
|
#include "third_party/stb/stb_image_write.h"
|
||||||
|
#include "third_party/tomlplusplus/toml.hpp"
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/clock.h"
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/cvar.h"
|
#include "xenia/base/cvar.h"
|
||||||
|
@ -2012,27 +2012,27 @@ void EmulatorWindow::LoadRecentlyLaunchedTitles() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<cpptoml::table> parsed_file;
|
toml::parse_result parsed_file;
|
||||||
try {
|
try {
|
||||||
cpptoml::parser p(file);
|
parsed_file = toml::parse(file);
|
||||||
parsed_file = p.parse();
|
} catch (toml::parse_error& exception) {
|
||||||
} catch (cpptoml::parse_exception& exception) {
|
|
||||||
XELOGE("Cannot parse file: recent.toml. Error: {}", exception.what());
|
XELOGE("Cannot parse file: recent.toml. Error: {}", exception.what());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsed_file->is_table()) {
|
if (parsed_file.is_table()) {
|
||||||
for (const auto& [index, entry] : *parsed_file->as_table()) {
|
for (const auto& [index, entry] : *parsed_file.as_table()) {
|
||||||
if (!entry->is_table()) {
|
if (!entry.is_table()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::shared_ptr<cpptoml::table> entry_table = entry->as_table();
|
const toml::table* entry_table = entry.as_table();
|
||||||
|
|
||||||
std::string title_name = *entry_table->get_as<std::string>("title_name");
|
std::string title_name =
|
||||||
std::string path = *entry_table->get_as<std::string>("path");
|
entry_table->get_as<std::string>("title_name")->get();
|
||||||
|
std::string path = entry_table->get_as<std::string>("path")->get();
|
||||||
std::time_t last_run_time =
|
std::time_t last_run_time =
|
||||||
*entry_table->get_as<uint64_t>("last_run_time");
|
entry_table->get_as<int64_t>("last_run_time")->get();
|
||||||
|
|
||||||
std::error_code ec = {};
|
std::error_code ec = {};
|
||||||
if (path.empty() || !std::filesystem::exists(path, ec)) {
|
if (path.empty() || !std::filesystem::exists(path, ec)) {
|
||||||
|
@ -2063,31 +2063,28 @@ void EmulatorWindow::AddRecentlyLaunchedTitle(
|
||||||
recently_launched_titles_.insert(recently_launched_titles_.cbegin(),
|
recently_launched_titles_.insert(recently_launched_titles_.cbegin(),
|
||||||
{title_name, path_to_file, time(nullptr)});
|
{title_name, path_to_file, time(nullptr)});
|
||||||
// Serialize to toml
|
// Serialize to toml
|
||||||
auto toml_table = cpptoml::make_table();
|
auto toml_table = toml::table();
|
||||||
|
|
||||||
uint8_t index = 0;
|
uint8_t index = 0;
|
||||||
for (const RecentTitleEntry& entry : recently_launched_titles_) {
|
for (const RecentTitleEntry& entry : recently_launched_titles_) {
|
||||||
auto entry_table = cpptoml::make_table();
|
auto entry_table = toml::table();
|
||||||
|
|
||||||
// Fill entry under specific index.
|
// Fill entry under specific index.
|
||||||
std::string str_path = xe::path_to_utf8(entry.path_to_file);
|
std::string str_path = xe::path_to_utf8(entry.path_to_file);
|
||||||
entry_table->insert("title_name", entry.title_name);
|
entry_table.insert("title_name", entry.title_name);
|
||||||
entry_table->insert("path", str_path);
|
entry_table.insert("path", str_path);
|
||||||
entry_table->insert("last_run_time", entry.last_run_time);
|
entry_table.insert("last_run_time", entry.last_run_time);
|
||||||
entry_table->end();
|
|
||||||
|
|
||||||
toml_table->insert(std::to_string(index++), entry_table);
|
toml_table.insert(std::to_string(index++), entry_table);
|
||||||
|
|
||||||
if (index >= cvars::recent_titles_entry_amount) {
|
if (index >= cvars::recent_titles_entry_amount) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toml_table->end();
|
|
||||||
|
|
||||||
// Open and write serialized data.
|
// Open and write serialized data.
|
||||||
std::ofstream file(emulator()->storage_root() / kRecentlyPlayedTitlesFilename,
|
std::ofstream file(emulator()->storage_root() / kRecentlyPlayedTitlesFilename,
|
||||||
std::ofstream::trunc);
|
std::ofstream::trunc);
|
||||||
file << *toml_table;
|
file << toml_table;
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ void ParseLaunchArguments(int& argc, char**& argv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace toml {
|
namespace toml_internal {
|
||||||
|
|
||||||
std::string EscapeBasicString(const std::string_view view) {
|
std::string EscapeBasicString(const std::string_view view) {
|
||||||
std::string result;
|
std::string result;
|
||||||
|
@ -202,7 +202,7 @@ std::string EscapeString(const std::string_view view) {
|
||||||
if (xe::utf8::find_any_of(view, escape_chars) == std::string_view::npos) {
|
if (xe::utf8::find_any_of(view, escape_chars) == std::string_view::npos) {
|
||||||
return "'" + std::string(view) + "'";
|
return "'" + std::string(view) + "'";
|
||||||
} else {
|
} else {
|
||||||
return "\"" + toml::EscapeBasicString(view) + "\"";
|
return "\"" + toml_internal::EscapeBasicString(view) + "\"";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// multi line
|
// multi line
|
||||||
|
@ -210,11 +210,12 @@ std::string EscapeString(const std::string_view view) {
|
||||||
xe::utf8::find_first_of(view, u8"'''") == std::string_view::npos) {
|
xe::utf8::find_first_of(view, u8"'''") == std::string_view::npos) {
|
||||||
return "'''\n" + std::string(view) + "'''";
|
return "'''\n" + std::string(view) + "'''";
|
||||||
} else {
|
} else {
|
||||||
return u8"\"\"\"\n" + toml::EscapeMultilineBasicString(view) + u8"\"\"\"";
|
return u8"\"\"\"\n" + toml_internal::EscapeMultilineBasicString(view) +
|
||||||
|
u8"\"\"\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace toml
|
} // namespace toml_internal
|
||||||
|
|
||||||
} // namespace cvar
|
} // namespace cvar
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "third_party/cpptoml/include/cpptoml.h"
|
|
||||||
#include "third_party/cxxopts/include/cxxopts.hpp"
|
#include "third_party/cxxopts/include/cxxopts.hpp"
|
||||||
#include "third_party/fmt/include/fmt/format.h"
|
#include "third_party/fmt/include/fmt/format.h"
|
||||||
|
#include "third_party/tomlplusplus/include/toml++/toml.hpp"
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
namespace cvar {
|
namespace cvar {
|
||||||
|
|
||||||
namespace toml {
|
namespace toml_internal {
|
||||||
std::string EscapeString(const std::string_view str);
|
std::string EscapeString(const std::string_view str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,8 +48,8 @@ class IConfigVar : virtual public ICommandVar {
|
||||||
virtual const std::string& category() const = 0;
|
virtual const std::string& category() const = 0;
|
||||||
virtual bool is_transient() const = 0;
|
virtual bool is_transient() const = 0;
|
||||||
virtual std::string config_value() const = 0;
|
virtual std::string config_value() const = 0;
|
||||||
virtual void LoadConfigValue(std::shared_ptr<cpptoml::base> result) = 0;
|
virtual void LoadConfigValue(const toml::node* result) = 0;
|
||||||
virtual void LoadGameConfigValue(std::shared_ptr<cpptoml::base> result) = 0;
|
virtual void LoadGameConfigValue(const toml::node* result) = 0;
|
||||||
virtual void ResetConfigValueToDefault() = 0;
|
virtual void ResetConfigValueToDefault() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,8 +87,8 @@ class ConfigVar : public CommandVar<T>, virtual public IConfigVar {
|
||||||
const std::string& category() const override;
|
const std::string& category() const override;
|
||||||
bool is_transient() const override;
|
bool is_transient() const override;
|
||||||
void AddToLaunchOptions(cxxopts::Options* options) override;
|
void AddToLaunchOptions(cxxopts::Options* options) override;
|
||||||
void LoadConfigValue(std::shared_ptr<cpptoml::base> result) override;
|
void LoadConfigValue(const toml::node* result) override;
|
||||||
void LoadGameConfigValue(std::shared_ptr<cpptoml::base> result) override;
|
void LoadGameConfigValue(const toml::node* result) override;
|
||||||
void SetConfigValue(T val);
|
void SetConfigValue(T val);
|
||||||
void SetGameConfigValue(T val);
|
void SetGameConfigValue(T val);
|
||||||
// Changes the actual value used to the one specified, and also makes it the
|
// Changes the actual value used to the one specified, and also makes it the
|
||||||
|
@ -146,24 +146,24 @@ inline void CommandVar<std::filesystem::path>::LoadFromLaunchOptions(
|
||||||
SetCommandLineValue(value);
|
SetCommandLineValue(value);
|
||||||
}
|
}
|
||||||
template <class T>
|
template <class T>
|
||||||
void ConfigVar<T>::LoadConfigValue(std::shared_ptr<cpptoml::base> result) {
|
void ConfigVar<T>::LoadConfigValue(const toml::node* result) {
|
||||||
SetConfigValue(*cpptoml::get_impl<T>(result));
|
SetConfigValue(result->value<T>().value());
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
inline void ConfigVar<std::filesystem::path>::LoadConfigValue(
|
inline void ConfigVar<std::filesystem::path>::LoadConfigValue(
|
||||||
std::shared_ptr<cpptoml::base> result) {
|
const toml::node* result) {
|
||||||
SetConfigValue(
|
SetConfigValue(
|
||||||
xe::utf8::fix_path_separators(*cpptoml::get_impl<std::string>(result)));
|
xe::utf8::fix_path_separators(result->as_string()->value_or("")));
|
||||||
}
|
}
|
||||||
template <class T>
|
template <class T>
|
||||||
void ConfigVar<T>::LoadGameConfigValue(std::shared_ptr<cpptoml::base> result) {
|
void ConfigVar<T>::LoadGameConfigValue(const toml::node* result) {
|
||||||
SetGameConfigValue(*cpptoml::get_impl<T>(result));
|
SetGameConfigValue(result->value<T>().value());
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
inline void ConfigVar<std::filesystem::path>::LoadGameConfigValue(
|
inline void ConfigVar<std::filesystem::path>::LoadGameConfigValue(
|
||||||
std::shared_ptr<cpptoml::base> result) {
|
const toml::node* result) {
|
||||||
SetGameConfigValue(
|
SetGameConfigValue(
|
||||||
xe::utf8::fix_path_separators(*cpptoml::get_impl<std::string>(result)));
|
xe::utf8::fix_path_separators(result->as_string()->value_or("")));
|
||||||
}
|
}
|
||||||
template <class T>
|
template <class T>
|
||||||
CommandVar<T>::CommandVar(const char* name, T* default_value,
|
CommandVar<T>::CommandVar(const char* name, T* default_value,
|
||||||
|
@ -216,12 +216,12 @@ inline std::string CommandVar<bool>::ToString(bool val) {
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
inline std::string CommandVar<std::string>::ToString(std::string val) {
|
inline std::string CommandVar<std::string>::ToString(std::string val) {
|
||||||
return toml::EscapeString(val);
|
return toml_internal::EscapeString(val);
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
inline std::string CommandVar<std::filesystem::path>::ToString(
|
inline std::string CommandVar<std::filesystem::path>::ToString(
|
||||||
std::filesystem::path val) {
|
std::filesystem::path val) {
|
||||||
return toml::EscapeString(
|
return toml_internal::EscapeString(
|
||||||
xe::utf8::fix_path_separators(xe::path_to_utf8(val), '/'));
|
xe::utf8::fix_path_separators(xe::path_to_utf8(val), '/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,23 +17,8 @@
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/base/string_buffer.h"
|
#include "xenia/base/string_buffer.h"
|
||||||
|
|
||||||
std::shared_ptr<cpptoml::table> ParseFile(
|
toml::parse_result ParseFile(const std::filesystem::path& filename) {
|
||||||
const std::filesystem::path& filename) {
|
return toml::parse_file(xe::path_to_utf8(filename));
|
||||||
std::ifstream file(filename);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
throw cpptoml::parse_exception(xe::path_to_utf8(filename) +
|
|
||||||
" could not be opened for parsing");
|
|
||||||
}
|
|
||||||
// since cpptoml can't parse files with a UTF-8 BOM we need to skip them
|
|
||||||
char bom[3];
|
|
||||||
file.read(bom, sizeof(bom));
|
|
||||||
if (file.fail() || bom[0] != '\xEF' || bom[1] != '\xBB' || bom[2] != '\xBF') {
|
|
||||||
file.clear();
|
|
||||||
file.seekg(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpptoml::parser p(file);
|
|
||||||
return p.parse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CmdVar(config, "", "Specifies the target config to load.");
|
CmdVar(config, "", "Specifies the target config to load.");
|
||||||
|
@ -57,14 +42,13 @@ bool sortCvar(cvar::IConfigVar* a, cvar::IConfigVar* b) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<cpptoml::table> ParseConfig(
|
toml::parse_result ParseConfig(const std::filesystem::path& config_path) {
|
||||||
const std::filesystem::path& config_path) {
|
|
||||||
try {
|
try {
|
||||||
return ParseFile(config_path);
|
return ParseFile(config_path);
|
||||||
} catch (cpptoml::parse_exception& e) {
|
} catch (toml::parse_error& e) {
|
||||||
xe::FatalError(fmt::format("Failed to parse config file '{}':\n\n{}",
|
xe::FatalError(fmt::format("Failed to parse config file '{}':\n\n{}",
|
||||||
xe::path_to_utf8(config_path), e.what()));
|
xe::path_to_utf8(config_path), e.what()));
|
||||||
return nullptr;
|
return toml::parse_result();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,9 +91,11 @@ void ReadConfig(const std::filesystem::path& file_path,
|
||||||
if (!cvar::ConfigVars) {
|
if (!cvar::ConfigVars) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto config = ParseConfig(file_path);
|
const auto config = ParseConfig(file_path);
|
||||||
|
|
||||||
PrintConfigToLog(file_path);
|
PrintConfigToLog(file_path);
|
||||||
|
|
||||||
// Loading an actual global config file that exists - if there's no
|
// Loading an actual global config file that exists - if there's no
|
||||||
// defaults_date in it, it's very old (before updating was added at all, thus
|
// defaults_date in it, it's very old (before updating was added at all, thus
|
||||||
// all defaults need to be updated).
|
// all defaults need to be updated).
|
||||||
|
@ -119,9 +105,12 @@ void ReadConfig(const std::filesystem::path& file_path,
|
||||||
defaults_date_cvar->SetConfigValue(0);
|
defaults_date_cvar->SetConfigValue(0);
|
||||||
for (auto& it : *cvar::ConfigVars) {
|
for (auto& it : *cvar::ConfigVars) {
|
||||||
auto config_var = static_cast<cvar::IConfigVar*>(it.second);
|
auto config_var = static_cast<cvar::IConfigVar*>(it.second);
|
||||||
auto config_key = config_var->category() + "." + config_var->name();
|
toml::path config_key =
|
||||||
if (config->contains_qualified(config_key)) {
|
toml::path(config_var->category() + "." + config_var->name());
|
||||||
config_var->LoadConfigValue(config->get_qualified(config_key));
|
|
||||||
|
const auto config_key_node = config.at_path(config_key);
|
||||||
|
if (config_key_node) {
|
||||||
|
config_var->LoadConfigValue(config_key_node.node());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint32_t config_defaults_date = defaults_date_cvar->GetTypedConfigValue();
|
uint32_t config_defaults_date = defaults_date_cvar->GetTypedConfigValue();
|
||||||
|
@ -139,9 +128,12 @@ void ReadGameConfig(const std::filesystem::path& file_path) {
|
||||||
const auto config = ParseConfig(file_path);
|
const auto config = ParseConfig(file_path);
|
||||||
for (auto& it : *cvar::ConfigVars) {
|
for (auto& it : *cvar::ConfigVars) {
|
||||||
auto config_var = static_cast<cvar::IConfigVar*>(it.second);
|
auto config_var = static_cast<cvar::IConfigVar*>(it.second);
|
||||||
auto config_key = config_var->category() + "." + config_var->name();
|
toml::path config_key =
|
||||||
if (config->contains_qualified(config_key)) {
|
toml::path(config_var->category() + "." + config_var->name());
|
||||||
config_var->LoadGameConfigValue(config->get_qualified(config_key));
|
|
||||||
|
const auto config_key_node = config.at_path(config_key);
|
||||||
|
if (config_key_node) {
|
||||||
|
config_var->LoadConfigValue(config_key_node.node());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XELOGI("Loaded game config: {}", xe::path_to_utf8(file_path));
|
XELOGI("Loaded game config: {}", xe::path_to_utf8(file_path));
|
||||||
|
|
|
@ -11,10 +11,9 @@
|
||||||
#define XENIA_CONFIG_H_
|
#define XENIA_CONFIG_H_
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "third_party/cpptoml/include/cpptoml.h"
|
#include "third_party/tomlplusplus/toml.hpp"
|
||||||
|
|
||||||
std::shared_ptr<cpptoml::table> ParseFile(
|
toml::parse_result ParseFile(const std::filesystem::path& filename);
|
||||||
const std::filesystem::path& filename);
|
|
||||||
|
|
||||||
namespace config {
|
namespace config {
|
||||||
void SetupConfig(const std::filesystem::path& config_folder);
|
void SetupConfig(const std::filesystem::path& config_folder);
|
||||||
|
|
|
@ -57,9 +57,10 @@ void PatchDB::LoadPatches() {
|
||||||
XELOGI("PatchDB: Loaded patches for {} titles", loaded_patches_.size());
|
XELOGI("PatchDB: Loaded patches for {} titles", loaded_patches_.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchFileEntry PatchDB::ReadPatchFile(const std::filesystem::path& file_path) {
|
PatchFileEntry PatchDB::ReadPatchFile(
|
||||||
|
const std::filesystem::path& file_path) const {
|
||||||
PatchFileEntry patch_file;
|
PatchFileEntry patch_file;
|
||||||
std::shared_ptr<cpptoml::table> patch_toml_fields;
|
toml::parse_result patch_toml_fields;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
patch_toml_fields = ParseFile(file_path);
|
patch_toml_fields = ParseFile(file_path);
|
||||||
|
@ -69,108 +70,118 @@ PatchFileEntry PatchDB::ReadPatchFile(const std::filesystem::path& file_path) {
|
||||||
return patch_file;
|
return patch_file;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto title_name = patch_toml_fields->get_as<std::string>("title_name");
|
auto title_name = patch_toml_fields.get_as<std::string>("title_name");
|
||||||
auto title_id = patch_toml_fields->get_as<std::string>("title_id");
|
auto title_id = patch_toml_fields.get_as<std::string>("title_id");
|
||||||
|
auto hashes_node = patch_toml_fields.get("hash");
|
||||||
|
|
||||||
patch_file.title_id = strtoul((*title_id).c_str(), NULL, 16);
|
patch_file.title_id = strtoul(title_id->get().c_str(), NULL, 16);
|
||||||
patch_file.title_name = *title_name;
|
patch_file.title_name = title_name->get();
|
||||||
ReadHashes(patch_file, patch_toml_fields);
|
ReadHashes(patch_file, hashes_node);
|
||||||
|
|
||||||
auto patch_table = patch_toml_fields->get_table_array("patch");
|
auto patch_array = patch_toml_fields.get("patch");
|
||||||
|
if (!patch_array->is_array()) {
|
||||||
|
return patch_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& patch_entry : *patch_array->as_array()) {
|
||||||
|
if (!patch_entry.is_table()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto patch_table_entry : *patch_table) {
|
|
||||||
PatchInfoEntry patch = PatchInfoEntry();
|
PatchInfoEntry patch = PatchInfoEntry();
|
||||||
auto patch_name = *patch_table_entry->get_as<std::string>("name");
|
ReadPatchHeader(patch, patch_entry.as_table());
|
||||||
auto patch_desc = *patch_table_entry->get_as<std::string>("desc");
|
|
||||||
auto patch_author = *patch_table_entry->get_as<std::string>("author");
|
|
||||||
auto is_enabled = *patch_table_entry->get_as<bool>("is_enabled");
|
|
||||||
|
|
||||||
patch.id = 0; // Todo(Gliniak): Implement id for future GUI stuff
|
|
||||||
patch.patch_name = patch_name;
|
|
||||||
patch.patch_desc = patch_desc;
|
|
||||||
patch.patch_author = patch_author;
|
|
||||||
patch.is_enabled = is_enabled;
|
|
||||||
|
|
||||||
// Iterate through all available data sizes
|
|
||||||
for (const auto& patch_data_type : patch_data_types_size_) {
|
|
||||||
bool success =
|
|
||||||
ReadPatchData(patch.patch_data, patch_data_type, patch_table_entry);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
XELOGE("PatchDB: Cannot read patch {}", patch_name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
patch_file.patch_info.push_back(patch);
|
patch_file.patch_info.push_back(patch);
|
||||||
}
|
}
|
||||||
return patch_file;
|
return patch_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PatchDB::ReadPatchData(
|
bool PatchDB::ReadPatchData(std::vector<PatchDataEntry>& patch_data,
|
||||||
std::vector<PatchDataEntry>& patch_data,
|
|
||||||
const std::pair<std::string, PatchData> data_type,
|
const std::pair<std::string, PatchData> data_type,
|
||||||
const std::shared_ptr<cpptoml::table>& patch_table) {
|
const toml::table* patch_fields) const {
|
||||||
auto patch_data_tarr = patch_table->get_table_array(data_type.first);
|
auto patch_data_fields = patch_fields->get_as<toml::array>(data_type.first);
|
||||||
if (!patch_data_tarr) {
|
if (!patch_data_fields) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& patch_data_table : *patch_data_tarr) {
|
for (const auto& patch_data_table : *patch_data_fields) {
|
||||||
uint32_t address = *patch_data_table->get_as<std::uint32_t>("address");
|
if (!patch_data_table.is_table()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto table = patch_data_table.as_table();
|
||||||
|
auto address =
|
||||||
|
static_cast<uint32_t>(table->get_as<int64_t>("address")->get());
|
||||||
size_t alloc_size = (size_t)data_type.second.size;
|
size_t alloc_size = (size_t)data_type.second.size;
|
||||||
|
|
||||||
switch (data_type.second.type) {
|
switch (data_type.second.type) {
|
||||||
case PatchDataType::kBE8: {
|
case PatchDataType::kBE8: {
|
||||||
uint16_t value = *patch_data_table->get_as<uint8_t>("value");
|
uint16_t value =
|
||||||
|
static_cast<uint16_t>(table->get_as<int64_t>("value")->get());
|
||||||
patch_data.push_back({address, PatchDataValue(alloc_size, value)});
|
patch_data.push_back({address, PatchDataValue(alloc_size, value)});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchDataType::kBE16: {
|
case PatchDataType::kBE16: {
|
||||||
uint16_t value = *patch_data_table->get_as<uint16_t>("value");
|
uint16_t value =
|
||||||
|
static_cast<uint16_t>(table->get_as<int64_t>("value")->get());
|
||||||
patch_data.push_back(
|
patch_data.push_back(
|
||||||
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchDataType::kBE32: {
|
case PatchDataType::kBE32: {
|
||||||
uint32_t value = *patch_data_table->get_as<uint32_t>("value");
|
uint32_t value =
|
||||||
|
static_cast<uint32_t>(table->get_as<int64_t>("value")->get());
|
||||||
patch_data.push_back(
|
patch_data.push_back(
|
||||||
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchDataType::kBE64: {
|
case PatchDataType::kBE64: {
|
||||||
uint64_t value = *patch_data_table->get_as<uint64_t>("value");
|
uint64_t value = table->get_as<int64_t>("value")->get();
|
||||||
patch_data.push_back(
|
patch_data.push_back(
|
||||||
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchDataType::kF64: {
|
case PatchDataType::kF64: {
|
||||||
double val = *patch_data_table->get_as<double>("value");
|
const auto value_field = table->get("value");
|
||||||
uint64_t value = *reinterpret_cast<uint64_t*>(&val);
|
double value = 0.0;
|
||||||
|
if (value_field->is_floating_point()) {
|
||||||
|
value = value_field->as_floating_point()->get();
|
||||||
|
}
|
||||||
|
if (value_field->is_integer()) {
|
||||||
|
value = static_cast<double>(value_field->as_integer()->get());
|
||||||
|
}
|
||||||
|
|
||||||
patch_data.push_back(
|
patch_data.push_back(
|
||||||
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchDataType::kF32: {
|
case PatchDataType::kF32: {
|
||||||
float value = float(*patch_data_table->get_as<double>("value"));
|
const auto value_field = table->get("value");
|
||||||
|
float value = 0.0f;
|
||||||
|
if (value_field->is_floating_point()) {
|
||||||
|
value = static_cast<float>(value_field->as_floating_point()->get());
|
||||||
|
}
|
||||||
|
if (value_field->is_integer()) {
|
||||||
|
value = static_cast<float>(value_field->as_integer()->get());
|
||||||
|
}
|
||||||
|
|
||||||
patch_data.push_back(
|
patch_data.push_back(
|
||||||
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
{address, PatchDataValue(alloc_size, xe::byte_swap(value))});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchDataType::kString: {
|
case PatchDataType::kString: {
|
||||||
std::string value = *patch_data_table->get_as<std::string>("value");
|
std::string value = table->get_as<std::string>("value")->get();
|
||||||
patch_data.push_back({address, PatchDataValue(value)});
|
patch_data.push_back({address, PatchDataValue(value)});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchDataType::kU16String: {
|
case PatchDataType::kU16String: {
|
||||||
std::u16string value =
|
std::u16string value =
|
||||||
xe::to_utf16(*patch_data_table->get_as<std::string>("value"));
|
xe::to_utf16(table->get_as<std::string>("value")->get());
|
||||||
patch_data.push_back({address, PatchDataValue(value)});
|
patch_data.push_back({address, PatchDataValue(value)});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchDataType::kByteArray: {
|
case PatchDataType::kByteArray: {
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
const std::string value =
|
const std::string value = table->get_as<std::string>("value")->get();
|
||||||
*patch_data_table->get_as<std::string>("value");
|
|
||||||
|
|
||||||
bool success = string_util::hex_string_to_array(data, value);
|
bool success = string_util::hex_string_to_array(data, value);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
@ -208,16 +219,67 @@ std::vector<PatchFileEntry> PatchDB::GetTitlePatches(
|
||||||
}
|
}
|
||||||
|
|
||||||
void PatchDB::ReadHashes(PatchFileEntry& patch_entry,
|
void PatchDB::ReadHashes(PatchFileEntry& patch_entry,
|
||||||
std::shared_ptr<cpptoml::table> patch_toml_fields) {
|
const toml::node* hashes_node) const {
|
||||||
auto title_hashes = patch_toml_fields->get_array_of<std::string>("hash");
|
auto add_hash = [&patch_entry](const toml::node* hash_node) {
|
||||||
|
if (!hash_node->is_string()) {
|
||||||
for (const auto& hash : *title_hashes) {
|
return;
|
||||||
patch_entry.hashes.push_back(strtoull(hash.c_str(), NULL, 16));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto single_hash = patch_toml_fields->get_as<std::string>("hash");
|
const auto string_hash = hash_node->as_string()->get();
|
||||||
if (single_hash) {
|
if (string_hash.empty()) {
|
||||||
patch_entry.hashes.push_back(strtoull((*single_hash).c_str(), NULL, 16));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
patch_entry.hashes.push_back(
|
||||||
|
xe::string_util::from_string<uint64_t>(string_hash, true));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hashes_node->is_value()) {
|
||||||
|
add_hash(hashes_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashes_node->is_array()) {
|
||||||
|
for (const auto& hash_entry : *hashes_node->as_array()) {
|
||||||
|
add_hash(&hash_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatchDB::ReadPatchHeader(PatchInfoEntry& patch_info,
|
||||||
|
const toml::table* patch_fields) const {
|
||||||
|
std::string patch_name = {};
|
||||||
|
std::string patch_desc = {};
|
||||||
|
std::string patch_author = {};
|
||||||
|
bool is_enabled = false;
|
||||||
|
|
||||||
|
if (patch_fields->contains("name")) {
|
||||||
|
patch_name = patch_fields->get_as<std::string>("name")->get();
|
||||||
|
}
|
||||||
|
if (patch_fields->contains("desc")) {
|
||||||
|
patch_desc = patch_fields->get_as<std::string>("desc")->get();
|
||||||
|
}
|
||||||
|
if (patch_fields->contains("author")) {
|
||||||
|
patch_author = patch_fields->get_as<std::string>("author")->get();
|
||||||
|
}
|
||||||
|
if (patch_fields->contains("is_enabled")) {
|
||||||
|
is_enabled = patch_fields->get_as<bool>("is_enabled")->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
patch_info.id = 0; // Todo(Gliniak): Implement id for future GUI stuff
|
||||||
|
patch_info.patch_name = patch_name;
|
||||||
|
patch_info.patch_desc = patch_desc;
|
||||||
|
patch_info.patch_author = patch_author;
|
||||||
|
patch_info.is_enabled = is_enabled;
|
||||||
|
|
||||||
|
// Iterate through all available data sizes
|
||||||
|
for (const auto& patch_data_type : patch_data_types_size_) {
|
||||||
|
bool success =
|
||||||
|
ReadPatchData(patch_info.patch_data, patch_data_type, patch_fields);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
XELOGE("PatchDB: Cannot read patch {}", patch_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
#include "third_party/cpptoml/include/cpptoml.h"
|
#include "third_party/tomlplusplus/toml.hpp"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace patcher {
|
namespace patcher {
|
||||||
|
@ -96,10 +96,7 @@ class PatchDB {
|
||||||
|
|
||||||
void LoadPatches();
|
void LoadPatches();
|
||||||
|
|
||||||
PatchFileEntry ReadPatchFile(const std::filesystem::path& file_path);
|
PatchFileEntry ReadPatchFile(const std::filesystem::path& file_path) const;
|
||||||
bool ReadPatchData(std::vector<PatchDataEntry>& patch_data,
|
|
||||||
const std::pair<std::string, PatchData> data_type,
|
|
||||||
const std::shared_ptr<cpptoml::table>& patch_table);
|
|
||||||
|
|
||||||
std::vector<PatchFileEntry> GetTitlePatches(
|
std::vector<PatchFileEntry> GetTitlePatches(
|
||||||
const uint32_t title_id, const std::optional<uint64_t> hash);
|
const uint32_t title_id, const std::optional<uint64_t> hash);
|
||||||
|
@ -107,7 +104,12 @@ class PatchDB {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ReadHashes(PatchFileEntry& patch_entry,
|
void ReadHashes(PatchFileEntry& patch_entry,
|
||||||
std::shared_ptr<cpptoml::table> patch_toml_fields);
|
const toml::node* patch_toml_fields) const;
|
||||||
|
void ReadPatchHeader(PatchInfoEntry& patch_info,
|
||||||
|
const toml::table* patch_fields) const;
|
||||||
|
bool ReadPatchData(std::vector<PatchDataEntry>& patch_data,
|
||||||
|
const std::pair<std::string, PatchData> data_type,
|
||||||
|
const toml::table* patch_fields) const;
|
||||||
|
|
||||||
inline static const std::regex patch_filename_regex_ =
|
inline static const std::regex patch_filename_regex_ =
|
||||||
std::regex("^[A-Fa-f0-9]{8}.*\\.patch\\.toml$");
|
std::regex("^[A-Fa-f0-9]{8}.*\\.patch\\.toml$");
|
||||||
|
|
|
@ -65,7 +65,7 @@ void PluginLoader::LoadTitleConfig(const uint32_t title_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<cpptoml::table> plugins_config;
|
toml::parse_result plugins_config;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
plugins_config = ParseFile(title_plugins_config);
|
plugins_config = ParseFile(title_plugins_config);
|
||||||
|
@ -76,64 +76,79 @@ void PluginLoader::LoadTitleConfig(const uint32_t title_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string title_name =
|
const std::string title_name =
|
||||||
*plugins_config->get_as<std::string>("title_name");
|
plugins_config.get_as<std::string>("title_name")->get();
|
||||||
|
|
||||||
const std::string patch_title_id =
|
const std::string plugin_title_id =
|
||||||
*plugins_config->get_as<std::string>("title_id");
|
plugins_config.get_as<std::string>("title_id")->get();
|
||||||
|
|
||||||
const auto plugin_tabels = plugins_config->get_table_array("plugin");
|
if (!plugins_config.contains("plugin")) {
|
||||||
|
|
||||||
if (!plugin_tabels) {
|
|
||||||
XELOGE("Plugins: Cannot find [[plugin]] table in {}",
|
XELOGE("Plugins: Cannot find [[plugin]] table in {}",
|
||||||
path_to_utf8(title_plugins_config));
|
path_to_utf8(title_plugins_config));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& plugin : *plugin_tabels) {
|
const auto plugin_array = plugins_config.get_as<toml::array>("plugin");
|
||||||
|
if (!plugin_array) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& plugin_entry : *plugin_array) {
|
||||||
PluginInfoEntry entry;
|
PluginInfoEntry entry;
|
||||||
|
|
||||||
const std::string name = *plugin->get_as<std::string>("name");
|
if (!plugin_entry.is_table()) {
|
||||||
const std::string file = *plugin->get_as<std::string>("file");
|
return;
|
||||||
const std::string desc = *plugin->get_as<std::string>("desc");
|
}
|
||||||
const bool is_enabled = *plugin->get_as<bool>("is_enabled");
|
entry.title_id =
|
||||||
|
xe::string_util::from_string<uint32_t>(plugin_title_id, true);
|
||||||
|
|
||||||
if (!plugin->contains("hash")) {
|
if (plugin_entry.as_table()->contains("name")) {
|
||||||
XELOGE("Hash error! skipping plugin {} in: {}", name,
|
entry.name = plugin_entry.as_table()->get_as<std::string>("name")->get();
|
||||||
|
}
|
||||||
|
if (plugin_entry.as_table()->contains("file")) {
|
||||||
|
entry.file = xe::string_util::trim(
|
||||||
|
plugin_entry.as_table()->get_as<std::string>("file")->get());
|
||||||
|
}
|
||||||
|
if (plugin_entry.as_table()->contains("desc")) {
|
||||||
|
entry.desc = plugin_entry.as_table()->get_as<std::string>("desc")->get();
|
||||||
|
}
|
||||||
|
if (plugin_entry.as_table()->contains("is_enabled")) {
|
||||||
|
entry.is_enabled =
|
||||||
|
plugin_entry.as_table()->get_as<bool>("is_enabled")->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!plugin_entry.as_table()->contains("hash")) {
|
||||||
|
XELOGE("Hash error! skipping plugin {} in: {}", entry.name,
|
||||||
path_to_utf8(title_plugins_config));
|
path_to_utf8(title_plugins_config));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.hashes = GetHashes(plugin->get("hash"));
|
entry.hashes = GetHashes(plugin_entry.as_table()->get("hash"));
|
||||||
entry.name = name;
|
|
||||||
entry.file = xe::string_util::trim(file);
|
|
||||||
entry.desc = desc;
|
|
||||||
entry.title_id = title_id;
|
|
||||||
entry.is_enabled = is_enabled;
|
|
||||||
plugin_configs_.push_back(entry);
|
plugin_configs_.push_back(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint64_t> PluginLoader::GetHashes(
|
std::vector<uint64_t> PluginLoader::GetHashes(
|
||||||
const std::shared_ptr<cpptoml::base> toml_entry) {
|
const toml::node* toml_entry) const {
|
||||||
std::vector<uint64_t> hashes;
|
std::vector<uint64_t> hashes;
|
||||||
|
|
||||||
if (!toml_entry) {
|
if (!toml_entry) {
|
||||||
return hashes;
|
return hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toml_entry->is_array()) {
|
|
||||||
const auto arr = toml_entry->as_array();
|
|
||||||
|
|
||||||
for (cpptoml::array::const_iterator itr = arr->begin(); itr != arr->end();
|
|
||||||
++itr) {
|
|
||||||
const std::string hash_entry = itr->get()->as<std::string>()->get();
|
|
||||||
hashes.push_back(strtoull(hash_entry.c_str(), NULL, 16));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toml_entry->is_value()) {
|
if (toml_entry->is_value()) {
|
||||||
const std::string hash = toml_entry->as<std::string>()->get();
|
hashes.push_back(xe::string_util::from_string<uint64_t>(
|
||||||
hashes.push_back(strtoull(hash.c_str(), NULL, 16));
|
toml_entry->as_string()->get(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toml_entry->is_array()) {
|
||||||
|
for (const auto& hash_entry : *toml_entry->as_array()) {
|
||||||
|
if (hash_entry.as_string()->get().empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashes.push_back(xe::string_util::from_string<uint64_t>(
|
||||||
|
hash_entry.as_string()->get(), true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return hashes;
|
return hashes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#ifndef XENIA_PLUGIN_LOADER_H_
|
#ifndef XENIA_PLUGIN_LOADER_H_
|
||||||
#define XENIA_PLUGIN_LOADER_H_
|
#define XENIA_PLUGIN_LOADER_H_
|
||||||
|
|
||||||
|
#include "third_party/tomlplusplus/toml.hpp"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
|
||||||
|
@ -41,8 +42,7 @@ class PluginLoader {
|
||||||
void CreatePluginDevice(const uint32_t title_id);
|
void CreatePluginDevice(const uint32_t title_id);
|
||||||
void LoadTitlePlugin(const PluginInfoEntry& entry);
|
void LoadTitlePlugin(const PluginInfoEntry& entry);
|
||||||
|
|
||||||
std::vector<uint64_t> GetHashes(
|
std::vector<uint64_t> GetHashes(const toml::node* toml_entry) const;
|
||||||
const std::shared_ptr<cpptoml::base> toml_entry);
|
|
||||||
|
|
||||||
kernel::KernelState* kernel_state_;
|
kernel::KernelState* kernel_state_;
|
||||||
std::filesystem::path plugins_root_;
|
std::filesystem::path plugins_root_;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit fededad7169e538ca47e11a9ee9251bc361a9a65
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 30172438cee64926dc41fdd9c11fb3ba5b2ba9de
|
|
@ -1,5 +1,5 @@
|
||||||
group("third_party")
|
group("third_party")
|
||||||
project("cpptoml")
|
project("tomlplusplus")
|
||||||
uuid("1e86cc51-3f8b-476d-9249-3b200424846b")
|
uuid("1e86cc51-3f8b-476d-9249-3b200424846b")
|
||||||
if os.istarget("android") then
|
if os.istarget("android") then
|
||||||
-- ndk-build only supports StaticLib and SharedLib.
|
-- ndk-build only supports StaticLib and SharedLib.
|
||||||
|
@ -9,5 +9,5 @@ project("cpptoml")
|
||||||
end
|
end
|
||||||
language("C++")
|
language("C++")
|
||||||
files({
|
files({
|
||||||
"cpptoml/include/cpptoml.h",
|
"tomlplusplus/include/toml++/toml.hpp"
|
||||||
})
|
})
|
Loading…
Reference in New Issue