mirror of
https://github.com/limo-app/limo.git
synced 2025-12-23 23:07:52 -05:00
add LOOT support for OpenMwPluginDeployer
This commit is contained in:
@@ -934,6 +934,16 @@ bool Deployer::isCaseInvariant() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Deployer::getEnableUnsafeSorting() const
|
||||
{
|
||||
return enable_unsafe_sorting_;
|
||||
}
|
||||
|
||||
void Deployer::setEnableUnsafeSorting(bool enable)
|
||||
{
|
||||
enable_unsafe_sorting_ = enable;
|
||||
}
|
||||
|
||||
void Deployer::removeManagedDirFile(const sfs::path& directory) const
|
||||
{
|
||||
sfs::remove(directory / managed_dir_file_name_);
|
||||
|
||||
@@ -372,6 +372,20 @@ public:
|
||||
* \return False.
|
||||
*/
|
||||
virtual bool isCaseInvariant() const;
|
||||
/*!
|
||||
* \brief Returns whether sorting mods is allowed affect overwrite behavior.
|
||||
*
|
||||
* If this is set to false, sorting will always be safe and only affect how mods are displayed.
|
||||
* \return The safe sorting state.
|
||||
*/
|
||||
bool getEnableUnsafeSorting() const;
|
||||
/*!
|
||||
* \brief Sets whether sorting mods is allowed affect overwrite behavior.
|
||||
*
|
||||
* If this is set to false, sorting will always be safe and only affect how mods are displayed.
|
||||
* \param The new safe sorting state.
|
||||
*/
|
||||
void setEnableUnsafeSorting(bool enable);
|
||||
|
||||
protected:
|
||||
/*! \brief Type of this deployer, e.g. Simple Deployer. */
|
||||
@@ -403,6 +417,8 @@ protected:
|
||||
bool is_autonomous_ = false;
|
||||
/*! \brief If true: Automatically update conflict groups when necessary. */
|
||||
bool auto_update_conflict_groups_ = false;
|
||||
/*! \brief Determines whether sorting mods can affect overwrite behavior. */
|
||||
bool enable_unsafe_sorting_ = false;
|
||||
|
||||
/*!
|
||||
* \brief Creates a pair of maps. One maps relative file paths to the mod id from which that
|
||||
|
||||
@@ -56,4 +56,6 @@ struct DeployerInfo
|
||||
std::vector<std::pair<std::string, std::string>> mod_actions = {};
|
||||
/*! \brief For every mod: IDs of every valid mod_action which is valid for that mod. */
|
||||
std::vector<std::vector<int>> valid_mod_actions = {};
|
||||
/*! \brief Determines whether sorting mods can affect overwrite behavior. */
|
||||
bool uses_unsafe_sorting = false;
|
||||
};
|
||||
|
||||
@@ -35,4 +35,6 @@ struct EditDeployerInfo
|
||||
* deployer to the ignore list.
|
||||
*/
|
||||
bool update_ignore_list = false;
|
||||
/*! \brief Determines whether sorting mods can affect overwrite behavior. */
|
||||
bool enable_unsafe_sorting = true;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ LootDeployer::LootDeployer(const sfs::path& source_path,
|
||||
LIST_URLS = DEFAULT_LIST_URLS;
|
||||
// make sure no hard link related checks are performed
|
||||
deploy_mode_ = copy;
|
||||
enable_unsafe_sorting_ = true;
|
||||
if(!perform_init)
|
||||
return;
|
||||
type_ = "Loot Deployer";
|
||||
@@ -169,6 +170,7 @@ void LootDeployer::sortModsByConflicts(std::optional<ProgressNode*> progress_nod
|
||||
updateMasterList();
|
||||
if(progress_node)
|
||||
(*progress_node)->child(0).advance();
|
||||
|
||||
sfs::path master_list_path = dest_path_ / "masterlist.yaml";
|
||||
if(!sfs::exists(master_list_path))
|
||||
throw std::runtime_error("Could not find masterlist.yaml at '" + master_list_path.string() +
|
||||
@@ -187,6 +189,7 @@ void LootDeployer::sortModsByConflicts(std::optional<ProgressNode*> progress_nod
|
||||
loot_handle->GetDatabase().LoadLists(master_list_path, user_list_path, prelude_path);
|
||||
if(progress_node)
|
||||
(*progress_node)->child(1).advance();
|
||||
|
||||
std::vector<sfs::path> plugin_paths;
|
||||
std::vector<std::string> plugin_file_names;
|
||||
plugin_paths.reserve(plugins_.size());
|
||||
@@ -200,7 +203,9 @@ void LootDeployer::sortModsByConflicts(std::optional<ProgressNode*> progress_nod
|
||||
auto sorted_plugins = loot_handle->SortPlugins(plugin_file_names);
|
||||
if(progress_node)
|
||||
(*progress_node)->child(2).advance();
|
||||
|
||||
std::vector<std::pair<std::string, bool>> new_plugins;
|
||||
new_plugins.reserve(plugins_.size());
|
||||
std::set<std::string> conflicting;
|
||||
int num_light_plugins = 0;
|
||||
int num_master_plugins = 0;
|
||||
@@ -253,7 +258,8 @@ void LootDeployer::sortModsByConflicts(std::optional<ProgressNode*> progress_nod
|
||||
num_master_plugins,
|
||||
num_standard_plugins,
|
||||
num_light_plugins));
|
||||
plugins_ = new_plugins;
|
||||
if(enable_unsafe_sorting_)
|
||||
plugins_ = new_plugins;
|
||||
writePluginTags();
|
||||
writePlugins();
|
||||
if(progress_node)
|
||||
|
||||
@@ -43,13 +43,17 @@ public:
|
||||
"https://raw.githubusercontent.com/loot/fallout4vr/v0.21/masterlist.yaml" },
|
||||
{ loot::GameType::fonv,
|
||||
"https://raw.githubusercontent.com/loot/falloutnv/v0.21/masterlist.yaml" },
|
||||
// there is no dedicated OpenMW masterlist, so we use the morrowind one
|
||||
{ loot::GameType::openmw,
|
||||
"https://raw.githubusercontent.com/loot/morrowind/v0.21/masterlist.yaml" },
|
||||
{ loot::GameType::starfield,
|
||||
"https://raw.githubusercontent.com/loot/starfield/v0.21/masterlist.yaml" },
|
||||
{ loot::GameType::tes3,
|
||||
"https://raw.githubusercontent.com/loot/morrowind/v0.21/masterlist.yaml" },
|
||||
{ loot::GameType::tes4,
|
||||
"https://raw.githubusercontent.com/loot/oblivion/v0.21/masterlist.yaml" },
|
||||
{ loot::GameType::tes5, "https://raw.githubusercontent.com/loot/skyrim/v0.21/masterlist.yaml" },
|
||||
{ loot::GameType::tes5,
|
||||
"https://raw.githubusercontent.com/loot/skyrim/v0.21/masterlist.yaml" },
|
||||
{ loot::GameType::tes5se,
|
||||
"https://raw.githubusercontent.com/loot/skyrimse/v0.21/masterlist.yaml" },
|
||||
{ loot::GameType::tes5vr,
|
||||
|
||||
@@ -307,6 +307,7 @@ void ModdedApplication::addDeployer(const EditDeployerInfo& info)
|
||||
info.deploy_mode,
|
||||
info.separate_profile_dirs,
|
||||
info.update_ignore_list));
|
||||
deployers_.back()->setEnableUnsafeSorting(info.enable_unsafe_sorting);
|
||||
for(int i = 0; i < profile_names_.size(); i++)
|
||||
deployers_.back()->addProfile();
|
||||
deployers_.back()->setProfile(current_profile_);
|
||||
@@ -532,6 +533,7 @@ void ModdedApplication::editDeployer(int deployer, const EditDeployerInfo& info)
|
||||
deployers_[deployer]->setName(info.name);
|
||||
deployers_[deployer]->setDestPath(info.target_dir);
|
||||
deployers_[deployer]->setDeployMode(info.deploy_mode);
|
||||
deployers_[deployer]->setEnableUnsafeSorting(info.enable_unsafe_sorting);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -550,6 +552,7 @@ void ModdedApplication::editDeployer(int deployer, const EditDeployerInfo& info)
|
||||
json_settings_["deployers"][deployer]["dest_path"] = info.target_dir;
|
||||
json_settings_["deployers"][deployer]["type"] = info.type;
|
||||
json_settings_["deployers"][deployer]["deploy_mode"] = info.deploy_mode;
|
||||
json_settings_["deployers"][deployer]["enable_unsafe_sorting"] = info.enable_unsafe_sorting;
|
||||
updateState();
|
||||
}
|
||||
if(deployers_[deployer]->isAutonomous() && info.type != DeployerFactory::REVERSEDEPLOYER)
|
||||
@@ -914,7 +917,8 @@ DeployerInfo ModdedApplication::getDeployerInfo(int deployer)
|
||||
deployers_[deployer]->idsAreSourceReferences(),
|
||||
{},
|
||||
deployers_[deployer]->getModActions(),
|
||||
deployers_[deployer]->getValidModActions() };
|
||||
deployers_[deployer]->getValidModActions(),
|
||||
deployers_[deployer]->getEnableUnsafeSorting() };
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -964,7 +968,8 @@ DeployerInfo ModdedApplication::getDeployerInfo(int deployer)
|
||||
deployers_[deployer]->idsAreSourceReferences(),
|
||||
mod_names,
|
||||
deployers_[deployer]->getModActions(),
|
||||
deployers_[deployer]->getValidModActions() };
|
||||
deployers_[deployer]->getValidModActions(),
|
||||
deployers_[deployer]->getEnableUnsafeSorting() };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1721,6 +1726,8 @@ void ModdedApplication::updateSettings(bool write)
|
||||
json_settings_["deployers"][depl]["name"] = deployers_[depl]->getName();
|
||||
json_settings_["deployers"][depl]["type"] = deployers_[depl]->getType();
|
||||
json_settings_["deployers"][depl]["deploy_mode"] = deployers_[depl]->getDeployMode();
|
||||
json_settings_["deployers"][depl]["enable_unsafe_sorting"] =
|
||||
deployers_[depl]->getEnableUnsafeSorting();
|
||||
|
||||
if(!deployers_[depl]->isAutonomous())
|
||||
{
|
||||
@@ -1908,6 +1915,8 @@ void ModdedApplication::updateState(bool read)
|
||||
sfs::path(deployers[depl]["dest_path"].asString()),
|
||||
deployers[depl]["name"].asString(),
|
||||
deploy_mode));
|
||||
if(deployers[depl].isMember("enable_unsafe_sorting"))
|
||||
deployers_.back()->setEnableUnsafeSorting(deployers[depl]["enable_unsafe_sorting"].asBool());
|
||||
|
||||
if(!deployers_[depl]->isAutonomous())
|
||||
{
|
||||
|
||||
@@ -13,10 +13,11 @@ namespace pu = path_utils;
|
||||
OpenMwPluginDeployer::OpenMwPluginDeployer(const sfs::path& source_path,
|
||||
const sfs::path& dest_path,
|
||||
const std::string& name) :
|
||||
PluginDeployer(source_path, dest_path, name)
|
||||
LootDeployer(source_path, dest_path, name, false, false)
|
||||
{
|
||||
type_ = "OpenMW Plugin Deployer";
|
||||
is_autonomous_ = true;
|
||||
app_type_ = loot::GameType::openmw;
|
||||
plugin_regex_ =
|
||||
std::regex(R"(.*\.(?:es[pm]|omwscripts|omwaddon|omwgame)$)", std::regex_constants::icase);
|
||||
plugin_file_line_regex_ = std::regex(
|
||||
@@ -80,8 +81,9 @@ std::map<std::string, int> OpenMwPluginDeployer::getAutoTagMap()
|
||||
|
||||
void OpenMwPluginDeployer::sortModsByConflicts(std::optional<ProgressNode*> progress_node)
|
||||
{
|
||||
LootDeployer::sortModsByConflicts(progress_node);
|
||||
|
||||
auto groups = getConflictGroups();
|
||||
;
|
||||
std::vector<std::pair<std::string, bool>> new_plugins;
|
||||
new_plugins.reserve(plugins_.size());
|
||||
for(const auto& group : groups)
|
||||
@@ -94,11 +96,6 @@ void OpenMwPluginDeployer::sortModsByConflicts(std::optional<ProgressNode*> prog
|
||||
writePlugins();
|
||||
}
|
||||
|
||||
bool OpenMwPluginDeployer::supportsModConflicts() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> OpenMwPluginDeployer::getModActions() const
|
||||
{
|
||||
return { { "Add Groundcover Tag", "tag-new" }, { "Remove Groundcover Tag", "tag-delete" } };
|
||||
@@ -140,6 +137,24 @@ void OpenMwPluginDeployer::applyModAction(int action, int mod_id)
|
||||
writePlugins();
|
||||
}
|
||||
|
||||
void OpenMwPluginDeployer::addProfile(int source)
|
||||
{
|
||||
// we don't want the changes made to profiles by LootDeployer
|
||||
PluginDeployer::addProfile(source);
|
||||
}
|
||||
|
||||
void OpenMwPluginDeployer::removeProfile(int profile)
|
||||
{
|
||||
// we don't want the changes made to profiles by LootDeployer
|
||||
PluginDeployer::removeProfile(profile);
|
||||
}
|
||||
|
||||
void OpenMwPluginDeployer::setProfile(int profile)
|
||||
{
|
||||
// we don't want the changes made to profiles by LootDeployer
|
||||
PluginDeployer::setProfile(profile);
|
||||
}
|
||||
|
||||
void OpenMwPluginDeployer::writePlugins() const
|
||||
{
|
||||
writePluginsPrivate();
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "plugindeployer.h"
|
||||
#include "lootdeployer.h"
|
||||
#include <set>
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Autonomous deployer which handles plugin files for OpenMW using LOOT.
|
||||
*/
|
||||
class OpenMwPluginDeployer : public PluginDeployer
|
||||
class OpenMwPluginDeployer : public LootDeployer
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
@@ -52,11 +52,6 @@ public:
|
||||
* \param progress_node Used to inform about the current progress.
|
||||
*/
|
||||
virtual void sortModsByConflicts(std::optional<ProgressNode*> progress_node = {}) override;
|
||||
/*!
|
||||
* \brief Returns whether or not this deployer type supports showing mod conflicts.
|
||||
* \return False.
|
||||
*/
|
||||
virtual bool supportsModConflicts() const override;
|
||||
/*!
|
||||
* \brief Returns names and icon names for additional actions which can be applied to a mod.
|
||||
* \return The actions.
|
||||
@@ -73,6 +68,23 @@ public:
|
||||
* \param mod_id Target mod.
|
||||
*/
|
||||
virtual void applyModAction(int action, int mod_id) override;
|
||||
/*!
|
||||
* \brief Adds a new profile and optionally copies it's load order from an existing profile.
|
||||
* Profiles are stored in the target directory.
|
||||
* \param source The profile to be copied. A value of -1 indicates no copy.
|
||||
*/
|
||||
virtual void addProfile(int source = -1) override;
|
||||
/*!
|
||||
* \brief Removes a profile.
|
||||
* \param profile The profile to be removed.
|
||||
*/
|
||||
virtual void removeProfile(int profile) override;
|
||||
/*!
|
||||
* \brief Setter for the active profile. Changes the currently active plugin files
|
||||
* to the ones saved in the new profile.
|
||||
* \param profile The new profile.
|
||||
*/
|
||||
virtual void setProfile(int profile) override;
|
||||
|
||||
private:
|
||||
/*! \brief Name of the OpenMW config file. */
|
||||
|
||||
@@ -77,6 +77,7 @@ void AddDeployerDialog::setAddMode(int app_id)
|
||||
ui->rev_depl_ignore_cb->setCheckState(Qt::Unchecked);
|
||||
ui->rev_depl_separate_cb->setCheckState(Qt::Unchecked);
|
||||
disable_confirmation_boxes_ = false;
|
||||
ui->unsafe_sorting_box->setCheckState(Qt::Checked);
|
||||
}
|
||||
|
||||
void AddDeployerDialog::setEditMode(const QString& type,
|
||||
@@ -86,6 +87,7 @@ void AddDeployerDialog::setEditMode(const QString& type,
|
||||
Deployer::DeployMode deploy_mode,
|
||||
int app_id,
|
||||
int deployer_id,
|
||||
bool uses_unsafe_sorting,
|
||||
bool has_separate_dirs,
|
||||
bool has_ignored_files)
|
||||
{
|
||||
@@ -95,6 +97,7 @@ void AddDeployerDialog::setEditMode(const QString& type,
|
||||
type_ = type;
|
||||
app_id_ = app_id;
|
||||
deployer_id_ = deployer_id;
|
||||
ui->unsafe_sorting_box->setCheckState(uses_unsafe_sorting ? Qt::Checked : Qt::Unchecked);
|
||||
has_separate_dirs_ = has_separate_dirs;
|
||||
has_ignored_files_ = has_ignored_files;
|
||||
ui->deploy_mode_box->setCurrentIndex(deploy_mode);
|
||||
@@ -132,6 +135,7 @@ void AddDeployerDialog::updateSourceFields()
|
||||
if(cur_text.empty())
|
||||
return;
|
||||
const bool is_reverse_deployer = cur_text == DeployerFactory::REVERSEDEPLOYER;
|
||||
const bool is_openmw_plugin_deployer = cur_text == DeployerFactory::OPENMWPLUGINDEPLOYER;
|
||||
const bool hide_source =
|
||||
!DeployerFactory::AUTONOMOUS_DEPLOYERS.at(cur_text) || is_reverse_deployer;
|
||||
const bool hide_mode = !hide_source && !is_reverse_deployer;
|
||||
@@ -149,6 +153,7 @@ void AddDeployerDialog::updateSourceFields()
|
||||
ui->rev_depl_ignore_button->setHidden(!is_reverse_deployer || !edit_mode_ ||
|
||||
type_.toStdString() != DeployerFactory::REVERSEDEPLOYER ||
|
||||
!has_ignored_files_);
|
||||
ui->unsafe_sorting_box->setHidden(!is_openmw_plugin_deployer);
|
||||
updateOkButton();
|
||||
}
|
||||
|
||||
@@ -194,9 +199,20 @@ void AddDeployerDialog::on_buttonBox_accepted()
|
||||
info.separate_profile_dirs = ui->rev_depl_separate_cb->checkState() == Qt::Checked;
|
||||
info.update_ignore_list = ui->rev_depl_ignore_cb->checkState() == Qt::Checked;
|
||||
if(edit_mode_)
|
||||
{
|
||||
info.enable_unsafe_sorting = ui->unsafe_sorting_box->checkState() == Qt::Checked;
|
||||
emit deployerEdited(info, app_id_, deployer_id_);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ui->type_box->currentText().toStdString() == DeployerFactory::LOOTDEPLOYER)
|
||||
info.enable_unsafe_sorting = true;
|
||||
else if(ui->type_box->currentText().toStdString() == DeployerFactory::OPENMWPLUGINDEPLOYER)
|
||||
info.enable_unsafe_sorting = ui->unsafe_sorting_box->checkState() == Qt::Checked;
|
||||
else
|
||||
info.enable_unsafe_sorting = false;
|
||||
emit deployerAdded(info, app_id_);
|
||||
}
|
||||
}
|
||||
|
||||
void AddDeployerDialog::onFileDialogAccepted(const QString& path)
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
* \param deploy_mode Determines how files are deployed to the target directory.
|
||||
* \param app_id Id of the ModdedApplication owning the edited Deployer.
|
||||
* \param deployer_id Id of the edited Deployer.
|
||||
* \param uses_unsafe_sorting Determines whether sorting mods can affect overwrite behavior.
|
||||
* \param has_separate_dirs Used by ReverseDeployers: If true: Store files on a per profile basis.
|
||||
* Else: All profiles use the same files.
|
||||
* \param has_ignored_files Used by ReverseDeployers: If true: Deployer has files on the ignore list.
|
||||
@@ -54,6 +55,7 @@ public:
|
||||
Deployer::DeployMode deploy_mode,
|
||||
int app_id,
|
||||
int deployer_id,
|
||||
bool uses_unsafe_sorting,
|
||||
bool has_separate_dirs = false,
|
||||
bool has_ignored_files = false);
|
||||
/*! \brief Enables/ Disables the ui elements responsible for setting a source directory. */
|
||||
|
||||
@@ -119,6 +119,17 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="unsafe_sorting_box">
|
||||
<property name="toolTip">
|
||||
<string>When enabled: Sorting plugins uses LOOT. This will affect override order.
|
||||
When disabled: Sorting only groups plugins by types. Does not affect override order.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use LOOT for sorting</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="rev_depl_ignore_cb">
|
||||
<property name="toolTip">
|
||||
@@ -165,7 +176,7 @@ li.checked::marker { content: "\2612"; }
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse</set>
|
||||
<set>Qt::TextInteractionFlag::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -182,7 +193,7 @@ li.checked::marker { content: "\2612"; }
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@@ -195,10 +206,10 @@ li.checked::marker { content: "\2612"; }
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -169,3 +169,8 @@ bool DeployerListModel::hasIgnoredFiles() const
|
||||
{
|
||||
return deployer_info_.has_ignored_files;
|
||||
}
|
||||
|
||||
bool DeployerListModel::usesUnsafeSorting() const
|
||||
{
|
||||
return deployer_info_.uses_unsafe_sorting;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,13 @@ public:
|
||||
* \return True if at least one file is ignored.
|
||||
*/
|
||||
bool hasIgnoredFiles() const;
|
||||
/*!
|
||||
* \brief Returns whether sorting mods is allowed affect overwrite behavior.
|
||||
*
|
||||
* If this is set to false, sorting will always be safe and only affect how mods are displayed.
|
||||
* \return The safe sorting state.
|
||||
*/
|
||||
bool usesUnsafeSorting() const;
|
||||
|
||||
private:
|
||||
/*! \brief Contains all mods managed by this model. */
|
||||
|
||||
@@ -844,6 +844,7 @@ void MainWindow::showEditDeployerDialog(int deployer)
|
||||
deploy_mode,
|
||||
currentApp(),
|
||||
deployer,
|
||||
deployer_model_->usesUnsafeSorting(),
|
||||
deployer_model_->hasSeparateDirs(),
|
||||
deployer_model_->hasIgnoredFiles());
|
||||
setBusyStatus(true, false);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"auto_update_master_list" : true,
|
||||
"current_profile" : 1,
|
||||
"list_download_time" : 0,
|
||||
"num_profiles" : 3
|
||||
}
|
||||
Reference in New Issue
Block a user