mirror of
https://github.com/k4zmu2a/SpaceCadetPinball.git
synced 2024-11-22 17:00:18 +01:00
Compare commits
No commits in common. "8f34829b1e4756472ce6889f31eb875ce343fb05" and "43af97127b3ae6cb8fb4e9aaebc0585aa2427dca" have entirely different histories.
8f34829b1e
...
43af97127b
9 changed files with 49 additions and 101 deletions
|
@ -4,11 +4,9 @@
|
||||||
int Sound::num_channels;
|
int Sound::num_channels;
|
||||||
bool Sound::enabled_flag = false;
|
bool Sound::enabled_flag = false;
|
||||||
int* Sound::TimeStamps = nullptr;
|
int* Sound::TimeStamps = nullptr;
|
||||||
int Sound::Volume = MIX_MAX_VOLUME;
|
|
||||||
|
|
||||||
bool Sound::Init(int channels, bool enableFlag, int volume)
|
bool Sound::Init(int channels, bool enableFlag)
|
||||||
{
|
{
|
||||||
Volume = volume;
|
|
||||||
Mix_Init(MIX_INIT_MID_Proxy);
|
Mix_Init(MIX_INIT_MID_Proxy);
|
||||||
auto result = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024);
|
auto result = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024);
|
||||||
SetChannels(channels);
|
SetChannels(channels);
|
||||||
|
@ -82,11 +80,4 @@ void Sound::SetChannels(int channels)
|
||||||
delete[] TimeStamps;
|
delete[] TimeStamps;
|
||||||
TimeStamps = new int[num_channels]();
|
TimeStamps = new int[num_channels]();
|
||||||
Mix_AllocateChannels(num_channels);
|
Mix_AllocateChannels(num_channels);
|
||||||
SetVolume(Volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sound::SetVolume(int volume)
|
|
||||||
{
|
|
||||||
Volume = volume;
|
|
||||||
Mix_Volume(-1, volume);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
class Sound
|
class Sound
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static bool Init(int channels, bool enableFlag, int volume);
|
static bool Init(int channels, bool enableFlag);
|
||||||
static void Enable(bool enableFlag);
|
static void Enable(bool enableFlag);
|
||||||
static void Activate();
|
static void Activate();
|
||||||
static void Deactivate();
|
static void Deactivate();
|
||||||
|
@ -13,10 +13,8 @@ public:
|
||||||
static Mix_Chunk* LoadWaveFile(const std::string& lpName);
|
static Mix_Chunk* LoadWaveFile(const std::string& lpName);
|
||||||
static void FreeSound(Mix_Chunk* wave);
|
static void FreeSound(Mix_Chunk* wave);
|
||||||
static void SetChannels(int channels);
|
static void SetChannels(int channels);
|
||||||
static void SetVolume(int volume);
|
|
||||||
private:
|
private:
|
||||||
static int num_channels;
|
static int num_channels;
|
||||||
static bool enabled_flag;
|
static bool enabled_flag;
|
||||||
static int* TimeStamps;
|
static int* TimeStamps;
|
||||||
static int Volume;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -154,7 +154,7 @@ void high_score::RenderHighScoreDialog()
|
||||||
bool unused_open = true;
|
bool unused_open = true;
|
||||||
if (ImGui::BeginPopupModal("High Scores", &unused_open, ImGuiWindowFlags_AlwaysAutoResize))
|
if (ImGui::BeginPopupModal("High Scores", &unused_open, ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
{
|
{
|
||||||
if (ImGui::BeginTable("table1", 3, ImGuiTableFlags_Borders))
|
if (ImGui::BeginTable("table1", 3, 0))
|
||||||
{
|
{
|
||||||
char buf[36];
|
char buf[36];
|
||||||
ImGui::TableSetupColumn("Rank");
|
ImGui::TableSetupColumn("Rank");
|
||||||
|
@ -166,7 +166,7 @@ void high_score::RenderHighScoreDialog()
|
||||||
{
|
{
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
snprintf(buf, sizeof buf, "%d", row + 1);
|
snprintf(buf, sizeof buf, "%d", row);
|
||||||
ImGui::TextUnformatted(buf);
|
ImGui::TextUnformatted(buf);
|
||||||
|
|
||||||
auto currentRow = &dlg_hst[row + offset];
|
auto currentRow = &dlg_hst[row + offset];
|
||||||
|
@ -190,6 +190,7 @@ void high_score::RenderHighScoreDialog()
|
||||||
}
|
}
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::Button("Ok"))
|
if (ImGui::Button("Ok"))
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
std::vector<Mix_Music*> midi::LoadedTracks{};
|
std::vector<Mix_Music*> midi::LoadedTracks{};
|
||||||
Mix_Music *midi::track1, *midi::track2, *midi::track3, *midi::active_track, *midi::NextTrack;
|
Mix_Music *midi::track1, *midi::track2, *midi::track3, *midi::active_track, *midi::NextTrack;
|
||||||
bool midi::SetNextTrackFlag;
|
bool midi::SetNextTrackFlag;
|
||||||
int midi::Volume = MIX_MAX_VOLUME;
|
|
||||||
|
|
||||||
constexpr uint32_t FOURCC(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
|
constexpr uint32_t FOURCC(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
|
||||||
{
|
{
|
||||||
|
@ -48,9 +47,8 @@ int midi::music_stop()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int midi::music_init(int volume)
|
int midi::music_init()
|
||||||
{
|
{
|
||||||
SetVolume(volume);
|
|
||||||
active_track = nullptr;
|
active_track = nullptr;
|
||||||
|
|
||||||
if (pb::FullTiltMode)
|
if (pb::FullTiltMode)
|
||||||
|
@ -89,12 +87,6 @@ void midi::music_shutdown()
|
||||||
LoadedTracks.clear();
|
LoadedTracks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void midi::SetVolume(int volume)
|
|
||||||
{
|
|
||||||
Volume = volume;
|
|
||||||
Mix_VolumeMusic(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mix_Music* midi::load_track(std::string fileName)
|
Mix_Music* midi::load_track(std::string fileName)
|
||||||
{
|
{
|
||||||
Mix_Music* audio = nullptr;
|
Mix_Music* audio = nullptr;
|
||||||
|
@ -165,9 +157,6 @@ bool midi::play_track(Mix_Music* midi)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On Windows, MIDI volume can only be set during playback.
|
|
||||||
// And it changes application master volume for some reason.
|
|
||||||
SetVolume(Volume);
|
|
||||||
active_track = midi;
|
active_track = midi;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,15 +88,12 @@ class midi
|
||||||
public:
|
public:
|
||||||
static int play_pb_theme();
|
static int play_pb_theme();
|
||||||
static int music_stop();
|
static int music_stop();
|
||||||
static int music_init(int volume);
|
static int music_init();
|
||||||
static void music_shutdown();
|
static void music_shutdown();
|
||||||
static void SetVolume(int volume);
|
|
||||||
private:
|
private:
|
||||||
static std::vector<Mix_Music*> LoadedTracks;
|
static std::vector<Mix_Music*> LoadedTracks;
|
||||||
static Mix_Music *track1, *track2, *track3, *active_track, *NextTrack;
|
static Mix_Music *track1, *track2, *track3, *active_track, *NextTrack;
|
||||||
static bool SetNextTrackFlag;
|
static bool SetNextTrackFlag;
|
||||||
static int Volume;
|
|
||||||
|
|
||||||
static Mix_Music* load_track(std::string fileName);
|
static Mix_Music* load_track(std::string fileName);
|
||||||
static bool play_track(Mix_Music* midi);
|
static bool play_track(Mix_Music* midi);
|
||||||
static std::vector<uint8_t>* MdsToMidi(std::string file);
|
static std::vector<uint8_t>* MdsToMidi(std::string file);
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
constexpr int options::MaxUps, options::MaxFps, options::MinUps, options::MinFps, options::DefUps, options::DefFps;
|
constexpr int options::MaxUps, options::MaxFps, options::MinUps, options::MinFps, options::DefUps, options::DefFps;
|
||||||
constexpr int options::MaxSoundChannels, options::MinSoundChannels, options::DefSoundChannels;
|
constexpr int options::MaxSoundChannels, options::MinSoundChannels, options::DefSoundChannels;
|
||||||
constexpr int options::MaxVolume, options::MinVolume, options::DefVolume;
|
|
||||||
|
|
||||||
optionsStruct options::Options{};
|
optionsStruct options::Options{};
|
||||||
std::map<std::string, std::string> options::settings{};
|
std::map<std::string, std::string> options::settings{};
|
||||||
|
@ -93,17 +92,16 @@ void options::InitPrimary()
|
||||||
ImGui::GetIO().FontGlobalScale = get_float("UI Scale", 1.0f);
|
ImGui::GetIO().FontGlobalScale = get_float("UI Scale", 1.0f);
|
||||||
Options.Resolution = get_int("Screen Resolution", -1);
|
Options.Resolution = get_int("Screen Resolution", -1);
|
||||||
Options.LinearFiltering = get_int("Linear Filtering", true);
|
Options.LinearFiltering = get_int("Linear Filtering", true);
|
||||||
Options.FramesPerSecond = Clamp(get_int("Frames Per Second", DefFps), MinFps, MaxFps);
|
Options.FramesPerSecond = std::min(MaxFps, std::max(MinUps, get_int("Frames Per Second", DefFps)));
|
||||||
Options.UpdatesPerSecond = Clamp(get_int("Updates Per Second", DefUps), MinUps, MaxUps);
|
Options.UpdatesPerSecond = std::min(MaxUps, std::max(MinUps, get_int("Updates Per Second", DefUps)));
|
||||||
Options.UpdatesPerSecond = std::max(Options.UpdatesPerSecond, Options.FramesPerSecond);
|
Options.UpdatesPerSecond = std::max(Options.UpdatesPerSecond, Options.FramesPerSecond);
|
||||||
Options.ShowMenu = get_int("ShowMenu", true);
|
Options.ShowMenu = get_int("ShowMenu", true);
|
||||||
Options.UncappedUpdatesPerSecond = get_int("Uncapped Updates Per Second", false);
|
Options.UncappedUpdatesPerSecond = get_int("Uncapped Updates Per Second", false);
|
||||||
Options.SoundChannels = Clamp(get_int("Sound Channels", DefSoundChannels), MinSoundChannels, MaxSoundChannels);
|
Options.SoundChannels = get_int("Sound Channels", DefSoundChannels);
|
||||||
|
Options.SoundChannels = std::min(MaxSoundChannels, std::max(MinSoundChannels, Options.SoundChannels));
|
||||||
Options.HybridSleep = get_int("HybridSleep", false);
|
Options.HybridSleep = get_int("HybridSleep", false);
|
||||||
Options.Prefer3DPBGameData = get_int("Prefer 3DPB Game Data", false);
|
Options.Prefer3DPBGameData = get_int("Prefer 3DPB Game Data", false);
|
||||||
Options.IntegerScaling = get_int("Integer Scaling", false);
|
Options.IntegerScaling = get_int("Integer Scaling", false);
|
||||||
Options.SoundVolume = Clamp(get_int("Sound Volume", DefVolume), MinVolume, MaxVolume);
|
|
||||||
Options.MusicVolume = Clamp(get_int("Music Volume", DefVolume), MinVolume, MaxVolume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void options::InitSecondary()
|
void options::InitSecondary()
|
||||||
|
@ -141,8 +139,6 @@ void options::uninit()
|
||||||
set_int("HybridSleep", Options.HybridSleep);
|
set_int("HybridSleep", Options.HybridSleep);
|
||||||
set_int("Prefer 3DPB Game Data", Options.Prefer3DPBGameData);
|
set_int("Prefer 3DPB Game Data", Options.Prefer3DPBGameData);
|
||||||
set_int("Integer Scaling", Options.IntegerScaling);
|
set_int("Integer Scaling", Options.IntegerScaling);
|
||||||
set_int("Sound Volume", Options.SoundVolume);
|
|
||||||
set_int("Music Volume", Options.MusicVolume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,8 +78,6 @@ struct optionsStruct
|
||||||
bool HybridSleep;
|
bool HybridSleep;
|
||||||
bool Prefer3DPBGameData;
|
bool Prefer3DPBGameData;
|
||||||
bool IntegerScaling;
|
bool IntegerScaling;
|
||||||
int SoundVolume;
|
|
||||||
int MusicVolume;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ControlRef
|
struct ControlRef
|
||||||
|
@ -97,7 +95,6 @@ public:
|
||||||
DefUps = 120, DefFps = 60;
|
DefUps = 120, DefFps = 60;
|
||||||
// Original uses 8 sound channels
|
// Original uses 8 sound channels
|
||||||
static constexpr int MaxSoundChannels = 32, MinSoundChannels = 1, DefSoundChannels = 8;
|
static constexpr int MaxSoundChannels = 32, MinSoundChannels = 1, DefSoundChannels = 8;
|
||||||
static constexpr int MaxVolume = MIX_MAX_VOLUME, MinVolume = 0, DefVolume = MaxVolume;
|
|
||||||
static optionsStruct Options;
|
static optionsStruct Options;
|
||||||
|
|
||||||
static void InitPrimary();
|
static void InitPrimary();
|
||||||
|
|
|
@ -78,18 +78,12 @@ inline float RandFloat()
|
||||||
return static_cast<float>(std::rand() / static_cast<double>(RAND_MAX));
|
return static_cast<float>(std::rand() / static_cast<double>(RAND_MAX));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> constexpr
|
||||||
constexpr int Sign(T val)
|
int Sign(T val)
|
||||||
{
|
{
|
||||||
return (T(0) < val) - (val < T(0));
|
return (T(0) < val) - (val < T(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const T& Clamp(const T& n, const T& lower, const T& upper)
|
|
||||||
{
|
|
||||||
return std::max(lower, std::min(n, upper));
|
|
||||||
}
|
|
||||||
|
|
||||||
// UTF-8 path adapter for fopen on Windows, implemented in SpaceCadetPinball.cpp
|
// UTF-8 path adapter for fopen on Windows, implemented in SpaceCadetPinball.cpp
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
extern FILE* fopenu(const char* path, const char* opt);
|
extern FILE* fopenu(const char* path, const char* opt);
|
||||||
|
|
|
@ -133,10 +133,10 @@ int winmain::WinMain(LPCSTR lpCmdLine)
|
||||||
// Second step: run updates depending on FullTiltMode
|
// Second step: run updates depending on FullTiltMode
|
||||||
options::InitSecondary();
|
options::InitSecondary();
|
||||||
|
|
||||||
if (!Sound::Init(Options.SoundChannels, Options.Sounds, Options.SoundVolume))
|
if (!Sound::Init(Options.SoundChannels, Options.Sounds))
|
||||||
Options.Sounds = false;
|
Options.Sounds = false;
|
||||||
|
|
||||||
if (!pinball::quickFlag && !midi::music_init(Options.MusicVolume))
|
if (!pinball::quickFlag && !midi::music_init())
|
||||||
Options.Music = false;
|
Options.Music = false;
|
||||||
|
|
||||||
if (pb::init())
|
if (pb::init())
|
||||||
|
@ -291,7 +291,8 @@ int winmain::WinMain(LPCSTR lpCmdLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit duration to 2 * target time
|
// Limit duration to 2 * target time
|
||||||
sleepRemainder = Clamp(DurationMs(frameEnd - updateEnd) - targetTimeDelta, -TargetFrameTime, TargetFrameTime);
|
sleepRemainder = std::max(std::min(DurationMs(frameEnd - updateEnd) - targetTimeDelta, TargetFrameTime),
|
||||||
|
-TargetFrameTime);
|
||||||
frameDuration = std::min<DurationMs>(DurationMs(frameEnd - frameStart), 2 * TargetFrameTime);
|
frameDuration = std::min<DurationMs>(DurationMs(frameEnd - frameStart), 2 * TargetFrameTime);
|
||||||
frameStart = frameEnd;
|
frameStart = frameEnd;
|
||||||
UpdateToFrameCounter++;
|
UpdateToFrameCounter++;
|
||||||
|
@ -430,46 +431,50 @@ void winmain::RenderUi()
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Sound", "F5", Options.Sounds))
|
||||||
|
{
|
||||||
|
options::toggle(Menu1::Sounds);
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("Music", "F6", Options.Music))
|
||||||
|
{
|
||||||
|
options::toggle(Menu1::Music);
|
||||||
|
}
|
||||||
|
ImGui::TextUnformatted("Sound Channels");
|
||||||
|
if (ImGui::SliderInt("##Sound Channels", &Options.SoundChannels, options::MinSoundChannels,
|
||||||
|
options::MaxSoundChannels, "%d", ImGuiSliderFlags_AlwaysClamp))
|
||||||
|
{
|
||||||
|
Options.SoundChannels = std::min(options::MaxSoundChannels,
|
||||||
|
std::max(options::MinSoundChannels, Options.SoundChannels));
|
||||||
|
Sound::SetChannels(Options.SoundChannels);
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::MenuItem("Player Controls...", "F8"))
|
if (ImGui::MenuItem("Player Controls...", "F8"))
|
||||||
{
|
{
|
||||||
pause(false);
|
pause(false);
|
||||||
options::ShowControlDialog();
|
options::ShowControlDialog();
|
||||||
}
|
}
|
||||||
ImGui::Separator();
|
if (ImGui::BeginMenu("Table Resolution"))
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Audio"))
|
|
||||||
{
|
{
|
||||||
if (ImGui::MenuItem("Sound", "F5", Options.Sounds))
|
char buffer[20]{};
|
||||||
|
auto maxResText = pinball::get_rc_string(fullscrn::GetMaxResolution() + 2030, 0);
|
||||||
|
if (ImGui::MenuItem(maxResText, nullptr, Options.Resolution == -1))
|
||||||
{
|
{
|
||||||
options::toggle(Menu1::Sounds);
|
options::toggle(Menu1::MaximumResolution);
|
||||||
}
|
}
|
||||||
ImGui::TextUnformatted("Sound Volume");
|
for (auto i = 0; i <= fullscrn::GetMaxResolution(); i++)
|
||||||
if (ImGui::SliderInt("##Sound Volume", &Options.SoundVolume, options::MinVolume, options::MaxVolume, "%d",
|
|
||||||
ImGuiSliderFlags_AlwaysClamp))
|
|
||||||
{
|
{
|
||||||
Sound::SetVolume(Options.SoundVolume);
|
auto& res = fullscrn::resolution_array[i];
|
||||||
}
|
snprintf(buffer, sizeof buffer - 1, "%d x %d", res.ScreenWidth, res.ScreenHeight);
|
||||||
ImGui::TextUnformatted("Sound Channels");
|
if (ImGui::MenuItem(buffer, nullptr, Options.Resolution == i))
|
||||||
if (ImGui::SliderInt("##Sound Channels", &Options.SoundChannels, options::MinSoundChannels,
|
{
|
||||||
options::MaxSoundChannels, "%d", ImGuiSliderFlags_AlwaysClamp))
|
options::toggle(static_cast<Menu1>(static_cast<int>(Menu1::R640x480) + i));
|
||||||
{
|
}
|
||||||
Sound::SetChannels(Options.SoundChannels);
|
|
||||||
}
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
if (ImGui::MenuItem("Music", "F6", Options.Music))
|
|
||||||
{
|
|
||||||
options::toggle(Menu1::Music);
|
|
||||||
}
|
|
||||||
ImGui::TextUnformatted("Music Volume");
|
|
||||||
if (ImGui::SliderInt("##Music Volume", &Options.MusicVolume, options::MinVolume, options::MaxVolume, "%d",
|
|
||||||
ImGuiSliderFlags_AlwaysClamp))
|
|
||||||
{
|
|
||||||
midi::SetVolume(Options.MusicVolume);
|
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Graphics"))
|
if (ImGui::BeginMenu("Graphics"))
|
||||||
{
|
{
|
||||||
if (ImGui::MenuItem("Uniform Scaling", nullptr, Options.UniformScaling))
|
if (ImGui::MenuItem("Uniform Scaling", nullptr, Options.UniformScaling))
|
||||||
|
@ -528,26 +533,6 @@ void winmain::RenderUi()
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Table Resolution"))
|
|
||||||
{
|
|
||||||
char buffer[20]{};
|
|
||||||
auto maxResText = pinball::get_rc_string(fullscrn::GetMaxResolution() + 2030, 0);
|
|
||||||
if (ImGui::MenuItem(maxResText, nullptr, Options.Resolution == -1))
|
|
||||||
{
|
|
||||||
options::toggle(Menu1::MaximumResolution);
|
|
||||||
}
|
|
||||||
for (auto i = 0; i <= fullscrn::GetMaxResolution(); i++)
|
|
||||||
{
|
|
||||||
auto& res = fullscrn::resolution_array[i];
|
|
||||||
snprintf(buffer, sizeof buffer - 1, "%d x %d", res.ScreenWidth, res.ScreenHeight);
|
|
||||||
if (ImGui::MenuItem(buffer, nullptr, Options.Resolution == i))
|
|
||||||
{
|
|
||||||
options::toggle(static_cast<Menu1>(static_cast<int>(Menu1::R640x480) + i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Game Data"))
|
if (ImGui::BeginMenu("Game Data"))
|
||||||
{
|
{
|
||||||
if (ImGui::MenuItem("Prefer 3DPB Data", nullptr, Options.Prefer3DPBGameData))
|
if (ImGui::MenuItem("Prefer 3DPB Data", nullptr, Options.Prefer3DPBGameData))
|
||||||
|
|
Loading…
Reference in a new issue