new blank + move ModInfo definitions to their own file + mod settings

are now saved
This commit is contained in:
HJfod 2022-09-17 15:42:33 +03:00
parent 71bd0fec83
commit c29443c0d7
10 changed files with 394 additions and 294 deletions

View file

@ -278,8 +278,8 @@ namespace geode {
Result<> loadPlatformBinary();
Result<> unloadPlatformBinary();
Result<> saveDataStore();
Result<> loadDataStore();
Result<> saveSettings();
Result<> loadSettings();
void postDSUpdate();
@ -323,13 +323,22 @@ namespace geode {
bool hasSettings() const;
decltype(ModInfo::m_settings) getSettings() const;
std::shared_ptr<Setting> getSetting(std::string const& key) const;
template<class T>
T getSettingValue(std::string const& key) const {
if (m_info.m_settings.count(key)) {
return geode::getSettingValue<T>(m_info.m_settings.at(key));
return geode::getBuiltInSettingValue<T>(m_info.m_settings.at(key));
}
return T();
}
template<class T>
bool setSettingValue(std::string const& key, T const& value) {
if (m_info.m_settings.count(key)) {
geode::setBuiltInSettingValue<T>(m_info.m_settings[key], value);
return true;
}
return false;
}
/**
* Get the mod container stored in the Interface

View file

@ -55,7 +55,7 @@ namespace geode {
};
namespace {
#define GEODE_PARSE_SETTING_IMPL(obj, func) \
#define GEODE_INT_PARSE_SETTING_IMPL(obj, func) \
if constexpr (requires(JsonMaybeObject& obj) {\
{res->func(obj)} -> std::same_as<Result<>>;\
}) {\
@ -63,7 +63,7 @@ namespace geode {
if (!r) return Err(r.error());\
}
#define GEODE_CONSTRAIN_SETTING_IMPL(func) \
#define GEODE_INT_CONSTRAIN_SETTING_IMPL(func) \
if constexpr (requires(ValueType& value) {\
this->func(value);\
}) {\
@ -91,17 +91,17 @@ namespace geode {
obj.needs("default").into(res->m_default);
obj.has("name").into(res->m_name);
obj.has("description").into(res->m_description);
GEODE_PARSE_SETTING_IMPL(obj, Class::parseMinMax);
GEODE_PARSE_SETTING_IMPL(obj, Class::parseOneOf);
GEODE_INT_PARSE_SETTING_IMPL(obj, Class::parseMinMax);
GEODE_INT_PARSE_SETTING_IMPL(obj, Class::parseOneOf);
res->setValue(res->m_default);
if (auto controls = obj.has("control").obj()) {
// every built-in setting type has a reset button
// by default
controls.has("can-reset").into(res->m_canResetToDefault);
GEODE_PARSE_SETTING_IMPL(controls, Class::parseArrows);
GEODE_PARSE_SETTING_IMPL(controls, Class::parseSlider);
GEODE_PARSE_SETTING_IMPL(controls, Class::parseInput);
GEODE_INT_PARSE_SETTING_IMPL(controls, Class::parseArrows);
GEODE_INT_PARSE_SETTING_IMPL(controls, Class::parseSlider);
GEODE_INT_PARSE_SETTING_IMPL(controls, Class::parseInput);
}
return Ok(res);
@ -128,17 +128,20 @@ namespace geode {
void setValue(ValueType const& value) {
m_value = value;
GEODE_CONSTRAIN_SETTING_IMPL(Class::constrainMinMax);
GEODE_CONSTRAIN_SETTING_IMPL(Class::constrainOneOf);
GEODE_INT_CONSTRAIN_SETTING_IMPL(Class::constrainMinMax);
GEODE_INT_CONSTRAIN_SETTING_IMPL(Class::constrainOneOf);
}
bool load(nlohmann::json const& json) override {
m_value = json["value"];
auto rawJson = json;
JsonChecker(rawJson)
.root("[setting value]")
.into(m_value);
return true;
}
bool save(nlohmann::json& json) const override {
json["value"] = m_value;
json = m_value;
return true;
}
@ -213,7 +216,7 @@ namespace geode {
}
};
#define GEODE_DECL_SETTING_CONTROL(Name, name, default, json) \
#define GEODE_INT_DECL_SETTING_CONTROL(Name, name, default, json) \
class IC##Name {\
protected:\
bool m_##name = default;\
@ -227,9 +230,9 @@ namespace geode {
}\
}
GEODE_DECL_SETTING_CONTROL(Arrows, hasArrows, true, "arrows");
GEODE_DECL_SETTING_CONTROL(Slider, hasSlider, true, "slider");
GEODE_DECL_SETTING_CONTROL(Input, hasInput, true, "input");
GEODE_INT_DECL_SETTING_CONTROL(Arrows, hasArrows, true, "arrows");
GEODE_INT_DECL_SETTING_CONTROL(Slider, hasSlider, true, "slider");
GEODE_INT_DECL_SETTING_CONTROL(Input, hasInput, true, "input");
}
class GEODE_DLL BoolSetting :
@ -269,31 +272,35 @@ namespace geode {
SettingNode* createNode(float width) override;
};
// these can't be member functions because C++ is single-pass >:(
#define GEODE_INT_BUILTIN_SETTING_IF(type, action, ...) \
if constexpr (__VA_ARGS__) {\
if (setting->getType() == SettingType::type) {\
return std::static_pointer_cast<type##Setting>(setting)->action;\
}\
}
template<class T>
T getSettingValue(const std::shared_ptr<Setting> setting) {
if constexpr (std::is_same_v<T, bool>) {
if (setting->getType() == SettingType::Bool) {
return std::static_pointer_cast<BoolSetting>(setting)->getValue();
}
}
else if constexpr (std::is_floating_point_v<T>) {
if (setting->getType() == SettingType::Float) {
return std::static_pointer_cast<FloatSetting>(setting)->getValue();
}
}
else if constexpr (std::is_integral_v<T>) {
if (setting->getType() == SettingType::Int) {
return std::static_pointer_cast<IntSetting>(setting)->getValue();
}
}
else if constexpr (std::is_same_v<T, std::string>) {
if (setting->getType() == SettingType::String) {
return std::static_pointer_cast<StringSetting>(setting)->getValue();
}
}
T getBuiltInSettingValue(const std::shared_ptr<Setting> setting) {
GEODE_INT_BUILTIN_SETTING_IF(Bool, getValue(), std::is_same_v<T, bool>)
else GEODE_INT_BUILTIN_SETTING_IF(Float, getValue(), std::is_floating_point_v<T>)
else GEODE_INT_BUILTIN_SETTING_IF(Int, getValue(), std::is_integral_v<T>)
else GEODE_INT_BUILTIN_SETTING_IF(String, getValue(), std::is_same_v<T, std::string>)
else {
static_assert(!std::is_same_v<T, T>, "todo: implement");
}
return T();
}
template<class T>
void setBuiltInSettingValue(const std::shared_ptr<Setting> setting, T const& value) {
GEODE_INT_BUILTIN_SETTING_IF(Bool, setValue(value), std::is_same_v<T, bool>)
else GEODE_INT_BUILTIN_SETTING_IF(Float, setValue(value), std::is_floating_point_v<T>)
else GEODE_INT_BUILTIN_SETTING_IF(Int, setValue(value), std::is_integral_v<T>)
else GEODE_INT_BUILTIN_SETTING_IF(String, setValue(value), std::is_same_v<T, std::string>)
else {
static_assert(!std::is_same_v<T, T>, "todo: implement");
}
}
}

View file

@ -58,13 +58,22 @@ namespace geode {
};
enum class BaseType {
// Circle buttons
Circle = 0,
// Like the buttons in the main menu
Cross = 1,
Account = 2,
// Like the friend request / account buttons
BevelledSquare = 2,
// Like the icon select button
IconSelect = 3,
GlobalThing = 4,
// Like the leaderboard buttons
BevelledSquare2 = 4,
// Like the buttons in the editor sidebar
Editor = 5,
// Like a list view tab
Tab = 6,
// Like the buttons in CreatorLayer
Category = 7,
};
/**

View file

@ -290,8 +290,8 @@ namespace geode {
}
JsonMaybeValue has(std::string const& key) {
if (this->isError()) return emptyValue();
this->addKnownKey(key);
if (this->isError()) return emptyValue();
if (!m_json.contains(key) || m_json[key].is_null()) {
return emptyValue();
}
@ -299,8 +299,8 @@ namespace geode {
}
JsonMaybeValue needs(std::string const& key) {
if (this->isError()) return emptyValue();
this->addKnownKey(key);
if (this->isError()) return emptyValue();
if (!m_json.contains(key)) {
this->setError(
m_hierarchy + " is missing required key \"" + key + "\""
@ -335,12 +335,8 @@ namespace geode {
return std::get<std::string>(m_result);
}
JsonMaybeObject root(std::string const& hierarchy) {
if (!m_json.is_object()) {
m_result = hierarchy + ": Root is not an object";
return JsonMaybeObject(*this, m_json, hierarchy, false);
}
return JsonMaybeObject(*this, m_json, hierarchy, true);
JsonMaybeValue root(std::string const& hierarchy) {
return JsonMaybeValue(*this, m_json, hierarchy, true);
}
};
}

Binary file not shown.

After

(image error) Size: 111 KiB

View file

@ -188,7 +188,8 @@ Result<> Loader::saveSettings() {
if (mod->isUninstalled()) continue;
auto value = nlohmann::json::object();
value["enabled"] = mod->m_enabled;
mod->saveDataStore();
mod->saveSettings();
json["mods"][id] = value;
}
json["succesfully-closed"] = true;

View file

@ -51,22 +51,82 @@ Mod::~Mod() {
this->unload();
}
Result<> Mod::loadDataStore() {
auto dsPath = this->m_saveDirPath / "ds.json";
Result<> Mod::loadSettings() {
// settings
// Check if settings exist
auto settPath = m_saveDirPath / "settings.json";
if (ghc::filesystem::exists(settPath)) {
auto settData = file_utils::readString(settPath);
if (!settData) return settData;
try {
// parse settings.json
auto data = nlohmann::json::parse(settData.value());
JsonChecker checker(data);
auto root = checker.root("[settings.json]");
for (auto& [key, value] : root.items()) {
// check if this is a known setting
if (auto sett = this->getSetting(key)) {
// load its value
if (!sett->load(value.json())) {
return Err(
"Unable to load value for setting \"" +
key + "\""
);
}
} else {
this->logInfo(
"Encountered unknown setting \"" + key + "\" while "
"loading settings",
Severity::Warning
);
}
}
} catch(std::exception& e) {
return Err(std::string("Unable to parse settings: ") + e.what());
}
}
// datastore
auto dsPath = m_saveDirPath / "ds.json";
if (!ghc::filesystem::exists(dsPath)) {
this->m_dataStore = this->m_info.m_defaultDataStore;
m_dataStore = m_info.m_defaultDataStore;
} else {
auto dsData = file_utils::readString(dsPath);
if (!dsData) return dsData;
this->m_dataStore = nlohmann::json::parse(dsData.value());
try {
m_dataStore = nlohmann::json::parse(dsData.value());
} catch(std::exception& e) {
return Err(std::string("Unable to parse datastore: ") + e.what());
}
}
return Ok<>();
return Ok();
}
Result<> Mod::saveDataStore() {
auto dsPath = this->m_saveDirPath / "ds.json";
return file_utils::writeString(dsPath, m_dataStore.dump(4));
Result<> Mod::saveSettings() {
// settings
auto settPath = m_saveDirPath / "settings.json";
auto json = nlohmann::json::object();
for (auto& [key, sett] : m_info.m_settings) {
if (!sett->save(json[key])) {
return Err("Unable to save setting \"" + key + "\"");
}
}
auto sw = file_utils::writeString(settPath, json.dump(4));
if (!sw) {
return sw;
}
// datastore
auto dsPath = m_saveDirPath / "ds.json";
auto dw = file_utils::writeString(dsPath, m_dataStore.dump(4));
if (!dw) {
return dw;
}
return Ok();
}
DataStore Mod::getDataStore() {
@ -497,239 +557,13 @@ decltype(ModInfo::m_settings) Mod::getSettings() const {
return m_info.m_settings;
}
std::shared_ptr<Setting> Mod::getSetting(std::string const& key) const {
if (m_info.m_settings.count(key)) {
return m_info.m_settings.at(key);
}
return nullptr;
}
std::string Mod::getLoadErrorInfo() const {
return m_loadErrorInfo;
}
static std::string sanitizeDetailsData(unsigned char* start, unsigned char* end) {
// delete CRLF
return string_utils::replace(std::string(start, end), "\r", "");
}
Result<ModInfo> ModInfo::createFromSchemaV010(nlohmann::json const& rawJson) {
ModInfo info;
auto json = rawJson;
#define PROPAGATE(err) \
{ auto err__ = err; if (!err__) return Err(err__.error()); }
JsonChecker checker(json);
auto root = checker.root("[mod.json]");
root.addKnownKey("geode");
root.addKnownKey("binary");
using nlohmann::detail::value_t;
root
.needs("id")
.validate(&Mod::validateID)
.into(info.m_id);
root
.needs("version")
.validate(&VersionInfo::validate)
.intoAs<std::string>(info.m_version);
root.needs("name").into(info.m_name);
root.needs("developer").into(info.m_developer);
root.has("description").into(info.m_description);
root.has("repository").into(info.m_repository);
root.has("datastore").intoRaw(info.m_defaultDataStore);
root.has("toggleable").into(info.m_supportsDisabling);
root.has("unloadable").into(info.m_supportsUnloading);
for (auto& dep : root.has("dependencies").iterate()) {
auto obj = dep.obj();
auto depobj = Dependency {};
obj
.needs("id")
.validate(&Mod::validateID)
.into(depobj.m_id);
obj
.needs("version")
.validate(&VersionInfo::validate)
.intoAs<std::string>(depobj.m_version);
obj
.has("required")
.into(depobj.m_required);
obj.checkUnknownKeys();
info.m_dependencies.push_back(depobj);
}
for (auto& [key, value] : root.has("settings").items()) {
auto sett = Setting::parse(key, value.json());
PROPAGATE(sett);
info.m_settings.insert({ key, sett.value() });
}
if (auto resources = root.has("resources").obj()) {
for (auto& [key, _] : resources.has("spritesheets").items()) {
info.m_spritesheets.push_back(info.m_id + "/" + key);
}
}
root.has("binary").asOneOf<value_t::string, value_t::object>();
bool autoEndBinaryName = true;
root.has("binary").is<value_t::string>().into(info.m_binaryName);
if (auto bin = root.has("binary").is<value_t::object>().obj()) {
bin.has("*").into(info.m_binaryName);
bin.has("auto").into(autoEndBinaryName);
#if defined(GEODE_IS_WINDOWS)
bin.has("windows").into(info.m_binaryName);
#elif defined(GEODE_IS_MACOS)
bin.has("macos").into(info.m_binaryName);
#elif defined(GEODE_IS_ANDROID)
bin.has("android").into(info.m_binaryName);
#elif defined(GEODE_IS_IOS)
bin.has("ios").into(info.m_binaryName);
#endif
}
if (
root.has("binary") &&
autoEndBinaryName &&
!string_utils::endsWith(info.m_binaryName, GEODE_PLATFORM_EXTENSION)
) {
info.m_binaryName += GEODE_PLATFORM_EXTENSION;
}
if (checker.isError()) {
return Err(checker.getError());
}
root.checkUnknownKeys();
return Ok(info);
}
Result<ModInfo> ModInfo::create(nlohmann::json const& json) {
// Check mod.json target version
auto schema = LOADER_VERSION;
if (json.contains("geode") && json["geode"].is_string()) {
auto ver = json["geode"];
if (VersionInfo::validate(ver)) {
schema = VersionInfo(ver);
} else {
return Err(
"[mod.json] has no target loader version "
"specified, or it is invalidally formatted (required: \"[v]X.X.X\")!"
);
}
} else {
return Err(
"[mod.json] has no target loader version "
"specified, or it is invalidally formatted (required: \"[v]X.X.X\")!"
);
}
if (schema < Loader::s_supportedVersionMin) {
return Err(
"[mod.json] is built for an older version (" +
schema.toString() + ") of Geode (current: " +
Loader::s_supportedVersionMin.toString() +
"). Please update the mod to the latest version, "
"and if the problem persists, contact the developer "
"to update it."
);
}
if (schema > Loader::s_supportedVersionMax) {
return Err(
"[mod.json] is built for a newer version (" +
schema.toString() + ") of Geode (current: " +
Loader::s_supportedVersionMax.toString() +
"). You need to update Geode in order to use "
"this mod."
);
}
// Handle mod.json data based on target
if (schema <= VersionInfo(0, 2, 0)) {
return ModInfo::createFromSchemaV010(json);
}
return Err(
"[mod.json] targets a version (" +
schema.toString() + ") that isn't "
"supported by this version (v" +
LOADER_VERSION_STR + ") of geode. "
"This is probably a bug; report it to "
"the Geode Development Team."
);
}
Result<ModInfo> ModInfo::createFromFile(ghc::filesystem::path const& path) {
try {
auto read = file_utils::readString(path);
if (!read) return Err(read.error());
auto res = ModInfo::create(nlohmann::json::parse(read.value()));
if (!res) return res;
auto info = res.value();
info.m_path = path;
return Ok(info);
} catch(std::exception const& e) {
return Err(e.what());
}
}
Result<ModInfo> ModInfo::createFromGeodeFile(ghc::filesystem::path const& path) {
ZipFile unzip(path.string());
if (!unzip.isLoaded()) {
return Err<>("\"" + path.string() + "\": Unable to unzip");
}
// Check if mod.json exists in zip
if (!unzip.fileExists("mod.json")) {
return Err<>("\"" + path.string() + "\" is missing mod.json");
}
// Read mod.json & parse if possible
unsigned long readSize = 0;
auto read = unzip.getFileData("mod.json", &readSize);
if (!read || !readSize) {
return Err("\"" + path.string() + "\": Unable to read mod.json");
}
nlohmann::json json;
try {
json = nlohmann::json::parse(std::string(read, read + readSize));
} catch(std::exception const& e) {
delete[] read;
return Err<>(e.what());
}
delete[] read;
if (!json.is_object()) {
return Err(
"\"" + path.string() + "/mod.json\" does not have an "
"object at root despite expected"
);
}
auto res = ModInfo::create(json);
if (!res) {
return Err("\"" + path.string() + "\" - " + res.error());
}
auto info = res.value();
info.m_path = path;
// unzip known MD files
using God = std::initializer_list<std::pair<std::string, std::string*>>;
for (auto [file, target] : God {
{ "about.md", &info.m_details },
{ "changelog.md", &info.m_changelog },
}) {
if (unzip.fileExists(file)) {
unsigned long readSize = 0;
auto fileData = unzip.getFileData(file, &readSize);
if (!fileData || !readSize) {
return Err("Unable to read \"" + path.string() + "\"/" + file);
} else {
*target = sanitizeDetailsData(fileData, fileData + readSize);
}
}
}
return Ok(info);
}

240
loader/src/load/ModInfo.cpp Normal file
View file

@ -0,0 +1,240 @@
#include <about.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/utils/string.hpp>
#include <Geode/utils/file.hpp>
USE_GEODE_NAMESPACE();
static std::string sanitizeDetailsData(unsigned char* start, unsigned char* end) {
// delete CRLF
return string_utils::replace(std::string(start, end), "\r", "");
}
Result<ModInfo> ModInfo::createFromSchemaV010(nlohmann::json const& rawJson) {
ModInfo info;
auto json = rawJson;
#define PROPAGATE(err) \
{ auto err__ = err; if (!err__) return Err(err__.error()); }
JsonChecker checker(json);
auto root = checker.root("[mod.json]").obj();
root.addKnownKey("geode");
root.addKnownKey("binary");
using nlohmann::detail::value_t;
root
.needs("id")
.validate(&Mod::validateID)
.into(info.m_id);
root
.needs("version")
.validate(&VersionInfo::validate)
.intoAs<std::string>(info.m_version);
root.needs("name").into(info.m_name);
root.needs("developer").into(info.m_developer);
root.has("description").into(info.m_description);
root.has("repository").into(info.m_repository);
root.has("datastore").intoRaw(info.m_defaultDataStore);
root.has("toggleable").into(info.m_supportsDisabling);
root.has("unloadable").into(info.m_supportsUnloading);
for (auto& dep : root.has("dependencies").iterate()) {
auto obj = dep.obj();
auto depobj = Dependency {};
obj
.needs("id")
.validate(&Mod::validateID)
.into(depobj.m_id);
obj
.needs("version")
.validate(&VersionInfo::validate)
.intoAs<std::string>(depobj.m_version);
obj
.has("required")
.into(depobj.m_required);
obj.checkUnknownKeys();
info.m_dependencies.push_back(depobj);
}
for (auto& [key, value] : root.has("settings").items()) {
auto sett = Setting::parse(key, value.json());
PROPAGATE(sett);
info.m_settings.insert({ key, sett.value() });
}
if (auto resources = root.has("resources").obj()) {
for (auto& [key, _] : resources.has("spritesheets").items()) {
info.m_spritesheets.push_back(info.m_id + "/" + key);
}
}
root.has("binary").asOneOf<value_t::string, value_t::object>();
bool autoEndBinaryName = true;
root.has("binary").is<value_t::string>().into(info.m_binaryName);
if (auto bin = root.has("binary").is<value_t::object>().obj()) {
bin.has("*").into(info.m_binaryName);
bin.has("auto").into(autoEndBinaryName);
#if defined(GEODE_IS_WINDOWS)
bin.has("windows").into(info.m_binaryName);
#elif defined(GEODE_IS_MACOS)
bin.has("macos").into(info.m_binaryName);
#elif defined(GEODE_IS_ANDROID)
bin.has("android").into(info.m_binaryName);
#elif defined(GEODE_IS_IOS)
bin.has("ios").into(info.m_binaryName);
#endif
}
if (
root.has("binary") &&
autoEndBinaryName &&
!string_utils::endsWith(info.m_binaryName, GEODE_PLATFORM_EXTENSION)
) {
info.m_binaryName += GEODE_PLATFORM_EXTENSION;
}
if (checker.isError()) {
return Err(checker.getError());
}
root.checkUnknownKeys();
return Ok(info);
}
Result<ModInfo> ModInfo::create(nlohmann::json const& json) {
// Check mod.json target version
auto schema = LOADER_VERSION;
if (json.contains("geode") && json["geode"].is_string()) {
auto ver = json["geode"];
if (VersionInfo::validate(ver)) {
schema = VersionInfo(ver);
} else {
return Err(
"[mod.json] has no target loader version "
"specified, or it is invalidally formatted (required: \"[v]X.X.X\")!"
);
}
} else {
return Err(
"[mod.json] has no target loader version "
"specified, or it is invalidally formatted (required: \"[v]X.X.X\")!"
);
}
if (schema < Loader::s_supportedVersionMin) {
return Err(
"[mod.json] is built for an older version (" +
schema.toString() + ") of Geode (current: " +
Loader::s_supportedVersionMin.toString() +
"). Please update the mod to the latest version, "
"and if the problem persists, contact the developer "
"to update it."
);
}
if (schema > Loader::s_supportedVersionMax) {
return Err(
"[mod.json] is built for a newer version (" +
schema.toString() + ") of Geode (current: " +
Loader::s_supportedVersionMax.toString() +
"). You need to update Geode in order to use "
"this mod."
);
}
// Handle mod.json data based on target
if (schema <= VersionInfo(0, 2, 0)) {
return ModInfo::createFromSchemaV010(json);
}
return Err(
"[mod.json] targets a version (" +
schema.toString() + ") that isn't "
"supported by this version (v" +
LOADER_VERSION_STR + ") of geode. "
"This is probably a bug; report it to "
"the Geode Development Team."
);
}
Result<ModInfo> ModInfo::createFromFile(ghc::filesystem::path const& path) {
try {
auto read = file_utils::readString(path);
if (!read) return Err(read.error());
auto res = ModInfo::create(nlohmann::json::parse(read.value()));
if (!res) return res;
auto info = res.value();
info.m_path = path;
return Ok(info);
} catch(std::exception const& e) {
return Err(e.what());
}
}
Result<ModInfo> ModInfo::createFromGeodeFile(ghc::filesystem::path const& path) {
ZipFile unzip(path.string());
if (!unzip.isLoaded()) {
return Err<>("\"" + path.string() + "\": Unable to unzip");
}
// Check if mod.json exists in zip
if (!unzip.fileExists("mod.json")) {
return Err<>("\"" + path.string() + "\" is missing mod.json");
}
// Read mod.json & parse if possible
unsigned long readSize = 0;
auto read = unzip.getFileData("mod.json", &readSize);
if (!read || !readSize) {
return Err("\"" + path.string() + "\": Unable to read mod.json");
}
nlohmann::json json;
try {
json = nlohmann::json::parse(std::string(read, read + readSize));
} catch(std::exception const& e) {
delete[] read;
return Err<>(e.what());
}
delete[] read;
if (!json.is_object()) {
return Err(
"\"" + path.string() + "/mod.json\" does not have an "
"object at root despite expected"
);
}
auto res = ModInfo::create(json);
if (!res) {
return Err("\"" + path.string() + "\" - " + res.error());
}
auto info = res.value();
info.m_path = path;
// unzip known MD files
using God = std::initializer_list<std::pair<std::string, std::string*>>;
for (auto [file, target] : God {
{ "about.md", &info.m_details },
{ "changelog.md", &info.m_changelog },
}) {
if (unzip.fileExists(file)) {
unsigned long readSize = 0;
auto fileData = unzip.getFileData(file, &readSize);
if (!fileData || !readSize) {
return Err("Unable to read \"" + path.string() + "\"/" + file);
} else {
*target = sanitizeDetailsData(fileData, fileData + readSize);
}
}
}
return Ok(info);
}

View file

@ -34,7 +34,7 @@ Result<std::shared_ptr<Setting>> Setting::parse(
if (rawJson.is_object()) {
auto json = rawJson;
JsonChecker checker(json);
auto root = checker.root("[setting \"" + key + "\"]");
auto root = checker.root("[setting \"" + key + "\"]").obj();
auto res = Setting::parse(
root.needs("type").get<std::string>(), key, root

View file

@ -37,9 +37,13 @@ Result<Mod*> Loader::loadModFromFile(std::string const& path) {
// create and set up Mod instance
auto mod = new Mod(res.value());
mod->m_saveDirPath = Loader::get()->getGeodeSaveDirectory() / GEODE_MOD_DIRECTORY / res.value().m_id;
mod->loadDataStore();
ghc::filesystem::create_directories(mod->m_saveDirPath);
auto sett = mod->loadSettings();
if (!sett) {
mod->logInfo(sett.error(), Severity::Error);
}
// enable mod if needed
mod->m_enabled = Loader::get()->shouldLoadMod(mod->m_info.m_id);
this->m_mods.insert({ res.value().m_id, mod });