mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-03-22 01:34:13 -05:00
294 lines
11 KiB
C++
294 lines
11 KiB
C++
#include "config.hpp"
|
|
|
|
#include "JSON.hpp"
|
|
#include "error.hpp"
|
|
#include "logger.hpp"
|
|
#include "stringutil.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace
|
|
{
|
|
/// @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";
|
|
// Paths file. Funny name too.
|
|
constexpr const char *PATH_PATHS_PATH = "sdmc:/config/JKSV/Paths.json";
|
|
// Actual config path.
|
|
constexpr const char *PATH_CONFIG_FILE = "sdmc:/config/JKSV/JKSV.json";
|
|
|
|
/// @brief This map holds the majority of config values.
|
|
std::map<std::string, uint8_t> s_configMap;
|
|
|
|
// Working directory
|
|
fslib::Path s_workingDirectory;
|
|
// UI animation scaling.
|
|
double s_uiAnimationScaling;
|
|
// Vector of favorite title ids
|
|
std::vector<uint64_t> s_favorites;
|
|
// Vector of titles to ignore.
|
|
std::vector<uint64_t> s_blacklist;
|
|
|
|
// Map of paths.
|
|
std::map<uint64_t, std::string> s_pathMap;
|
|
} // namespace
|
|
|
|
// Definitions at bottom.
|
|
static void read_array_to_vector(std::vector<uint64_t> &vector, json_object *array);
|
|
static void save_custom_paths();
|
|
|
|
void config::initialize()
|
|
{
|
|
// This is so we don't constantly construct new Paths
|
|
const fslib::Path configDir{PATH_CONFIG_FOLDER};
|
|
|
|
const bool configDirExists = fslib::directory_exists(configDir);
|
|
const bool configDirError = !configDirExists && error::fslib(fslib::create_directories_recursively(configDir));
|
|
if (!configDirExists && configDirError)
|
|
{
|
|
config::reset_to_default();
|
|
return;
|
|
}
|
|
|
|
json::Object configJSON = json::new_object(json_object_from_file, PATH_CONFIG_FILE);
|
|
if (!configJSON)
|
|
{
|
|
logger::log("Error opening config for reading: %s", fslib::error::get_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.
|
|
const bool workingDirectory = std::strcmp(keyName, config::keys::WORKING_DIRECTORY.data()) == 0;
|
|
const bool animationScaling = std::strcmp(keyName, config::keys::UI_ANIMATION_SCALE.data()) == 0;
|
|
const bool favorites = std::strcmp(keyName, config::keys::FAVORITES.data()) == 0;
|
|
const bool blacklist = std::strcmp(keyName, config::keys::BLACKLIST.data()) == 0;
|
|
|
|
if (workingDirectory) { s_workingDirectory = json_object_get_string(configValue); }
|
|
else if (animationScaling) { s_uiAnimationScaling = json_object_get_double(configValue); }
|
|
else if (favorites) { read_array_to_vector(s_favorites, configValue); }
|
|
else if (blacklist) { read_array_to_vector(s_blacklist, configValue); }
|
|
else { s_configMap[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);
|
|
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))
|
|
{
|
|
const char *idString = json_object_iter_peek_name(&pathsIterator);
|
|
json_object *pathObject = json_object_iter_peek_value(&pathsIterator);
|
|
const uint64_t applicationID = std::strtoull(idString, nullptr, 16);
|
|
const char *path = json_object_get_string(pathObject);
|
|
|
|
s_pathMap[applicationID] = path;
|
|
|
|
json_object_iter_next(&pathsIterator);
|
|
}
|
|
}
|
|
|
|
void config::reset_to_default()
|
|
{
|
|
s_workingDirectory = PATH_DEFAULT_WORK_DIR;
|
|
s_configMap[config::keys::INCLUDE_DEVICE_SAVES.data()] = 0;
|
|
s_configMap[config::keys::AUTO_BACKUP_ON_RESTORE.data()] = 1;
|
|
s_configMap[config::keys::AUTO_NAME_BACKUPS.data()] = 0;
|
|
s_configMap[config::keys::AUTO_UPLOAD.data()] = 0;
|
|
s_configMap[config::keys::USE_TITLE_IDS.data()] = 0;
|
|
s_configMap[config::keys::HOLD_FOR_DELETION.data()] = 1;
|
|
s_configMap[config::keys::HOLD_FOR_RESTORATION.data()] = 1;
|
|
s_configMap[config::keys::HOLD_FOR_OVERWRITE.data()] = 1;
|
|
s_configMap[config::keys::ONLY_LIST_MOUNTABLE.data()] = 1;
|
|
s_configMap[config::keys::LIST_ACCOUNT_SYS_SAVES.data()] = 0;
|
|
s_configMap[config::keys::ALLOW_WRITING_TO_SYSTEM.data()] = 0;
|
|
s_configMap[config::keys::EXPORT_TO_ZIP.data()] = 1;
|
|
s_configMap[config::keys::ZIP_COMPRESSION_LEVEL.data()] = 6;
|
|
s_configMap[config::keys::TITLE_SORT_TYPE.data()] = 0;
|
|
s_configMap[config::keys::JKSM_TEXT_MODE.data()] = 0;
|
|
s_configMap[config::keys::FORCE_ENGLISH.data()] = 0;
|
|
s_configMap[config::keys::ENABLE_TRASH_BIN.data()] = 0;
|
|
s_uiAnimationScaling = 2.5f;
|
|
}
|
|
|
|
void config::save()
|
|
{
|
|
{
|
|
json::Object configJSON = json::new_object(json_object_new_object);
|
|
|
|
json_object *workingDirectory = json_object_new_string(s_workingDirectory.full_path());
|
|
json::add_object(configJSON, config::keys::WORKING_DIRECTORY.data(), workingDirectory);
|
|
|
|
for (const auto &[key, value] : s_configMap)
|
|
{
|
|
json_object *jsonValue = json_object_new_uint64(value);
|
|
json::add_object(configJSON, key.c_str(), jsonValue);
|
|
}
|
|
|
|
json_object *scaling = json_object_new_double(s_uiAnimationScaling);
|
|
json::add_object(configJSON, config::keys::UI_ANIMATION_SCALE.data(), scaling);
|
|
|
|
json_object *favoritesArray = json_object_new_array();
|
|
for (const uint64_t &titleID : s_favorites)
|
|
{
|
|
const std::string idHex = stringutil::get_formatted_string("%016llX", titleID);
|
|
json_object *newFavorite = json_object_new_string(idHex.c_str());
|
|
|
|
json_object_array_add(favoritesArray, newFavorite);
|
|
}
|
|
json::add_object(configJSON, config::keys::FAVORITES.data(), favoritesArray);
|
|
|
|
json_object *blacklistArray = json_object_new_array();
|
|
for (const uint64_t &titleID : s_blacklist)
|
|
{
|
|
const std::string idHex = stringutil::get_formatted_string("%016llX", titleID);
|
|
json_object *newBlacklist = json_object_new_string(idHex.c_str());
|
|
|
|
json_object_array_add(blacklistArray, newBlacklist);
|
|
}
|
|
json::add_object(configJSON, config::keys::BLACKLIST.data(), blacklistArray);
|
|
|
|
const char *jsonString = json_object_get_string(configJSON.get());
|
|
const int64_t configLength = std::char_traits<char>::length(jsonString);
|
|
fslib::File configFile{PATH_CONFIG_FILE, FsOpenMode_Create | FsOpenMode_Write, configLength};
|
|
if (configFile) { configFile << jsonString; }
|
|
}
|
|
}
|
|
|
|
uint8_t config::get_by_key(std::string_view key)
|
|
{
|
|
// See if the key can be found.
|
|
auto findKey = s_configMap.find(key.data());
|
|
if (findKey == s_configMap.end()) { return 0; }
|
|
return findKey->second;
|
|
}
|
|
|
|
void config::toggle_by_key(std::string_view key)
|
|
{
|
|
auto findKey = s_configMap.find(key.data());
|
|
if (findKey == s_configMap.end()) { return; }
|
|
findKey->second = findKey->second ? 0 : 1;
|
|
}
|
|
|
|
void config::set_by_key(std::string_view key, uint8_t value)
|
|
{
|
|
auto findKey = s_configMap.find(key.data());
|
|
if (findKey == s_configMap.end()) { return; }
|
|
findKey->second = value;
|
|
}
|
|
|
|
fslib::Path config::get_working_directory() { return s_workingDirectory; }
|
|
|
|
double config::get_animation_scaling() { 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); }
|
|
|
|
config::save();
|
|
}
|
|
|
|
bool config::is_favorite(uint64_t applicationID)
|
|
{
|
|
return std::find(s_favorites.begin(), s_favorites.end(), applicationID) != s_favorites.end();
|
|
}
|
|
|
|
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); }
|
|
|
|
config::save();
|
|
}
|
|
|
|
void config::get_blacklisted_titles(std::vector<uint64_t> &listOut)
|
|
{
|
|
listOut.clear();
|
|
listOut.assign(s_blacklist.begin(), s_blacklist.end());
|
|
}
|
|
|
|
bool config::is_blacklisted(uint64_t applicationID)
|
|
{
|
|
return std::find(s_blacklist.begin(), s_blacklist.end(), applicationID) != s_blacklist.end();
|
|
}
|
|
|
|
bool config::blacklist_is_empty() { return s_blacklist.size() <= 0; }
|
|
|
|
void config::add_custom_path(uint64_t applicationID, std::string_view customPath)
|
|
{
|
|
s_pathMap[applicationID] = customPath.data();
|
|
save_custom_paths();
|
|
}
|
|
|
|
bool config::has_custom_path(uint64_t applicationID) { return s_pathMap.find(applicationID) != s_pathMap.end(); }
|
|
|
|
void config::get_custom_path(uint64_t applicationID, char *pathOut, size_t pathOutSize)
|
|
{
|
|
const auto findPath = s_pathMap.find(applicationID);
|
|
if (findPath == s_pathMap.end()) { return; }
|
|
std::memcpy(pathOut, s_pathMap[applicationID].c_str(), s_pathMap[applicationID].length());
|
|
}
|
|
|
|
static void read_array_to_vector(std::vector<uint64_t> &vector, json_object *array)
|
|
{
|
|
// Just in case. Shouldn't happen though.
|
|
vector.clear();
|
|
|
|
const 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));
|
|
}
|
|
}
|
|
|
|
static void save_custom_paths()
|
|
{
|
|
json::Object pathsJson = json::new_object(json_object_new_object);
|
|
if (!pathsJson) { return; }
|
|
|
|
for (const auto &[applicationId, path] : s_pathMap)
|
|
{
|
|
const std::string titleIdHex = stringutil::get_formatted_string("%016llX", applicationId);
|
|
json_object *jsonPath = json_object_new_string(path.c_str());
|
|
|
|
json::add_object(pathsJson, titleIdHex, jsonPath);
|
|
}
|
|
|
|
const char *jsonString = json_object_get_string(pathsJson.get());
|
|
const int64_t jsonLength = std::char_traits<char>::length(jsonString);
|
|
fslib::File pathsFile{PATH_PATHS_PATH, FsOpenMode_Create | FsOpenMode_Write, jsonLength};
|
|
if (error::fslib(pathsFile)) { return; }
|
|
|
|
pathsFile << jsonString;
|
|
}
|