Skip to content

Commit 5eada50

Browse files
authored
Rename manifest to mod.json, refactor config parsing, add display_name, description, short_description fields (#81)
1 parent cdfe416 commit 5eada50

File tree

3 files changed

+165
-130
lines changed

3 files changed

+165
-130
lines changed

librecomp/include/librecomp/mods.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ namespace recomp {
6464
NoManifest,
6565
FailedToParseManifest,
6666
InvalidManifestSchema,
67-
UnrecognizedManifestField,
6867
IncorrectManifestFieldType,
6968
InvalidVersionString,
7069
InvalidMinimumRecompVersionString,
@@ -157,6 +156,9 @@ namespace recomp {
157156

158157
struct ModDetails {
159158
std::string mod_id;
159+
std::string display_name;
160+
std::string description;
161+
std::string short_description;
160162
Version version;
161163
std::vector<std::string> authors;
162164
std::vector<Dependency> dependencies;
@@ -168,6 +170,9 @@ namespace recomp {
168170

169171
std::vector<std::string> mod_game_ids;
170172
std::string mod_id;
173+
std::string display_name;
174+
std::string description;
175+
std::string short_description;
171176
std::vector<std::string> authors;
172177
std::vector<Dependency> dependencies;
173178
std::unordered_map<std::string, size_t> dependencies_by_id;

librecomp/src/mod_manifest.cpp

Lines changed: 156 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ enum class ManifestField {
143143

144144
const std::string game_mod_id_key = "game_id";
145145
const std::string mod_id_key = "id";
146+
const std::string display_name_key = "display_name";
147+
const std::string description_key = "description";
148+
const std::string short_description_key = "short_description";
146149
const std::string version_key = "version";
147150
const std::string authors_key = "authors";
148151
const std::string minimum_recomp_version_key = "minimum_recomp_version";
@@ -224,6 +227,77 @@ static bool parse_dependency(const std::string& val, recomp::mods::Dependency& o
224227
return false;
225228
}
226229

230+
template <typename T1, typename T2>
231+
recomp::mods::ModOpenError try_get(T2& out, const nlohmann::json& data, const std::string& key, bool required, std::string& error_param) {
232+
auto find_it = data.find(key);
233+
if (find_it == data.end()) {
234+
if (required) {
235+
error_param = key;
236+
return recomp::mods::ModOpenError::MissingManifestField;
237+
}
238+
out = {};
239+
return recomp::mods::ModOpenError::Good;
240+
}
241+
242+
const T1* ptr = find_it->get_ptr<const T1*>();
243+
if (ptr == nullptr) {
244+
error_param = key;
245+
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
246+
}
247+
248+
out = *ptr;
249+
return recomp::mods::ModOpenError::Good;
250+
}
251+
252+
recomp::mods::ModOpenError try_get_version(recomp::Version& out, const nlohmann::json& data, const std::string& key, std::string& error_param, recomp::mods::ModOpenError invalid_version_error) {
253+
std::string version_string{};
254+
255+
recomp::mods::ModOpenError try_get_err = try_get<nlohmann::json::string_t>(version_string, data, key, true, error_param);
256+
if (try_get_err != recomp::mods::ModOpenError::Good) {
257+
return try_get_err;
258+
}
259+
260+
if (!recomp::Version::from_string(version_string, out)) {
261+
error_param = version_string;
262+
return invalid_version_error;
263+
}
264+
265+
return recomp::mods::ModOpenError::Good;
266+
}
267+
268+
template <typename T1, typename T2>
269+
recomp::mods::ModOpenError try_get_vec(std::vector<T2>& out, const nlohmann::json& data, const std::string& key, bool required, std::string& error_param) {
270+
auto find_it = data.find(key);
271+
if (find_it == data.end()) {
272+
if (required) {
273+
error_param = key;
274+
return recomp::mods::ModOpenError::MissingManifestField;
275+
}
276+
return recomp::mods::ModOpenError::Good;
277+
}
278+
279+
const nlohmann::json::array_t* ptr = find_it->get_ptr<const nlohmann::json::array_t*>();
280+
if (ptr == nullptr) {
281+
error_param = key;
282+
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
283+
}
284+
285+
out.clear();
286+
287+
for (const nlohmann::json& cur_val : *ptr) {
288+
const T1* temp_ptr = cur_val.get_ptr<const T1*>();
289+
if (temp_ptr == nullptr) {
290+
out.clear();
291+
error_param = key;
292+
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
293+
}
294+
295+
out.emplace_back(*temp_ptr);
296+
}
297+
298+
return recomp::mods::ModOpenError::Good;
299+
}
300+
227301
recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const std::vector<char>& manifest_data, std::string& error_param) {
228302
using json = nlohmann::json;
229303
json manifest_json = json::parse(manifest_data.begin(), manifest_data.end(), nullptr, false);
@@ -236,137 +310,97 @@ recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const
236310
return recomp::mods::ModOpenError::InvalidManifestSchema;
237311
}
238312

239-
for (const auto& [key, val] : manifest_json.items()) {
240-
const auto find_key_it = field_map.find(key);
241-
if (find_key_it == field_map.end()) {
242-
// Unrecognized field
243-
error_param = key;
244-
return recomp::mods::ModOpenError::UnrecognizedManifestField;
245-
}
313+
recomp::mods::ModOpenError current_error = recomp::mods::ModOpenError::Good;
246314

247-
ManifestField field = find_key_it->second;
248-
switch (field) {
249-
case ManifestField::GameModId:
250-
{
251-
std::string mod_game_id;
252-
if (!get_to<json::string_t>(val, mod_game_id)) {
253-
error_param = key;
254-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
255-
}
256-
ret.mod_game_ids.resize(1);
257-
ret.mod_game_ids[0] = std::move(mod_game_id);
258-
}
259-
break;
260-
case ManifestField::Id:
261-
if (!get_to<json::string_t>(val, ret.mod_id)) {
262-
error_param = key;
263-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
264-
}
265-
break;
266-
case ManifestField::Version:
267-
{
268-
const std::string* version_str = val.get_ptr<const std::string*>();
269-
if (version_str == nullptr) {
270-
error_param = key;
271-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
272-
}
273-
if (!recomp::Version::from_string(*version_str, ret.version)) {
274-
error_param = *version_str;
275-
return recomp::mods::ModOpenError::InvalidVersionString;
276-
}
277-
}
278-
break;
279-
case ManifestField::Authors:
280-
if (!get_to_vec<std::string>(val, ret.authors)) {
281-
error_param = key;
282-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
283-
}
284-
break;
285-
case ManifestField::MinimumRecompVersion:
286-
{
287-
const std::string* version_str = val.get_ptr<const std::string*>();
288-
if (version_str == nullptr) {
289-
error_param = key;
290-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
291-
}
292-
if (!recomp::Version::from_string(*version_str, ret.minimum_recomp_version)) {
293-
error_param = *version_str;
294-
return recomp::mods::ModOpenError::InvalidMinimumRecompVersionString;
295-
}
296-
ret.minimum_recomp_version.suffix.clear();
297-
}
298-
break;
299-
case ManifestField::Dependencies:
300-
{
301-
std::vector<std::string> dep_strings{};
302-
if (!get_to_vec<std::string>(val, dep_strings)) {
303-
error_param = key;
304-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
305-
}
306-
307-
for (const std::string& dep_string : dep_strings) {
308-
recomp::mods::Dependency cur_dep;
309-
if (!parse_dependency(dep_string, cur_dep)) {
310-
error_param = dep_string;
311-
return recomp::mods::ModOpenError::InvalidDependencyString;
312-
}
313-
314-
size_t dependency_index = ret.dependencies.size();
315-
ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index);
316-
ret.dependencies.emplace_back(std::move(cur_dep));
317-
}
318-
}
319-
break;
320-
case ManifestField::NativeLibraries:
321-
{
322-
if (!val.is_object()) {
323-
error_param = key;
324-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
325-
}
326-
for (const auto& [lib_name, lib_exports] : val.items()) {
327-
recomp::mods::NativeLibraryManifest& cur_lib = ret.native_libraries.emplace_back();
328-
329-
cur_lib.name = lib_name;
330-
if (!get_to_vec<std::string>(lib_exports, cur_lib.exports)) {
331-
error_param = key;
332-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
333-
}
334-
}
335-
}
336-
break;
337-
}
315+
// Mod Game ID
316+
std::string mod_game_id{};
317+
current_error = try_get<json::string_t>(mod_game_id, manifest_json, game_mod_id_key, true, error_param);
318+
if (current_error != recomp::mods::ModOpenError::Good) {
319+
return current_error;
338320
}
321+
ret.mod_game_ids.emplace_back(std::move(mod_game_id));
339322

340-
return recomp::mods::ModOpenError::Good;
341-
}
323+
// Mod ID
324+
current_error = try_get<json::string_t>(ret.mod_id, manifest_json, mod_id_key, true, error_param);
325+
if (current_error != recomp::mods::ModOpenError::Good) {
326+
return current_error;
327+
}
342328

343-
recomp::mods::ModOpenError validate_manifest(const recomp::mods::ModManifest& manifest, std::string& error_param) {
344-
using namespace recomp::mods;
329+
// Display name
330+
current_error = try_get<json::string_t>(ret.display_name, manifest_json, display_name_key, true, error_param);
331+
if (current_error != recomp::mods::ModOpenError::Good) {
332+
return current_error;
333+
}
345334

346-
// Check for required fields.
347-
if (manifest.mod_game_ids.empty()) {
348-
error_param = game_mod_id_key;
349-
return ModOpenError::MissingManifestField;
350-
}
351-
if (manifest.mod_id.empty()) {
352-
error_param = mod_id_key;
353-
return ModOpenError::MissingManifestField;
335+
// Description (optional)
336+
current_error = try_get<json::string_t>(ret.description, manifest_json, description_key, false, error_param);
337+
if (current_error != recomp::mods::ModOpenError::Good) {
338+
return current_error;
354339
}
355-
if (manifest.version.major == -1 || manifest.version.major == -1 || manifest.version.major == -1) {
356-
error_param = version_key;
357-
return ModOpenError::MissingManifestField;
340+
341+
// Short Description (optional)
342+
current_error = try_get<json::string_t>(ret.short_description, manifest_json, short_description_key, false, error_param);
343+
if (current_error != recomp::mods::ModOpenError::Good) {
344+
return current_error;
358345
}
359-
if (manifest.authors.empty()) {
360-
error_param = authors_key;
361-
return ModOpenError::MissingManifestField;
346+
347+
// Version
348+
current_error = try_get_version(ret.version, manifest_json, version_key, error_param, recomp::mods::ModOpenError::InvalidVersionString);
349+
if (current_error != recomp::mods::ModOpenError::Good) {
350+
return current_error;
362351
}
363-
if (manifest.minimum_recomp_version.major == -1 || manifest.minimum_recomp_version.major == -1 || manifest.minimum_recomp_version.major == -1) {
364-
error_param = minimum_recomp_version_key;
365-
return ModOpenError::MissingManifestField;
352+
353+
// Authors
354+
current_error = try_get_vec<json::string_t>(ret.authors, manifest_json, authors_key, true, error_param);
355+
if (current_error != recomp::mods::ModOpenError::Good) {
356+
return current_error;
366357
}
367358

368-
return ModOpenError::Good;
369-
}
359+
// Minimum recomp version
360+
current_error = try_get_version(ret.minimum_recomp_version, manifest_json, minimum_recomp_version_key, error_param, recomp::mods::ModOpenError::InvalidMinimumRecompVersionString);
361+
if (current_error != recomp::mods::ModOpenError::Good) {
362+
return current_error;
363+
}
364+
365+
// Dependencies (optional)
366+
std::vector<std::string> dep_strings{};
367+
current_error = try_get_vec<json::string_t>(dep_strings, manifest_json, dependencies_key, false, error_param);
368+
if (current_error != recomp::mods::ModOpenError::Good) {
369+
return current_error;
370+
}
371+
for (const std::string& dep_string : dep_strings) {
372+
recomp::mods::Dependency cur_dep;
373+
if (!parse_dependency(dep_string, cur_dep)) {
374+
error_param = dep_string;
375+
return recomp::mods::ModOpenError::InvalidDependencyString;
376+
}
377+
378+
size_t dependency_index = ret.dependencies.size();
379+
ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index);
380+
ret.dependencies.emplace_back(std::move(cur_dep));
381+
}
382+
383+
// Native libraries (optional)
384+
auto find_libs_it = manifest_json.find(native_libraries_key);
385+
if (find_libs_it != manifest_json.end()) {
386+
auto& val = *find_libs_it;
387+
if (!val.is_object()) {
388+
error_param = native_libraries_key;
389+
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
390+
}
391+
for (const auto& [lib_name, lib_exports] : val.items()) {
392+
recomp::mods::NativeLibraryManifest& cur_lib = ret.native_libraries.emplace_back();
393+
394+
cur_lib.name = lib_name;
395+
if (!get_to_vec<std::string>(lib_exports, cur_lib.exports)) {
396+
error_param = native_libraries_key;
397+
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
398+
}
399+
}
400+
}
401+
402+
return recomp::mods::ModOpenError::Good;
403+
}
370404

371405
recomp::mods::ModOpenError recomp::mods::ModContext::open_mod(const std::filesystem::path& mod_path, std::string& error_param, const std::vector<ModContentTypeId>& supported_content_types, bool requires_manifest) {
372406
ModManifest manifest{};
@@ -406,7 +440,7 @@ recomp::mods::ModOpenError recomp::mods::ModContext::open_mod(const std::filesys
406440

407441
{
408442
bool exists;
409-
std::vector<char> manifest_data = manifest.file_handle->read_file("manifest.json", exists);
443+
std::vector<char> manifest_data = manifest.file_handle->read_file("mod.json", exists);
410444
if (!exists) {
411445
// If this container type requires a manifest then return an error.
412446
if (requires_manifest) {
@@ -449,11 +483,6 @@ recomp::mods::ModOpenError recomp::mods::ModContext::open_mod(const std::filesys
449483
}
450484
mod_ids.emplace(manifest.mod_id);
451485

452-
ModOpenError validate_error = validate_manifest(manifest, error_param);
453-
if (validate_error != ModOpenError::Good) {
454-
return validate_error;
455-
}
456-
457486
// Check for this mod's game ids being valid.
458487
std::vector<size_t> game_indices;
459488
for (const auto& mod_game_id : manifest.mod_game_ids) {
@@ -513,8 +542,6 @@ std::string recomp::mods::error_to_string(ModOpenError error) {
513542
return "Failed to parse mod's manifest.json";
514543
case ModOpenError::InvalidManifestSchema:
515544
return "Mod's manifest.json has an invalid schema";
516-
case ModOpenError::UnrecognizedManifestField:
517-
return "Unrecognized field in manifest.json";
518545
case ModOpenError::IncorrectManifestFieldType:
519546
return "Incorrect type for field in manifest.json";
520547
case ModOpenError::InvalidVersionString:

librecomp/src/mods.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,9 @@ std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_mod_details(
742742

743743
ret.emplace_back(ModDetails{
744744
.mod_id = mod.manifest.mod_id,
745+
.display_name = mod.manifest.display_name,
746+
.description = mod.manifest.description,
747+
.short_description = mod.manifest.short_description,
745748
.version = mod.manifest.version,
746749
.authors = mod.manifest.authors,
747750
.dependencies = mod.manifest.dependencies,

0 commit comments

Comments
 (0)