forked from eden-emu/eden
		
	configure_cpu: Generate UI
This commit is contained in:
		
							parent
							
								
									c5a3642cb6
								
							
						
					
					
						commit
						daa31121ee
					
				
					 5 changed files with 92 additions and 188 deletions
				
			
		|  | @ -5,88 +5,83 @@ | ||||||
| #include <memory> | #include <memory> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/settings.h" | #include "common/settings.h" | ||||||
|  | #include "configuration/shared_widget.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "ui_configure_cpu.h" | #include "ui_configure_cpu.h" | ||||||
| #include "yuzu/configuration/configuration_shared.h" | #include "yuzu/configuration/configuration_shared.h" | ||||||
| #include "yuzu/configuration/configure_cpu.h" | #include "yuzu/configuration/configure_cpu.h" | ||||||
| 
 | 
 | ||||||
| ConfigureCpu::ConfigureCpu(const Core::System& system_, | ConfigureCpu::ConfigureCpu( | ||||||
|  |     const Core::System& system_, | ||||||
|     std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group, |     std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group, | ||||||
|                            QWidget* parent) |     const ConfigurationShared::TranslationMap& translations_, | ||||||
|     : Tab(group, parent), ui{std::make_unique<Ui::ConfigureCpu>()}, system{system_} { |     const ConfigurationShared::ComboboxTranslationMap& combobox_translations_, QWidget* parent) | ||||||
|  |     : Tab(group, parent), ui{std::make_unique<Ui::ConfigureCpu>()}, system{system_}, | ||||||
|  |       translations{translations_}, combobox_translations{combobox_translations_} { | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
| 
 | 
 | ||||||
|     SetupPerGameUI(); |     Setup(); | ||||||
| 
 | 
 | ||||||
|     SetConfiguration(); |     SetConfiguration(); | ||||||
| 
 | 
 | ||||||
|     connect(ui->accuracy, qOverload<int>(&QComboBox::currentIndexChanged), this, |     connect(accuracy_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||||||
|             &ConfigureCpu::UpdateGroup); |             &ConfigureCpu::UpdateGroup); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureCpu::~ConfigureCpu() = default; | ConfigureCpu::~ConfigureCpu() = default; | ||||||
| 
 | 
 | ||||||
| void ConfigureCpu::SetConfiguration() { | void ConfigureCpu::SetConfiguration() {} | ||||||
|  | void ConfigureCpu::Setup() { | ||||||
|     const bool runtime_lock = !system.IsPoweredOn(); |     const bool runtime_lock = !system.IsPoweredOn(); | ||||||
|  |     auto* accuracy_layout = ui->widget_accuracy->layout(); | ||||||
|  |     auto* unsafe_layout = ui->unsafe_widget->layout(); | ||||||
|  |     std::map<std::string, QWidget*> unsafe_hold{}; | ||||||
| 
 | 
 | ||||||
|     ui->accuracy->setEnabled(runtime_lock); |     std::forward_list<Settings::BasicSetting*> settings; | ||||||
|     ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock); |     const auto push = [&](Settings::Category category) { | ||||||
|     ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); |         for (const auto setting : Settings::values.linkage.by_category[category]) { | ||||||
|     ui->cpuopt_unsafe_ignore_standard_fpcr->setEnabled(runtime_lock); |             settings.push_front(setting); | ||||||
|     ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); |  | ||||||
|     ui->cpuopt_unsafe_fastmem_check->setEnabled(runtime_lock); |  | ||||||
|     ui->cpuopt_unsafe_ignore_global_monitor->setEnabled(runtime_lock); |  | ||||||
| 
 |  | ||||||
|     ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); |  | ||||||
|     ui->cpuopt_unsafe_reduce_fp_error->setChecked( |  | ||||||
|         Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()); |  | ||||||
|     ui->cpuopt_unsafe_ignore_standard_fpcr->setChecked( |  | ||||||
|         Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue()); |  | ||||||
|     ui->cpuopt_unsafe_inaccurate_nan->setChecked( |  | ||||||
|         Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()); |  | ||||||
|     ui->cpuopt_unsafe_fastmem_check->setChecked( |  | ||||||
|         Settings::values.cpuopt_unsafe_fastmem_check.GetValue()); |  | ||||||
|     ui->cpuopt_unsafe_ignore_global_monitor->setChecked( |  | ||||||
|         Settings::values.cpuopt_unsafe_ignore_global_monitor.GetValue()); |  | ||||||
| 
 |  | ||||||
|     if (Settings::IsConfiguringGlobal()) { |  | ||||||
|         ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue())); |  | ||||||
|     } else { |  | ||||||
|         ConfigurationShared::SetPerGameSetting(ui->accuracy, &Settings::values.cpu_accuracy); |  | ||||||
|         ConfigurationShared::SetHighlight(ui->widget_accuracy, |  | ||||||
|                                           !Settings::values.cpu_accuracy.UsingGlobal()); |  | ||||||
|         } |         } | ||||||
|     UpdateGroup(ui->accuracy->currentIndex()); |     }; | ||||||
|  | 
 | ||||||
|  |     push(Settings::Category::Cpu); | ||||||
|  |     push(Settings::Category::CpuUnsafe); | ||||||
|  | 
 | ||||||
|  |     for (const auto setting : settings) { | ||||||
|  |         auto* widget = new ConfigurationShared::Widget(setting, translations, combobox_translations, | ||||||
|  |                                                        this, runtime_lock, apply_funcs); | ||||||
|  | 
 | ||||||
|  |         if (!widget->Valid()) { | ||||||
|  |             delete widget; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (setting->Id() == Settings::values.cpu_accuracy.Id()) { | ||||||
|  |             accuracy_layout->addWidget(widget); | ||||||
|  |             accuracy_combobox = widget->combobox; | ||||||
|  |         } else { | ||||||
|  |             unsafe_hold.insert({setting->GetLabel(), widget}); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (const auto& [label, widget] : unsafe_hold) { | ||||||
|  |         unsafe_layout->addWidget(widget); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     UpdateGroup(accuracy_combobox->currentIndex()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigureCpu::UpdateGroup(int index) { | void ConfigureCpu::UpdateGroup(int index) { | ||||||
|     if (!Settings::IsConfiguringGlobal()) { |     const auto accuracy = static_cast<Settings::CPUAccuracy>( | ||||||
|         index -= ConfigurationShared::USE_GLOBAL_OFFSET; |         combobox_translations.at(typeid(Settings::CPUAccuracy))[index].first); | ||||||
|     } |  | ||||||
|     const auto accuracy = static_cast<Settings::CPUAccuracy>(index); |  | ||||||
|     ui->unsafe_group->setVisible(accuracy == Settings::CPUAccuracy::Unsafe); |     ui->unsafe_group->setVisible(accuracy == Settings::CPUAccuracy::Unsafe); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigureCpu::ApplyConfiguration() { | void ConfigureCpu::ApplyConfiguration() { | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpu_accuracy, ui->accuracy); |     const bool is_powered_on = system.IsPoweredOn(); | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma, |     for (const auto& apply_func : apply_funcs) { | ||||||
|                                              ui->cpuopt_unsafe_unfuse_fma, |         apply_func(is_powered_on); | ||||||
|                                              cpuopt_unsafe_unfuse_fma); |     } | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_reduce_fp_error, |  | ||||||
|                                              ui->cpuopt_unsafe_reduce_fp_error, |  | ||||||
|                                              cpuopt_unsafe_reduce_fp_error); |  | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_standard_fpcr, |  | ||||||
|                                              ui->cpuopt_unsafe_ignore_standard_fpcr, |  | ||||||
|                                              cpuopt_unsafe_ignore_standard_fpcr); |  | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_inaccurate_nan, |  | ||||||
|                                              ui->cpuopt_unsafe_inaccurate_nan, |  | ||||||
|                                              cpuopt_unsafe_inaccurate_nan); |  | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_fastmem_check, |  | ||||||
|                                              ui->cpuopt_unsafe_fastmem_check, |  | ||||||
|                                              cpuopt_unsafe_fastmem_check); |  | ||||||
|     ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_ignore_global_monitor, |  | ||||||
|                                              ui->cpuopt_unsafe_ignore_global_monitor, |  | ||||||
|                                              cpuopt_unsafe_ignore_global_monitor); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigureCpu::changeEvent(QEvent* event) { | void ConfigureCpu::changeEvent(QEvent* event) { | ||||||
|  | @ -100,32 +95,3 @@ void ConfigureCpu::changeEvent(QEvent* event) { | ||||||
| void ConfigureCpu::RetranslateUI() { | void ConfigureCpu::RetranslateUI() { | ||||||
|     ui->retranslateUi(this); |     ui->retranslateUi(this); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| void ConfigureCpu::SetupPerGameUI() { |  | ||||||
|     if (Settings::IsConfiguringGlobal()) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ConfigurationShared::SetColoredComboBox( |  | ||||||
|         ui->accuracy, ui->widget_accuracy, |  | ||||||
|         static_cast<u32>(Settings::values.cpu_accuracy.GetValue(true))); |  | ||||||
| 
 |  | ||||||
|     ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_unfuse_fma, |  | ||||||
|                                             Settings::values.cpuopt_unsafe_unfuse_fma, |  | ||||||
|                                             cpuopt_unsafe_unfuse_fma); |  | ||||||
|     ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_reduce_fp_error, |  | ||||||
|                                             Settings::values.cpuopt_unsafe_reduce_fp_error, |  | ||||||
|                                             cpuopt_unsafe_reduce_fp_error); |  | ||||||
|     ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_standard_fpcr, |  | ||||||
|                                             Settings::values.cpuopt_unsafe_ignore_standard_fpcr, |  | ||||||
|                                             cpuopt_unsafe_ignore_standard_fpcr); |  | ||||||
|     ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_inaccurate_nan, |  | ||||||
|                                             Settings::values.cpuopt_unsafe_inaccurate_nan, |  | ||||||
|                                             cpuopt_unsafe_inaccurate_nan); |  | ||||||
|     ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_fastmem_check, |  | ||||||
|                                             Settings::values.cpuopt_unsafe_fastmem_check, |  | ||||||
|                                             cpuopt_unsafe_fastmem_check); |  | ||||||
|     ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_ignore_global_monitor, |  | ||||||
|                                             Settings::values.cpuopt_unsafe_ignore_global_monitor, |  | ||||||
|                                             cpuopt_unsafe_ignore_global_monitor); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -19,6 +19,8 @@ class ConfigureCpu : public ConfigurationShared::Tab { | ||||||
| public: | public: | ||||||
|     explicit ConfigureCpu(const Core::System& system_, |     explicit ConfigureCpu(const Core::System& system_, | ||||||
|                           std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group, |                           std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group, | ||||||
|  |                           const ConfigurationShared::TranslationMap& translations, | ||||||
|  |                           const ConfigurationShared::ComboboxTranslationMap& combobox_translations, | ||||||
|                           QWidget* parent = nullptr); |                           QWidget* parent = nullptr); | ||||||
|     ~ConfigureCpu() override; |     ~ConfigureCpu() override; | ||||||
| 
 | 
 | ||||||
|  | @ -31,7 +33,7 @@ private: | ||||||
| 
 | 
 | ||||||
|     void UpdateGroup(int index); |     void UpdateGroup(int index); | ||||||
| 
 | 
 | ||||||
|     void SetupPerGameUI(); |     void Setup(); | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<Ui::ConfigureCpu> ui; |     std::unique_ptr<Ui::ConfigureCpu> ui; | ||||||
| 
 | 
 | ||||||
|  | @ -43,4 +45,11 @@ private: | ||||||
|     ConfigurationShared::CheckState cpuopt_unsafe_ignore_global_monitor; |     ConfigurationShared::CheckState cpuopt_unsafe_ignore_global_monitor; | ||||||
| 
 | 
 | ||||||
|     const Core::System& system; |     const Core::System& system; | ||||||
|  | 
 | ||||||
|  |     const ConfigurationShared::TranslationMap& translations; | ||||||
|  |     const ConfigurationShared::ComboboxTranslationMap& combobox_translations; | ||||||
|  | 
 | ||||||
|  |     std::forward_list<std::function<void(bool)>> apply_funcs{}; | ||||||
|  | 
 | ||||||
|  |     QComboBox* accuracy_combobox; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -27,38 +27,19 @@ | ||||||
|        <layout class="QVBoxLayout"> |        <layout class="QVBoxLayout"> | ||||||
|         <item> |         <item> | ||||||
|          <widget class="QWidget" name="widget_accuracy" native="true"> |          <widget class="QWidget" name="widget_accuracy" native="true"> | ||||||
|           <layout class="QHBoxLayout" name="layout_accuracy"> |           <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|            <item> |            <property name="leftMargin"> | ||||||
|             <widget class="QLabel" name="label_accuracy"> |             <number>0</number> | ||||||
|              <property name="text"> |  | ||||||
|               <string>Accuracy:</string> |  | ||||||
|            </property> |            </property> | ||||||
|             </widget> |            <property name="topMargin"> | ||||||
|            </item> |             <number>0</number> | ||||||
|            <item> |  | ||||||
|             <widget class="QComboBox" name="accuracy"> |  | ||||||
|              <item> |  | ||||||
|               <property name="text"> |  | ||||||
|                <string>Auto</string> |  | ||||||
|            </property> |            </property> | ||||||
|              </item> |            <property name="rightMargin"> | ||||||
|              <item> |             <number>0</number> | ||||||
|               <property name="text"> |  | ||||||
|                <string>Accurate</string> |  | ||||||
|            </property> |            </property> | ||||||
|              </item> |            <property name="bottomMargin"> | ||||||
|              <item> |             <number>0</number> | ||||||
|               <property name="text"> |  | ||||||
|                <string>Unsafe</string> |  | ||||||
|            </property> |            </property> | ||||||
|              </item> |  | ||||||
|              <item> |  | ||||||
|               <property name="text"> |  | ||||||
|                <string>Paranoid (disables most optimizations)</string> |  | ||||||
|               </property> |  | ||||||
|              </item> |  | ||||||
|             </widget> |  | ||||||
|            </item> |  | ||||||
|           </layout> |           </layout> | ||||||
|          </widget> |          </widget> | ||||||
|         </item> |         </item> | ||||||
|  | @ -96,75 +77,21 @@ | ||||||
|          </widget> |          </widget> | ||||||
|         </item> |         </item> | ||||||
|         <item> |         <item> | ||||||
|          <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma"> |          <widget class="QWidget" name="unsafe_widget" native="true"> | ||||||
|           <property name="toolTip"> |           <layout class="QVBoxLayout" name="unsafe_layout"> | ||||||
|            <string> |            <property name="leftMargin"> | ||||||
|             <div>This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.</div> |             <number>0</number> | ||||||
|            </string> |  | ||||||
|            </property> |            </property> | ||||||
|           <property name="text"> |            <property name="topMargin"> | ||||||
|            <string>Unfuse FMA (improve performance on CPUs without FMA)</string> |             <number>0</number> | ||||||
|            </property> |            </property> | ||||||
|          </widget> |            <property name="rightMargin"> | ||||||
|         </item> |             <number>0</number> | ||||||
|         <item> |  | ||||||
|          <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error"> |  | ||||||
|           <property name="toolTip"> |  | ||||||
|            <string> |  | ||||||
|             <div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div> |  | ||||||
|            </string> |  | ||||||
|            </property> |            </property> | ||||||
|           <property name="text"> |            <property name="bottomMargin"> | ||||||
|            <string>Faster FRSQRTE and FRECPE</string> |             <number>0</number> | ||||||
|           </property> |  | ||||||
|          </widget> |  | ||||||
|         </item> |  | ||||||
|         <item> |  | ||||||
|          <widget class="QCheckBox" name="cpuopt_unsafe_ignore_standard_fpcr"> |  | ||||||
|           <property name="toolTip"> |  | ||||||
|            <string> |  | ||||||
|             <div>This option improves the speed of 32 bits ASIMD floating-point functions by running with incorrect rounding modes.</div> |  | ||||||
|            </string> |  | ||||||
|           </property> |  | ||||||
|           <property name="text"> |  | ||||||
|            <string>Faster ASIMD instructions (32 bits only)</string> |  | ||||||
|           </property> |  | ||||||
|          </widget> |  | ||||||
|         </item> |  | ||||||
|         <item> |  | ||||||
|          <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan"> |  | ||||||
|           <property name="toolTip"> |  | ||||||
|            <string> |  | ||||||
|             <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> |  | ||||||
|            </string> |  | ||||||
|           </property> |  | ||||||
|           <property name="text"> |  | ||||||
|            <string>Inaccurate NaN handling</string> |  | ||||||
|           </property> |  | ||||||
|          </widget> |  | ||||||
|         </item> |  | ||||||
|         <item> |  | ||||||
|          <widget class="QCheckBox" name="cpuopt_unsafe_fastmem_check"> |  | ||||||
|           <property name="toolTip"> |  | ||||||
|            <string> |  | ||||||
|             <div>This option improves speed by eliminating a safety check before every memory read/write in guest. Disabling it may allow a game to read/write the emulator's memory.</div> |  | ||||||
|            </string> |  | ||||||
|           </property> |  | ||||||
|           <property name="text"> |  | ||||||
|            <string>Disable address space checks</string> |  | ||||||
|           </property> |  | ||||||
|          </widget> |  | ||||||
|         </item> |  | ||||||
|         <item> |  | ||||||
|          <widget class="QCheckBox" name="cpuopt_unsafe_ignore_global_monitor"> |  | ||||||
|           <property name="toolTip"> |  | ||||||
|            <string> |  | ||||||
|             <div>This option improves speed by relying only on the semantics of cmpxchg to ensure safety of exclusive access instructions. Please note this may result in deadlocks and other race conditions.</div> |  | ||||||
|            </string> |  | ||||||
|           </property> |  | ||||||
|           <property name="text"> |  | ||||||
|            <string>Ignore global monitor</string> |  | ||||||
|            </property> |            </property> | ||||||
|  |           </layout> | ||||||
|          </widget> |          </widget> | ||||||
|         </item> |         </item> | ||||||
|        </layout> |        </layout> | ||||||
|  |  | ||||||
|  | @ -37,7 +37,8 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, | ||||||
|       combobox_translations{ConfigurationShared::ComboboxEnumeration(this)}, |       combobox_translations{ConfigurationShared::ComboboxEnumeration(this)}, | ||||||
|       audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *translations, |       audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *translations, | ||||||
|                                                  *combobox_translations, this)}, |                                                  *combobox_translations, this)}, | ||||||
|       cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, this)}, |       cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, *translations, | ||||||
|  |                                              *combobox_translations, this)}, | ||||||
|       debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, |       debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, | ||||||
|       filesystem_tab{std::make_unique<ConfigureFilesystem>(this)}, |       filesystem_tab{std::make_unique<ConfigureFilesystem>(this)}, | ||||||
|       general_tab{std::make_unique<ConfigureGeneral>(system_, nullptr, *translations, |       general_tab{std::make_unique<ConfigureGeneral>(system_, nullptr, *translations, | ||||||
|  |  | ||||||
|  | @ -53,7 +53,8 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st | ||||||
|     addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); |     addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); | ||||||
|     audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *translations, |     audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *translations, | ||||||
|                                                  *combobox_translations, this); |                                                  *combobox_translations, this); | ||||||
|     cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, this); |     cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, *translations, | ||||||
|  |                                              *combobox_translations, this); | ||||||
|     graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>( |     graphics_advanced_tab = std::make_unique<ConfigureGraphicsAdvanced>( | ||||||
|         system_, tab_group, *translations, *combobox_translations, this); |         system_, tab_group, *translations, *combobox_translations, this); | ||||||
|     graphics_tab = std::make_unique<ConfigureGraphics>( |     graphics_tab = std::make_unique<ConfigureGraphics>( | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 lat9nq
						lat9nq