#include "config.hpp" #include "JSON.hpp" #include "logger.hpp" #include "stringutil.hpp" #include #include #include #include #include #include namespace { // Makes stuff slightly easier to read. using ConfigPair = std::pair; /// @brief This is the default working directory path. constexpr std::string_view PATH_DEFAULT_WORK_DIR = "sdmc:/JKSV"; // Folder path. constexpr std::string_view PATH_CONFIG_FOLDER = "sdmc:/config/JKSV"; // Actual config path. constexpr std::string_view PATH_CONFIG_FILE = "sdmc:/config/JKSV/JKSV.json"; // Paths file. Funny name too. constexpr std::string_view PATH_PATHS_PATH = "sdmc:/config/JKSV/Paths.json"; // Vector to preserve order now. std::vector s_configVector; // Working directory fslib::Path s_workingDirectory; // UI animation scaling. double s_uiAnimationScaling; // Vector of favorite title ids std::vector s_favorites; // Vector of titles to ignore. std::vector s_blacklist; // Map of paths. std::unordered_map s_pathMap; } // namespace static void read_array_to_vector(std::vector &vector, json_object *array) { // Just in case. Shouldn't happen though. vector.clear(); size_t arrayLength = json_object_array_length(array); for (size_t i = 0; i < arrayLength; i++) { json_object *arrayEntry = json_object_array_get_idx(array, i); if (!arrayEntry) { continue; } vector.push_back(std::strtoull(json_object_get_string(arrayEntry), NULL, 16)); } } void config::initialize(void) { if (!fslib::directory_exists(PATH_CONFIG_FOLDER) && !fslib::create_directories_recursively(PATH_CONFIG_FOLDER)) { logger::log("Error creating config folder: %s.", fslib::get_error_string()); config::reset_to_default(); return; } json::Object configJSON = json::new_object(json_object_from_file, PATH_CONFIG_FILE.data()); if (!configJSON) { logger::log("Error opening config for reading: %s", fslib::get_error_string()); config::reset_to_default(); return; } json_object_iterator configIterator = json_object_iter_begin(configJSON.get()); json_object_iterator configEnd = json_object_iter_end(configJSON.get()); while (!json_object_iter_equal(&configIterator, &configEnd)) { const char *keyName = json_object_iter_peek_name(&configIterator); json_object *configValue = json_object_iter_peek_value(&configIterator); // These are exemptions. if (std::strcmp(keyName, config::keys::WORKING_DIRECTORY.data()) == 0) { s_workingDirectory = json_object_get_string(configValue); } else if (std::strcmp(keyName, config::keys::UI_ANIMATION_SCALE.data()) == 0) { s_uiAnimationScaling = json_object_get_double(configValue); } else if (std::strcmp(keyName, config::keys::FAVORITES.data()) == 0) { read_array_to_vector(s_favorites, configValue); } else if (std::strcmp(keyName, config::keys::BLACKLIST.data()) == 0) { read_array_to_vector(s_blacklist, configValue); } else { s_configVector.push_back(std::make_pair(keyName, json_object_get_uint64(configValue))); } json_object_iter_next(&configIterator); } // Load custom output paths. if (!fslib::file_exists(PATH_PATHS_PATH)) { // Just bail. return; } json::Object pathsJSON = json::new_object(json_object_from_file, PATH_PATHS_PATH.data()); if (!pathsJSON) { return; } json_object_iterator pathsIterator = json_object_iter_begin(pathsJSON.get()); json_object_iterator pathsEnd = json_object_iter_end(pathsJSON.get()); while (!json_object_iter_equal(&pathsIterator, &pathsEnd)) { // Grab these uint64_t applicationID = std::strtoull(json_object_iter_peek_name(&pathsIterator), NULL, 16); json_object *path = json_object_iter_peek_value(&pathsIterator); // Map em. s_pathMap[applicationID] = json_object_get_string(path); json_object_iter_next(&pathsIterator); } } void config::reset_to_default(void) { s_workingDirectory = PATH_DEFAULT_WORK_DIR; s_configVector.push_back(std::make_pair(config::keys::INCLUDE_DEVICE_SAVES.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::AUTO_BACKUP_ON_RESTORE.data(), 1)); s_configVector.push_back(std::make_pair(config::keys::AUTO_NAME_BACKUPS.data(), 1)); s_configVector.push_back(std::make_pair(config::keys::AUTO_UPLOAD.data(), 1)); s_configVector.push_back(std::make_pair(config::keys::HOLD_FOR_DELETION.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::HOLD_FOR_RESTORATION.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::HOLD_FOR_OVERWRITE.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::ONLY_LIST_MOUNTABLE.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::LIST_ACCOUNT_SYS_SAVES.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::ALLOW_WRITING_TO_SYSTEM.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::EXPORT_TO_ZIP.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::TITLE_SORT_TYPE.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::JKSM_TEXT_MODE.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::FORCE_ENGLISH.data(), 0)); s_configVector.push_back(std::make_pair(config::keys::ENABLE_TRASH_BIN.data(), 0)); s_uiAnimationScaling = 2.5f; } void config::save(void) { { json::Object configJSON = json::new_object(json_object_new_object); // Add working directory first. json_object *workingDirectory = json_object_new_string(s_workingDirectory.c_string()); json::add_object(configJSON, config::keys::WORKING_DIRECTORY.data(), workingDirectory); // Loop through map and add it. for (auto &[key, value] : s_configVector) { json_object *jsonValue = json_object_new_uint64(value); json::add_object(configJSON, key.c_str(), jsonValue); } // Add UI scaling. json_object *scaling = json_object_new_double(s_uiAnimationScaling); json::add_object(configJSON, config::keys::UI_ANIMATION_SCALE.data(), scaling); // Favorites json_object *favoritesArray = json_object_new_array(); for (uint64_t &titleID : s_favorites) { // Need to do it like this or json-c does decimal instead of hex. json_object *newFavorite = json_object_new_string(stringutil::get_formatted_string("%016lX", titleID).c_str()); json_object_array_add(favoritesArray, newFavorite); } json::add_object(configJSON, config::keys::FAVORITES.data(), favoritesArray); // Same but blacklist json_object *blacklistArray = json_object_new_array(); for (uint64_t &titleID : s_blacklist) { json_object *newBlacklist = json_object_new_string(stringutil::get_formatted_string("%016lX", titleID).c_str()); json_object_array_add(blacklistArray, newBlacklist); } json::add_object(configJSON, config::keys::BLACKLIST.data(), blacklistArray); // Write config file fslib::File configFile(PATH_CONFIG_FILE, FsOpenMode_Create | FsOpenMode_Write, std::strlen(json_object_get_string(configJSON.get()))); if (configFile) { configFile << json_object_get_string(configJSON.get()); } } if (!s_pathMap.empty()) { // Paths file. json::Object pathsJSON = json::new_object(json_object_new_object); // Loop through map and write stuff. for (auto &[applicationID, path] : s_pathMap) { // Get ID as hex string. std::string idHex = stringutil::get_formatted_string("%016llX", applicationID); // path json_object *pathObject = json_object_new_string(path.c_str()); // Add to pathsJSON object. json::add_object(pathsJSON, idHex.c_str(), pathObject); } // Write it. fslib::File pathsFile(PATH_PATHS_PATH, FsOpenMode_Create | FsOpenMode_Write, std::strlen(json_object_get_string(pathsJSON.get()))); if (pathsFile) { pathsFile << json_object_get_string(pathsJSON.get()); } } } uint8_t config::get_by_key(std::string_view key) { // See if the key can be found. auto findKey = std::find_if(s_configVector.begin(), s_configVector.end(), [key](const auto &configPair) { return key == configPair.first; }); // Bail. if (findKey == s_configVector.end()) { return 0; } // Return the value. return findKey->second; } void config::toggle_by_key(std::string_view key) { // Make sure the key exists first. auto findKey = std::find_if(s_configVector.begin(), s_configVector.end(), [key](const auto &configPair) { return key == configPair.first; }); if (findKey == s_configVector.end()) { return; } findKey->second = findKey->second ? 0 : 1; } void config::set_by_key(std::string_view key, uint8_t value) { auto findKey = std::find_if(s_configVector.begin(), s_configVector.end(), [key](const auto &configPair) { return key == configPair.first; }); if (findKey == s_configVector.end()) { return; } findKey->second = value; } uint8_t config::get_by_index(int index) { if (index < 0 || index >= static_cast(s_configVector.size())) { return 0; } return s_configVector.at(index).second; } void config::toggle_by_index(int index) { if (index < 0 || index >= static_cast(s_configVector.size())) { return; } s_configVector[index].second = s_configVector[index].second ? 0 : 1; } void config::set_by_index(int index, uint8_t value) { if (index < 0 || index >= static_cast(s_configVector.size())) { return; } s_configVector[index].second = value; } fslib::Path config::get_working_directory(void) { return s_workingDirectory; } double config::get_animation_scaling(void) { return s_uiAnimationScaling; } void config::set_animation_scaling(double newScale) { s_uiAnimationScaling = newScale; } void config::add_remove_favorite(uint64_t applicationID) { auto findTitle = std::find(s_favorites.begin(), s_favorites.end(), applicationID); if (findTitle == s_favorites.end()) { s_favorites.push_back(applicationID); } else { s_favorites.erase(findTitle); } } bool config::is_favorite(uint64_t applicationID) { if (std::find(s_favorites.begin(), s_favorites.end(), applicationID) == s_favorites.end()) { return false; } return true; } void config::add_remove_blacklist(uint64_t applicationID) { auto findTitle = std::find(s_blacklist.begin(), s_blacklist.end(), applicationID); if (findTitle == s_blacklist.end()) { s_blacklist.push_back(applicationID); } else { s_blacklist.erase(findTitle); } } bool config::is_blacklisted(uint64_t applicationID) { if (std::find(s_blacklist.begin(), s_blacklist.end(), applicationID) == s_blacklist.end()) { return false; } return true; } void config::add_custom_path(uint64_t applicationID, std::string_view customPath) { s_pathMap[applicationID] = customPath.data(); } bool config::has_custom_path(uint64_t applicationID) { if (s_pathMap.find(applicationID) == s_pathMap.end()) { return false; } return true; } void config::get_custom_path(uint64_t applicationID, char *pathOut, size_t pathOutSize) { if (s_pathMap.find(applicationID) == s_pathMap.end()) { return; } std::memcpy(pathOut, s_pathMap[applicationID].c_str(), s_pathMap[applicationID].length()); }