@@ -143,6 +143,9 @@ enum class ManifestField {
143143
144144const std::string game_mod_id_key = " game_id" ;
145145const 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" ;
146149const std::string version_key = " version" ;
147150const std::string authors_key = " authors" ;
148151const 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+
227301recomp::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
371405recomp::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:
0 commit comments