mirror of
https://github.com/Lime3DS/Lime3DS
synced 2024-10-31 12:17:51 +00:00
Remove all edits not in the scope of this PR
This commit is contained in:
parent
373efd3158
commit
98e669cf00
14 changed files with 13 additions and 182 deletions
|
@ -28,8 +28,6 @@ add_executable(citra-qt
|
||||||
configuration/configure_system.h
|
configuration/configure_system.h
|
||||||
configuration/configure_web.cpp
|
configuration/configure_web.cpp
|
||||||
configuration/configure_web.h
|
configuration/configure_web.h
|
||||||
debugger/console.h
|
|
||||||
debugger/console.cpp
|
|
||||||
debugger/graphics/graphics.cpp
|
debugger/graphics/graphics.cpp
|
||||||
debugger/graphics/graphics.h
|
debugger/graphics/graphics.h
|
||||||
debugger/graphics/graphics_breakpoint_observer.cpp
|
debugger/graphics/graphics_breakpoint_observer.cpp
|
||||||
|
@ -140,14 +138,6 @@ if (APPLE)
|
||||||
target_sources(citra-qt PRIVATE ${MACOSX_ICON})
|
target_sources(citra-qt PRIVATE ${MACOSX_ICON})
|
||||||
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE TRUE)
|
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE TRUE)
|
||||||
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
|
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
|
||||||
elseif(WIN32)
|
|
||||||
# compile as a win32 gui application instead of a console application
|
|
||||||
target_link_libraries(citra-qt PRIVATE Qt5::WinMain)
|
|
||||||
if(MSVC)
|
|
||||||
set_target_properties(citra-qt PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
|
||||||
elseif(MINGW)
|
|
||||||
set_target_properties(citra-qt PROPERTIES LINK_FLAGS_RELEASE "-mwindows")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
create_target_directory_groups(citra-qt)
|
create_target_directory_groups(citra-qt)
|
||||||
|
|
|
@ -224,7 +224,6 @@ void Config::ReadValues() {
|
||||||
UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
|
UISettings::values.confirm_before_closing = qt_config->value("confirmClose", true).toBool();
|
||||||
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
|
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
|
||||||
UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
|
UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
|
||||||
UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
|
|
||||||
|
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
|
@ -366,7 +365,6 @@ void Config::SaveValues() {
|
||||||
qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
|
qt_config->setValue("confirmClose", UISettings::values.confirm_before_closing);
|
||||||
qt_config->setValue("firstStart", UISettings::values.first_start);
|
qt_config->setValue("firstStart", UISettings::values.first_start);
|
||||||
qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
|
qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
|
||||||
qt_config->setValue("showConsole", UISettings::values.show_console);
|
|
||||||
|
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,27 +2,13 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#include <QUrl>
|
|
||||||
|
|
||||||
#include "citra_qt/configuration/configure_debug.h"
|
#include "citra_qt/configuration/configure_debug.h"
|
||||||
#include "citra_qt/debugger/console.h"
|
|
||||||
#include "citra_qt/ui_settings.h"
|
|
||||||
#include "common/file_util.h"
|
|
||||||
#include "common/logging/backend.h"
|
|
||||||
#include "common/logging/filter.h"
|
|
||||||
#include "common/logging/log.h"
|
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "ui_configure_debug.h"
|
#include "ui_configure_debug.h"
|
||||||
|
|
||||||
ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
|
ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureDebug) {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
this->setConfiguration();
|
this->setConfiguration();
|
||||||
connect(ui->open_log_button, &QPushButton::pressed, []() {
|
|
||||||
QString path = QString::fromStdString(FileUtil::GetUserPath(D_LOGS_IDX));
|
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigureDebug::~ConfigureDebug() {}
|
ConfigureDebug::~ConfigureDebug() {}
|
||||||
|
@ -31,20 +17,11 @@ void ConfigureDebug::setConfiguration() {
|
||||||
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
|
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
|
||||||
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
|
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub);
|
||||||
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
|
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port);
|
||||||
ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
|
||||||
ui->toggle_console->setChecked(UISettings::values.show_console);
|
|
||||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureDebug::applyConfiguration() {
|
void ConfigureDebug::applyConfiguration() {
|
||||||
Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
|
Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
|
||||||
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
|
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
|
||||||
UISettings::values.show_console = ui->toggle_console->isChecked();
|
|
||||||
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
|
||||||
Debugger::ToggleConsole();
|
|
||||||
Log::Filter filter;
|
|
||||||
filter.ParseFilterString(Settings::values.log_filter);
|
|
||||||
Log::SetFilter(&filter);
|
|
||||||
Settings::Apply();
|
Settings::Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,47 +72,6 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
|
||||||
<property name="title">
|
|
||||||
<string>Logging</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Global Log Filter</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="log_filter_edit"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="toggle_console">
|
|
||||||
<property name="text">
|
|
||||||
<string>Show Log Console (Windows Only)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="open_log_button">
|
|
||||||
<property name="text">
|
|
||||||
<string>Open Log Location</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
// Copyright 2018 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include <wincon.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "citra_qt/debugger/console.h"
|
|
||||||
#include "citra_qt/ui_settings.h"
|
|
||||||
|
|
||||||
namespace Debugger {
|
|
||||||
void ToggleConsole() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (UISettings::values.show_console) {
|
|
||||||
if (AllocConsole()) {
|
|
||||||
freopen_s((FILE**)stdin, "CONIN$", "r", stdin);
|
|
||||||
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
|
|
||||||
freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (FreeConsole()) {
|
|
||||||
// In order to close the console, we have to also detach the streams on it.
|
|
||||||
// Just redirect them to NUL if there is no console window
|
|
||||||
freopen_s((FILE**)stdin, "NUL", "r", stdin);
|
|
||||||
freopen_s((FILE**)stdout, "NUL", "w", stdout);
|
|
||||||
freopen_s((FILE**)stderr, "NUL", "w", stderr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
} // namespace Debugger
|
|
|
@ -1,14 +0,0 @@
|
||||||
// Copyright 2018 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Debugger {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the WINAPI to hide or show the stderr console. This function is a placeholder until we can
|
|
||||||
* get a real qt logging window which would work for all platforms.
|
|
||||||
*/
|
|
||||||
void ToggleConsole();
|
|
||||||
} // namespace Debugger
|
|
|
@ -56,8 +56,6 @@ struct Values {
|
||||||
std::vector<Shortcut> shortcuts;
|
std::vector<Shortcut> shortcuts;
|
||||||
|
|
||||||
uint32_t callout_flags;
|
uint32_t callout_flags;
|
||||||
|
|
||||||
bool show_console;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Values values;
|
extern Values values;
|
||||||
|
|
|
@ -36,12 +36,8 @@
|
||||||
#define SDMC_DIR "sdmc"
|
#define SDMC_DIR "sdmc"
|
||||||
#define NAND_DIR "nand"
|
#define NAND_DIR "nand"
|
||||||
#define SYSDATA_DIR "sysdata"
|
#define SYSDATA_DIR "sysdata"
|
||||||
#define LOG_DIR "log"
|
|
||||||
|
|
||||||
// Filenames
|
// Filenames
|
||||||
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
|
|
||||||
#define LOG_FILE "citra_log.txt"
|
|
||||||
|
|
||||||
// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
|
// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
|
||||||
#define EMU_CONFIG "emu.ini"
|
#define EMU_CONFIG "emu.ini"
|
||||||
#define DEBUGGER_CONFIG "debugger.ini"
|
#define DEBUGGER_CONFIG "debugger.ini"
|
||||||
|
|
|
@ -715,8 +715,6 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new
|
||||||
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
|
||||||
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
|
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
|
||||||
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
|
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
|
||||||
// TODO: Put the logs in a better location for each OS
|
|
||||||
paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPath.empty()) {
|
if (!newPath.empty()) {
|
||||||
|
@ -803,8 +801,8 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||||
|
|
||||||
IOFile::IOFile() {}
|
IOFile::IOFile() {}
|
||||||
|
|
||||||
IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
|
IOFile::IOFile(const std::string& filename, const char openmode[]) {
|
||||||
Open(filename, openmode, flags);
|
Open(filename, openmode);
|
||||||
}
|
}
|
||||||
|
|
||||||
IOFile::~IOFile() {
|
IOFile::~IOFile() {
|
||||||
|
@ -825,16 +823,11 @@ void IOFile::Swap(IOFile& other) {
|
||||||
std::swap(m_good, other.m_good);
|
std::swap(m_good, other.m_good);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IOFile::Open(const std::string& filename, const char openmode[], int flags) {
|
bool IOFile::Open(const std::string& filename, const char openmode[]) {
|
||||||
Close();
|
Close();
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (flags != 0) {
|
|
||||||
m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
|
|
||||||
Common::UTF8ToUTF16W(openmode).c_str(), flags);
|
|
||||||
} else {
|
|
||||||
_wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
|
_wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
|
||||||
Common::UTF8ToUTF16W(openmode).c_str());
|
Common::UTF8ToUTF16W(openmode).c_str());
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
m_file = fopen(filename.c_str(), openmode);
|
m_file = fopen(filename.c_str(), openmode);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -156,8 +156,7 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||||
class IOFile : public NonCopyable {
|
class IOFile : public NonCopyable {
|
||||||
public:
|
public:
|
||||||
IOFile();
|
IOFile();
|
||||||
/// Opens the file. flags is for windows shared file settings and are ignored on other oses
|
IOFile(const std::string& filename, const char openmode[]);
|
||||||
IOFile(const std::string& filename, const char openmode[], int flags = 0);
|
|
||||||
|
|
||||||
~IOFile();
|
~IOFile();
|
||||||
|
|
||||||
|
@ -166,7 +165,7 @@ public:
|
||||||
|
|
||||||
void Swap(IOFile& other);
|
void Swap(IOFile& other);
|
||||||
|
|
||||||
bool Open(const std::string& filename, const char openmode[], int flags = 0);
|
bool Open(const std::string& filename, const char openmode[]);
|
||||||
bool Close();
|
bool Close();
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -225,10 +224,6 @@ public:
|
||||||
return WriteArray(&object, 1);
|
return WriteArray(&object, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t WriteString(const std::string& str) {
|
|
||||||
return WriteArray(str.c_str(), str.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsOpen() const {
|
bool IsOpen() const {
|
||||||
return nullptr != m_file;
|
return nullptr != m_file;
|
||||||
}
|
}
|
||||||
|
|
|
@ -484,4 +484,5 @@ const char* TrimSourcePath(const char* path, const char* root) {
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
@ -37,10 +36,6 @@ public:
|
||||||
T& Front() const {
|
T& Front() const {
|
||||||
return read_ptr->current;
|
return read_ptr->current;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Push data to the queue. If NeedSize=True then Push will notify the waiting consumer thread
|
|
||||||
*/
|
|
||||||
template <typename Arg>
|
template <typename Arg>
|
||||||
void Push(Arg&& t) {
|
void Push(Arg&& t) {
|
||||||
// create the element, add it to the queue
|
// create the element, add it to the queue
|
||||||
|
@ -50,11 +45,8 @@ public:
|
||||||
ElementPtr* new_ptr = new ElementPtr();
|
ElementPtr* new_ptr = new ElementPtr();
|
||||||
write_ptr->next.store(new_ptr, std::memory_order_release);
|
write_ptr->next.store(new_ptr, std::memory_order_release);
|
||||||
write_ptr = new_ptr;
|
write_ptr = new_ptr;
|
||||||
if (NeedSize) {
|
if (NeedSize)
|
||||||
std::lock_guard<std::mutex> lock(size_lock);
|
|
||||||
size++;
|
size++;
|
||||||
size_cv.notify_one();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pop() {
|
void Pop() {
|
||||||
|
@ -83,25 +75,6 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits up to timeout for data to be Pushed to the queue. Push uses a condition variable to
|
|
||||||
* signal the waiting thread, but only if NeedSize = true. Returns false if the timeout is
|
|
||||||
* triggered. If the condition variable is signalled, returns the value from Pop
|
|
||||||
* @param T In parameter to store the value if this method returns true
|
|
||||||
* @param timeout Time in milliseconds to wait for a signal from a Push
|
|
||||||
*/
|
|
||||||
bool PopWait(T& t, u64 timeout = 500) {
|
|
||||||
if (NeedSize) {
|
|
||||||
std::unique_lock<std::mutex> lock(size_lock);
|
|
||||||
if (size_cv.wait_for(lock, std::chrono::milliseconds(timeout),
|
|
||||||
[& size = size] { return size > 0; })) {
|
|
||||||
return Pop(t);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return Pop(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// not thread-safe
|
// not thread-safe
|
||||||
void Clear() {
|
void Clear() {
|
||||||
size.store(0);
|
size.store(0);
|
||||||
|
@ -129,9 +102,6 @@ private:
|
||||||
ElementPtr* write_ptr;
|
ElementPtr* write_ptr;
|
||||||
ElementPtr* read_ptr;
|
ElementPtr* read_ptr;
|
||||||
std::atomic<u32> size;
|
std::atomic<u32> size;
|
||||||
|
|
||||||
std::mutex size_lock;
|
|
||||||
std::condition_variable size_cv;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// a simple thread-safe,
|
// a simple thread-safe,
|
||||||
|
|
|
@ -4,13 +4,14 @@
|
||||||
|
|
||||||
#include "audio_core/dsp_interface.h"
|
#include "audio_core/dsp_interface.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/emu_window.h"
|
|
||||||
#include "core/gdbstub/gdbstub.h"
|
#include "core/gdbstub/gdbstub.h"
|
||||||
#include "core/hle/service/hid/hid.h"
|
#include "core/hle/service/hid/hid.h"
|
||||||
#include "core/hle/service/ir/ir.h"
|
#include "core/hle/service/ir/ir.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
#include "core/frontend/emu_window.h"
|
||||||
|
|
||||||
namespace Settings {
|
namespace Settings {
|
||||||
|
|
||||||
Values values = {};
|
Values values = {};
|
||||||
|
|
|
@ -129,6 +129,8 @@ struct Values {
|
||||||
float bg_green;
|
float bg_green;
|
||||||
float bg_blue;
|
float bg_blue;
|
||||||
|
|
||||||
|
std::string log_filter;
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
std::string sink_id;
|
std::string sink_id;
|
||||||
bool enable_audio_stretching;
|
bool enable_audio_stretching;
|
||||||
|
@ -141,7 +143,6 @@ struct Values {
|
||||||
// Debugging
|
// Debugging
|
||||||
bool use_gdbstub;
|
bool use_gdbstub;
|
||||||
u16 gdbstub_port;
|
u16 gdbstub_port;
|
||||||
std::string log_filter;
|
|
||||||
|
|
||||||
// Movie
|
// Movie
|
||||||
std::string movie_play;
|
std::string movie_play;
|
||||||
|
|
Loading…
Reference in a new issue