Firmware libraries settings overhaul

This commit is contained in:
Eladash 2020-12-08 21:22:08 +02:00 committed by Ivan
parent e321765c54
commit e5603fec1e
12 changed files with 178 additions and 220 deletions

View File

@ -1390,12 +1390,12 @@ void ppu_load_exec(const ppu_exec_object& elf)
// Module list to load at startup
std::set<std::string> load_libs;
if ((g_cfg.core.lib_loading != lib_loading_type::hybrid && g_cfg.core.lib_loading != lib_loading_type::manual) || g_cfg.core.load_libraries.get_set().count("liblv2.sprx"))
if (g_cfg.core.libraries_control.get_set().count("liblv2.sprx:lle") || !g_cfg.core.libraries_control.get_set().count("liblv2.sprx:hle"))
{
// Will load libsysmodule.sprx internally
load_libs.emplace("liblv2.sprx");
}
else if (g_cfg.core.lib_loading == lib_loading_type::hybrid)
else if (g_cfg.core.libraries_control.get_set().count("libsysmodule.sprx:lle") || !g_cfg.core.libraries_control.get_set().count("libsysmodule.sprx:hle"))
{
// Load only libsysmodule.sprx
load_libs.emplace("libsysmodule.sprx");

View File

@ -21,7 +21,7 @@ extern void ppu_initialize(const ppu_module&);
LOG_CHANNEL(sys_prx);
extern const std::unordered_map<std::string_view, int> g_prx_list
extern const std::map<std::string_view, int> g_prx_list
{
{ "libaacenc.sprx", 0 },
{ "libaacenc_spurs.sprx", 0 },
@ -205,17 +205,20 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
if (is_firmware_sprx)
{
// First condition, LLE for selected libs
ignore = g_cfg.core.load_libraries.get_set().count(name) == 0;
if (g_cfg.core.lib_loading != lib_loading_type::liblv2list && g_cfg.core.lib_loading != lib_loading_type::manual)
if (g_cfg.core.libraries_control.get_set().count(name + ":lle"))
{
// Override list setting condition for liblv2only
// For the other modes g_prx_list is a second condition which filters HLE selected libs by list setting
if (ignore || g_cfg.core.lib_loading == lib_loading_type::liblv2only)
{
ignore = g_prx_list.at(name) != 0;
}
// Force LLE
ignore = false;
}
else if (g_cfg.core.libraries_control.get_set().count(name + ":hle"))
{
// Force HLE
ignore = true;
}
else
{
// Use list
ignore = g_prx_list.at(name) != 0;
}
}
else if (vpath0.starts_with("/"))

View File

@ -1077,10 +1077,20 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
// Force LLVM recompiler
g_cfg.core.ppu_decoder.from_default();
// Force lib loading mode
g_cfg.core.lib_loading.from_string("Manually load selected libraries");
ensure(g_cfg.core.lib_loading == lib_loading_type::manual);
g_cfg.core.load_libraries.from_default();
// Force LLE lib loading mode
g_cfg.core.libraries_control.set_set([]()
{
std::set<std::string> set;
extern const std::map<std::string_view, int> g_prx_list;
for (const auto& lib : g_prx_list)
{
set.emplace(std::string(lib.first) + ":lle");
}
return set;
}());
// Fake arg (workaround)
argv.resize(1);
@ -1361,10 +1371,6 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
const std::string game_path = "/dev_hdd0/game/" + m_path.substr(hdd0_game.size(), 9);
sys_log.notice("Forcing manual lib loading mode");
g_cfg.core.lib_loading.from_string(fmt::format("%s", lib_loading_type::manual));
g_cfg.core.load_libraries.from_list({});
argv.resize(9);
argv[0] = "/dev_flash/ps1emu/ps1_newemu.self";
argv[1] = m_title_id + "_mc1.VM1"; // virtual mc 1 /dev_hdd0/savedata/vmc/%argv[1]%

View File

@ -60,9 +60,8 @@ struct cfg_root : cfg::node
cfg::_int<-64, 64> stub_ppu_traps{ this, "Stub PPU Traps", 0, true }; // Hack, skip PPU traps for rare cases where the trap is continueable (specify relative instructions to skip)
cfg::_bool debug_console_mode{ this, "Debug Console Mode", false }; // Debug console emulation, not recommended
cfg::_enum<lib_loading_type> lib_loading{ this, "Lib Loader", lib_loading_type::liblv2only };
cfg::_bool hook_functions{ this, "Hook static functions" };
cfg::set_entry load_libraries{ this, "Load libraries" };
cfg::set_entry libraries_control{ this, "Libraries Control" }; // Override HLE/LLE behaviour of selected libs
cfg::_bool hle_lwmutex{ this, "HLE lwmutex" }; // Force alternative lwmutex/lwcond implementation
cfg::uint64 spu_llvm_lower_bound{ this, "SPU LLVM Lower Bound" };
cfg::uint64 spu_llvm_upper_bound{ this, "SPU LLVM Upper Bound", 0xffffffffffffffff };

View File

@ -355,24 +355,6 @@ void fmt_class_string<move_handler>::format(std::string& out, u64 arg)
});
}
template <>
void fmt_class_string<lib_loading_type>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](lib_loading_type value)
{
switch (value)
{
case lib_loading_type::manual: return "Manually load selected libraries";
case lib_loading_type::hybrid: return "Load automatic and manual selection";
case lib_loading_type::liblv2only: return "Load liblv2.sprx only";
case lib_loading_type::liblv2both: return "Load liblv2.sprx and manual selection";
case lib_loading_type::liblv2list: return "Load liblv2.sprx and strict selection";
}
return unknown;
});
}
template <>
void fmt_class_string<ppu_decoder_type>::format(std::string& out, u64 arg)
{

View File

@ -22,15 +22,6 @@ enum class spu_block_size_type
giga,
};
enum class lib_loading_type
{
manual,
hybrid,
liblv2only,
liblv2both,
liblv2list,
};
enum class sleep_timers_accuracy_level
{
_as_host,

View File

@ -603,6 +603,7 @@ void emu_settings::EnhanceRadioButton(QButtonGroup* button_group, emu_settings_t
}
const QString selected = qstr(GetSetting(type));
const QString def = qstr(GetSettingDefault(type));
const QStringList options = GetSettingOptions(type);
if (button_group->buttons().count() < options.size())
@ -611,32 +612,51 @@ void emu_settings::EnhanceRadioButton(QButtonGroup* button_group, emu_settings_t
return;
}
bool found = false;
int def_pos = -1;
for (int i = 0; i < options.count(); i++)
{
const QString localized_setting = GetLocalizedSetting(options[i], type, i);
button_group->button(i)->setText(localized_setting);
if (options[i] == selected)
if (!found && options[i] == selected)
{
found = true;
button_group->button(i)->setChecked(true);
}
else if (def_pos == -1 && options[i] == def)
{
def_pos = i;
}
connect(button_group->button(i), &QAbstractButton::clicked, [=, this]()
{
SetSetting(type, sstr(options[i]));
});
}
if (!found)
{
ensure(def_pos >= 0);
cfg_log.error("EnhanceRadioButton '%s' tried to set an invalid value: %s. Setting to default: %s.", cfg_adapter::get_setting_name(type), sstr(selected), sstr(def));
m_broken_types.insert(type);
// Select the default option on invalid setting string
button_group->button(def_pos)->setChecked(true);
}
}
std::vector<std::string> emu_settings::GetLoadedLibraries()
std::vector<std::string> emu_settings::GetLibrariesControl()
{
return m_currentSettings["Core"]["Load libraries"].as<std::vector<std::string>, std::initializer_list<std::string>>({});
return m_currentSettings["Core"]["Libraries Control"].as<std::vector<std::string>, std::initializer_list<std::string>>({});
}
void emu_settings::SaveSelectedLibraries(const std::vector<std::string>& libs)
{
m_currentSettings["Core"]["Load libraries"] = libs;
m_currentSettings["Core"]["Libraries Control"] = libs;
}
QStringList emu_settings::GetSettingOptions(emu_settings_type type) const
@ -860,16 +880,6 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
case screen_quadrant::bottom_right: return tr("Bottom Right", "Performance overlay position");
}
break;
case emu_settings_type::LibLoadOptions:
switch (static_cast<lib_loading_type>(index))
{
case lib_loading_type::manual: return tr("Manually load selected libraries", "Libraries");
case lib_loading_type::hybrid: return tr("Load automatic and manual selection", "Libraries");
case lib_loading_type::liblv2only: return tr("Load liblv2.sprx only", "Libraries");
case lib_loading_type::liblv2both: return tr("Load liblv2.sprx and manual selection", "Libraries");
case lib_loading_type::liblv2list: return tr("Load liblv2.sprx and strict selection", "Libraries");
}
break;
case emu_settings_type::PPUDecoder:
switch (static_cast<ppu_decoder_type>(index))
{

View File

@ -56,7 +56,7 @@ public:
/** Connects a button group with the target settings type*/
void EnhanceRadioButton(QButtonGroup* button_group, emu_settings_type type);
std::vector<std::string> GetLoadedLibraries();
std::vector<std::string> GetLibrariesControl();
void SaveSelectedLibraries(const std::vector<std::string>& libs);
/** Returns the valid options for a given setting.*/

View File

@ -10,7 +10,6 @@ enum class emu_settings_type
// Core
PPUDecoder,
SPUDecoder,
LibLoadOptions,
HookStaticFuncs,
EnableThreadScheduler,
LowerSPUThreadPrio,
@ -156,7 +155,6 @@ static const QMap<emu_settings_type, cfg_location> settings_location =
// Core Tab
{ emu_settings_type::PPUDecoder, { "Core", "PPU Decoder"}},
{ emu_settings_type::SPUDecoder, { "Core", "SPU Decoder"}},
{ emu_settings_type::LibLoadOptions, { "Core", "Lib Loader"}},
{ emu_settings_type::HookStaticFuncs, { "Core", "Hook static functions"}},
{ emu_settings_type::EnableThreadScheduler, { "Core", "Enable thread scheduler"}},
{ emu_settings_type::LowerSPUThreadPrio, { "Core", "Lower SPU thread priority"}},

View File

@ -44,6 +44,8 @@ inline std::string sstr(const QString& _in) { return _in.toStdString(); }
inline std::string sstr(const QVariant& _in) { return sstr(_in.toString()); }
inline QString qsv(std::string_view sv) { return QString(sv.data()); }
extern const std::map<std::string_view, int> g_prx_list;
settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, const int& tab_index, QWidget *parent, const GameInfo* game)
: QDialog(parent)
, m_tab_index(tab_index)
@ -104,16 +106,28 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
const auto apply_configs = [this, use_discord_old = m_use_discord, discord_state_old = m_discord_state](bool do_exit)
{
std::set<std::string> selectedlle;
std::set<std::string> selected;
for (int i = 0; i < ui->lleList->count(); ++i)
{
const auto& item = ui->lleList->item(i);
if (item->checkState() != Qt::CheckState::Unchecked)
{
selectedlle.emplace(sstr(item->text()));
// suffix indicates forced HLE mode
selected.emplace(sstr(item->text()) + ":hle");
}
}
std::vector<std::string> selected_ls = std::vector<std::string>(selectedlle.begin(), selectedlle.end());
for (int i = 0; i < ui->hleList->count(); ++i)
{
const auto& item = ui->hleList->item(i);
if (item->checkState() != Qt::CheckState::Unchecked)
{
// suffix indicates forced LLE mode
selected.emplace(sstr(item->text()) + ":lle");
}
}
std::vector<std::string> selected_ls(selected.begin(), selected.end());
m_emu_settings->SaveSelectedLibraries(selected_ls);
m_emu_settings->SaveSettings();
@ -1012,78 +1026,60 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
SubscribeTooltip(ui->gb_wakeupDelay, tooltips.settings.wake_up_delay);
}
// lib options tool tips
SubscribeTooltip(ui->lib_manu, tooltips.settings.libraries_manual);
SubscribeTooltip(ui->lib_both, tooltips.settings.libraries_both);
SubscribeTooltip(ui->lib_lv2, tooltips.settings.libraries_liblv2);
SubscribeTooltip(ui->lib_lv2b, tooltips.settings.libraries_liblv2both);
SubscribeTooltip(ui->lib_lv2l, tooltips.settings.libraries_liblv2list);
// creating this in ui file keeps scrambling the order...
QButtonGroup *lib_mode_bg = new QButtonGroup(this);
lib_mode_bg->addButton(ui->lib_manu, static_cast<int>(lib_loading_type::manual));
lib_mode_bg->addButton(ui->lib_both, static_cast<int>(lib_loading_type::hybrid));
lib_mode_bg->addButton(ui->lib_lv2, static_cast<int>(lib_loading_type::liblv2only));
lib_mode_bg->addButton(ui->lib_lv2b, static_cast<int>(lib_loading_type::liblv2both));
lib_mode_bg->addButton(ui->lib_lv2l, static_cast<int>(lib_loading_type::liblv2list));
m_emu_settings->EnhanceRadioButton(lib_mode_bg, emu_settings_type::LibLoadOptions);
std::vector<std::string> loadedLibs = m_emu_settings->GetLoadedLibraries();
std::vector<std::string> loadedLibs = m_emu_settings->GetLibrariesControl();
std::set<std::string_view> set(loadedLibs.begin(), loadedLibs.end());
for (const auto& lib : set)
{
QListWidgetItem* item = new QListWidgetItem(qsv(lib), ui->lleList);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag
item->setCheckState(Qt::Checked); // AND initialize check state
ui->lleList->addItem(item);
}
extern const std::unordered_map<std::string_view, int> g_prx_list;
for (const auto& lib : g_prx_list)
{
if (set.count(lib.first))
{
continue;
}
// -1: Override LLE
// 1: Override HLE
// 0: No override
const int res = static_cast<int>(set.count(std::string(lib.first) + ":hle") - set.count(std::string(lib.first) + ":lle"));
QListWidgetItem* item = new QListWidgetItem(qsv(lib.first), ui->lleList);
const auto list = (lib.second ? ui->hleList : ui->lleList);
QListWidgetItem* item = new QListWidgetItem(qsv(lib.first), list);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag
item->setCheckState(Qt::Unchecked); // AND initialize check state
ui->lleList->addItem(item);
// If no override selected (res=0), checkbox is unchecked
// Otherwise if the override does not match the default behaviour, checkbox is checked
item->setCheckState(res && res != (lib.second * 2 - 1) ? Qt::Checked : Qt::Unchecked); // AND initialize check state
item->setToolTip(!lib.second ? tooltips.settings.lib_default_lle :
(lib.first.starts_with("libsysutil") ? tr("Do not touch libsysutil libs, development purposes only, will cause game crashes.") : tooltips.settings.lib_default_hle));
list->addItem(item);
}
SubscribeTooltip(ui->lleList, tooltips.settings.lle_list);
SubscribeTooltip(ui->hleList, tooltips.settings.hle_list);
ui->searchBox->setPlaceholderText(tr("Search libraries", "Library search box"));
auto on_lib_button_clicked = [this](int id)
{
const bool enableLibs = id != static_cast<int>(lib_loading_type::liblv2only);
ui->searchBox->setEnabled(enableLibs);
ui->lleList->setEnabled(enableLibs);
};
auto on_search_box_text_changed = [this](QString text)
auto on_lib_state_changed = [this](QString text)
{
const QString search_term = text.toLower();
std::vector<QListWidgetItem*> items;
std::vector<QListWidgetItem*> items, items2;
// duplicate current items, we need clones to preserve checkstates
for (int i = 0; i < ui->lleList->count(); i++)
// Take current items
while (ui->lleList->count())
{
items.push_back(ui->lleList->item(i)->clone());
items.push_back(ui->lleList->takeItem(0));
}
while (ui->hleList->count())
{
items2.push_back(ui->hleList->takeItem(0));
}
// sort items: checked items first then alphabetical order
std::sort(items.begin(), items.end(), [](QListWidgetItem *i1, QListWidgetItem *i2)
const auto func = [](QListWidgetItem *i1, QListWidgetItem *i2)
{
return (i1->checkState() != i2->checkState()) ? (i1->checkState() > i2->checkState()) : (i1->text() < i2->text());
});
// refill library list
ui->lleList->clear();
};
std::sort(items.begin(), items.end(), func);
std::sort(items2.begin(), items2.end(), func);
for (uint i = 0; i < items.size(); i++)
{
@ -1092,11 +1088,35 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
// only show items filtered for search text
ui->lleList->setRowHidden(i, !items[i]->text().contains(search_term));
}
for (uint i = 0; i < items2.size(); i++)
{
ui->hleList->addItem(items2[i]);
// only show items filtered for search text
ui->hleList->setRowHidden(i, !items2[i]->text().contains(search_term));
}
};
// Sort libs
on_lib_state_changed({});
// Events
connect(lib_mode_bg, &QButtonGroup::idClicked, on_lib_button_clicked);
connect(ui->searchBox, &QLineEdit::textChanged, on_search_box_text_changed);
connect(ui->searchBox, &QLineEdit::textChanged, on_lib_state_changed);
connect(ui->resetLleList, &QAbstractButton::clicked, [this, on_lib_state_changed]()
{
for (int i = 0; i < ui->lleList->count(); i++)
{
ui->lleList->item(i)->setCheckState(Qt::Unchecked);
}
for (int i = 0; i < ui->hleList->count(); i++)
{
ui->hleList->item(i)->setCheckState(Qt::Unchecked);
}
on_lib_state_changed(ui->searchBox->text());
});
// enable multiselection (there must be a better way)
connect(ui->lleList, &QListWidget::itemChanged, [this](QListWidgetItem* item)
@ -1107,10 +1127,13 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
}
});
if (const int lib_mode_button_id = lib_mode_bg->checkedId(); lib_mode_button_id >= 0)
connect(ui->hleList, &QListWidget::itemChanged, [this](QListWidgetItem* item)
{
on_lib_button_clicked(lib_mode_button_id);
}
for (auto cb : ui->hleList->selectedItems())
{
cb->setCheckState(item->checkState());
}
});
// ______ _ _ _______ _
// | ____| | | | | |__ __| | |

View File

@ -1904,86 +1904,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_lib_settings">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Firmware Settings</string>
</property>
<layout class="QVBoxLayout" name="lib_settings_layout">
<item>
<widget class="QRadioButton" name="lib_manu">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Manually load selected libraries</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="lib_both">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Load automatic and manual selection</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="lib_lv2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Load liblv2.sprx only</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="lib_lv2b">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Load liblv2.sprx and manual selection</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="lib_lv2l">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Load liblv2.sprx and strict selection</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="advancedTabSpacerLeft">
<property name="orientation">
@ -2013,6 +1933,22 @@
<string>Firmware Libraries</string>
</property>
<layout class="QVBoxLayout" name="gb_libs_layout">
<item>
<widget class="QListWidget" name="hleList">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="lleList">
<property name="sizePolicy">
@ -2030,14 +1966,25 @@
</widget>
</item>
<item>
<widget class="QLineEdit" name="searchBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<layout class="QHBoxLayout" name="gb_lleLibs_layout" stretch="1,0">
<property name="spacing">
<number>6</number>
</property>
</widget>
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<widget class="QLineEdit" name="searchBox">
</widget>
</item>
<item>
<widget class="QPushButton" name="resetLleList">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <QString>
#include <QObject>
@ -19,11 +19,10 @@ public:
{
// advanced
const QString libraries_manual = tr("Allows the user to manually choose the LLE libraries to load.\nIf unsure, don't use this option. Nothing will work if you use this.");
const QString libraries_both = tr("Load libsysmodule.sprx and chosen list of libraries. Option for backward compatibility.\nIf unsure, don't use this option.");
const QString libraries_liblv2both = tr("Loads liblv2.sprx and chosen list of libraries.\nIf unsure, don't use this option.");
const QString libraries_liblv2list = tr("Loads liblv2.sprx and nothing but selected libraries.\nDon't use this option.");
const QString libraries_liblv2 = tr("This closely emulates how games can load and unload system module files on a real PlayStation 3.\nSome games require this.\nThis is the preferred option.");
const QString lle_list = tr("This libraries group are LLEd by default (lower list), selection will switch to HLE.\nLLE - \"Low Level Emulated\", function code inside the selected SPRX file will be used for exported firmware functions.\nHLE - \"High Level Emulated\", alternative emulator code will be used instead for exported firmware functions.\nIf choosen wrongly, games will not work! If unsure, leave both lists' selection empty.");
const QString hle_list = tr("This libraries group are HLEd by default (upper list), selection will switch to LLE.\nLLE - \"Low Level Emulated\", function code inside the selected SPRX file will be used for exported firmware functions.\nHLE - \"High Level Emulated\", alternative emulator code will be used instead for exported firmware functions.\nIf choosen wrongly, games will not work! If unsure, leave both lists' selection empty.");
const QString lib_default_hle = tr("Select to LLE. (HLE by default)");
const QString lib_default_lle = tr("Select to HLE. (LLE by default)");
const QString debug_console_mode = tr("Increases the amount of usable system memory to match a DECR console and more.\nCauses some software to behave differently than on retail hardware.");
const QString silence_all_logs = tr("Stop writing any logs after game startup. Don't use unless you believe it's necessary.");