mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Move a lot of drive parameters into a toplevel drive{} config rather than
leaving them in the source/sink configs (which means they have to be set twice).
This commit is contained in:
@@ -7,9 +7,10 @@ import "lib/imagewriter/imagewriter.proto";
|
||||
import "lib/fluxsource/fluxsource.proto";
|
||||
import "lib/fluxsink/fluxsink.proto";
|
||||
import "lib/usb/usb.proto";
|
||||
import "lib/drive.proto";
|
||||
import "lib/common.proto";
|
||||
|
||||
// NEXT_TAG: 14
|
||||
// NEXT_TAG: 15
|
||||
message ConfigProto {
|
||||
optional string comment = 8;
|
||||
optional bool is_extension = 13;
|
||||
@@ -19,6 +20,7 @@ message ConfigProto {
|
||||
|
||||
optional FluxSourceProto flux_source = 10;
|
||||
optional FluxSinkProto flux_sink = 11;
|
||||
optional DriveProto drive = 14;
|
||||
|
||||
optional EncoderProto encoder = 3;
|
||||
optional DecoderProto decoder = 4;
|
||||
|
||||
14
lib/drive.proto
Normal file
14
lib/drive.proto
Normal file
@@ -0,0 +1,14 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message DriveProto {
|
||||
optional int32 drive = 1 [default = 0, (help) = "which drive to write to (0 or 1)"];
|
||||
optional IndexMode index_mode = 2 [default = INDEXMODE_DRIVE, (help) = "index pulse source"];
|
||||
optional int32 hard_sector_count = 3 [default = 0, (help) = "number of hard sectors on disk"];
|
||||
optional bool high_density = 4 [default = true, (help) = "set if this is a high density disk"];
|
||||
optional bool sync_with_index = 5 [default = false, (help) = "start reading at index mark"];
|
||||
optional double revolutions = 6 [default = 1.2, (help) = "number of revolutions to read"];
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ void FluxSink::updateConfigForFilename(FluxSinkProto* proto, const std::string&
|
||||
{ std::regex("^(.*\\.scp)$"), [](auto& s, auto* proto) { proto->mutable_scp()->set_filename(s); }},
|
||||
{ std::regex("^vcd:(.*)$"), [](auto& s, auto* proto) { proto->mutable_vcd()->set_directory(s); }},
|
||||
{ std::regex("^au:(.*)$"), [](auto& s, auto* proto) { proto->mutable_au()->set_directory(s); }},
|
||||
{ std::regex("^drive:(.*)"), [](auto& s, auto* proto) { proto->mutable_drive()->set_drive(std::stoi(s)); }},
|
||||
{ std::regex("^drive:(.*)"), [](auto& s, auto* proto) { proto->mutable_drive(); config.mutable_drive()->set_drive(std::stoi(s)); }},
|
||||
};
|
||||
|
||||
for (const auto& it : formats)
|
||||
|
||||
@@ -2,12 +2,7 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message HardwareFluxSinkProto {
|
||||
optional IndexMode index_mode = 1 [default = INDEXMODE_DRIVE, (help) = "index pulse source"];
|
||||
optional int32 hard_sector_count = 2 [(help) = "number of hard sectors on the disk"];
|
||||
optional bool high_density = 3 [default = true, (help) = "set if this is a high density disk"];
|
||||
optional int32 drive = 4 [default = 0, (help) = "which drive to write to (0 or 1)"];
|
||||
}
|
||||
message HardwareFluxSinkProto {}
|
||||
|
||||
message AuFluxSinkProto {
|
||||
optional string directory = 1 [default = "aufiles", (help) = "directory to write .au files to"];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "logger.h"
|
||||
#include "proto.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
#include "lib/fluxsink/fluxsink.pb.h"
|
||||
@@ -10,19 +11,19 @@
|
||||
class HardwareFluxSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
HardwareFluxSink(const HardwareFluxSinkProto& config):
|
||||
_config(config)
|
||||
HardwareFluxSink(const HardwareFluxSinkProto& conf):
|
||||
_config(conf)
|
||||
{
|
||||
if (config.has_hard_sector_count())
|
||||
if (config.drive().has_hard_sector_count())
|
||||
{
|
||||
nanoseconds_t oneRevolution;
|
||||
int retries = 5;
|
||||
usbSetDrive(_config.drive(), _config.high_density(), _config.index_mode());
|
||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
||||
Logger() << BeginSpeedOperationLogMessage();
|
||||
|
||||
do {
|
||||
oneRevolution = usbGetRotationalPeriod(_config.hard_sector_count());
|
||||
_hardSectorThreshold = oneRevolution * 3 / (4 * _config.hard_sector_count());
|
||||
oneRevolution = usbGetRotationalPeriod(config.drive().hard_sector_count());
|
||||
_hardSectorThreshold = oneRevolution * 3 / (4 * config.drive().hard_sector_count());
|
||||
retries--;
|
||||
} while ((oneRevolution == 0) && (retries > 0));
|
||||
|
||||
@@ -43,7 +44,7 @@ public:
|
||||
public:
|
||||
void writeFlux(int track, int side, const Fluxmap& fluxmap) override
|
||||
{
|
||||
usbSetDrive(_config.drive(), _config.high_density(), _config.index_mode());
|
||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
||||
#if 0
|
||||
if (fluxSourceSinkFortyTrack)
|
||||
{
|
||||
@@ -60,7 +61,7 @@ public:
|
||||
|
||||
operator std::string () const
|
||||
{
|
||||
return fmt::format("drive {}", _config.drive());
|
||||
return fmt::format("drive {}", config.drive().drive());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -57,7 +57,7 @@ void FluxSource::updateConfigForFilename(FluxSourceProto* proto, const std::stri
|
||||
{ std::regex("^erase:$"), [](auto& s, auto* proto) { proto->mutable_erase(); }},
|
||||
{ std::regex("^kryoflux:(.*)$"), [](auto& s, auto* proto) { proto->mutable_kryoflux()->set_directory(s); }},
|
||||
{ std::regex("^testpattern:(.*)"), [](auto& s, auto* proto) { proto->mutable_test_pattern(); }},
|
||||
{ std::regex("^drive:(.*)"), [](auto& s, auto* proto) { proto->mutable_drive()->set_drive(std::stoi(s)); }},
|
||||
{ std::regex("^drive:(.*)"), [](auto& s, auto* proto) { proto->mutable_drive(); config.mutable_drive()->set_drive(std::stoi(s)); }},
|
||||
};
|
||||
|
||||
for (const auto& it : formats)
|
||||
|
||||
@@ -2,15 +2,7 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message HardwareFluxSourceProto {
|
||||
optional int32 drive = 1 [default = 0, (help) = "which drive to read from"];
|
||||
optional double revolutions = 2 [default = 1.2, (help) = "number of revolutions to read"];
|
||||
|
||||
optional bool sync_with_index = 3 [default = false, (help) = "start reading at index mark"];
|
||||
optional IndexMode index_mode = 4 [default = INDEXMODE_DRIVE, (help) = "index pulse source"];
|
||||
optional int32 hard_sector_count = 5 [default = 0, (help) = "number of hard sectors on disk"];
|
||||
optional bool high_density = 6 [default = true, (help) = "set if this is a high density disk"];
|
||||
}
|
||||
message HardwareFluxSourceProto {}
|
||||
|
||||
message TestPatternFluxSourceProto {
|
||||
optional double interval_us = 1 [default = 4.0, (help) = "interval between pulses"];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "logger.h"
|
||||
#include "proto.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "lib/fluxsource/fluxsource.pb.h"
|
||||
@@ -28,14 +29,12 @@ private:
|
||||
|
||||
std::unique_ptr<const Fluxmap> next()
|
||||
{
|
||||
usbSetDrive(_fluxsource._config.drive(),
|
||||
_fluxsource._config.high_density(),
|
||||
_fluxsource._config.index_mode());
|
||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
||||
usbSeek(_cylinder);
|
||||
|
||||
Bytes data = usbRead(_head,
|
||||
_fluxsource._config.sync_with_index(),
|
||||
_fluxsource._config.revolutions() * _fluxsource._oneRevolution,
|
||||
config.drive().sync_with_index(),
|
||||
config.drive().revolutions() * _fluxsource._oneRevolution,
|
||||
_fluxsource._hardSectorThreshold);
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBytes(data);
|
||||
@@ -49,20 +48,19 @@ private:
|
||||
};
|
||||
|
||||
public:
|
||||
HardwareFluxSource(const HardwareFluxSourceProto& config): _config(config)
|
||||
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf)
|
||||
{
|
||||
int retries = 5;
|
||||
usbSetDrive(
|
||||
_config.drive(), _config.high_density(), _config.index_mode());
|
||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
||||
Logger() << BeginSpeedOperationLogMessage();
|
||||
|
||||
do
|
||||
{
|
||||
_oneRevolution =
|
||||
usbGetRotationalPeriod(_config.hard_sector_count());
|
||||
if (_config.hard_sector_count() != 0)
|
||||
usbGetRotationalPeriod(config.drive().hard_sector_count());
|
||||
if (config.drive().hard_sector_count() != 0)
|
||||
_hardSectorThreshold =
|
||||
_oneRevolution * 3 / (4 * _config.hard_sector_count());
|
||||
_oneRevolution * 3 / (4 * config.drive().hard_sector_count());
|
||||
else
|
||||
_hardSectorThreshold = 0;
|
||||
|
||||
|
||||
@@ -67,24 +67,16 @@ public:
|
||||
if (mediaFlag == 0x20)
|
||||
{
|
||||
Logger() << "D88: high density mode";
|
||||
if (config.flux_sink().dest_case() ==
|
||||
FluxSinkProto::DestCase::kDrive)
|
||||
{
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(
|
||||
true);
|
||||
}
|
||||
if (!config.drive().has_drive())
|
||||
config.mutable_drive()->set_high_density(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger() << "D88: single/double density mode";
|
||||
physicalStep = 2;
|
||||
clockRate = 300;
|
||||
if (config.flux_sink().dest_case() ==
|
||||
FluxSinkProto::DestCase::kDrive)
|
||||
{
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(
|
||||
false);
|
||||
}
|
||||
if (!config.drive().has_drive())
|
||||
config.mutable_drive()->set_high_density(false);
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> image(new Image);
|
||||
|
||||
@@ -57,10 +57,8 @@ public:
|
||||
auto ibm = config.mutable_encoder()->mutable_ibm();
|
||||
config.mutable_cylinders()->set_end(0);
|
||||
Logger() << "NFD: HD 1.2MB mode";
|
||||
if (config.flux_sink().dest_case() == FluxSinkProto::DestCase::kDrive)
|
||||
{
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(true);
|
||||
}
|
||||
if (!config.drive().has_drive())
|
||||
config.mutable_drive()->set_high_density(true);
|
||||
|
||||
std::unique_ptr<Image> image(new Image);
|
||||
for (int track = 0; track < 163; track++)
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
ConfigProto config = []() {
|
||||
ConfigProto config;
|
||||
config.mutable_flux_source()->mutable_drive()->set_drive(0);
|
||||
config.mutable_flux_sink()->mutable_drive()->set_drive(0);
|
||||
config.mutable_drive()->set_drive(0);
|
||||
config.mutable_drive()->set_drive(0);
|
||||
return config;
|
||||
}();
|
||||
|
||||
|
||||
@@ -382,9 +382,10 @@ buildproto libconfig.a \
|
||||
lib/common.proto \
|
||||
lib/config.proto \
|
||||
lib/decoders/decoders.proto \
|
||||
lib/drive.proto \
|
||||
lib/encoders/encoders.proto \
|
||||
lib/fluxsource/fluxsource.proto \
|
||||
lib/fluxsink/fluxsink.proto \
|
||||
lib/fluxsource/fluxsource.proto \
|
||||
lib/imagereader/imagereader.proto \
|
||||
lib/imagewriter/imagewriter.proto \
|
||||
lib/usb/usb.proto \
|
||||
|
||||
@@ -204,14 +204,15 @@ static void draw_x_graticules(Agg2D& painter, double x1, double y1, double x2, d
|
||||
|
||||
int mainAnalyseDriveResponse(int argc, const char* argv[])
|
||||
{
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, {});
|
||||
|
||||
if (!config.flux_sink().has_drive())
|
||||
Error() << "this only makes sense with a real disk drive";
|
||||
|
||||
usbSetDrive(config.flux_sink().drive().drive(),
|
||||
config.flux_sink().drive().high_density(),
|
||||
config.flux_sink().drive().index_mode());
|
||||
usbSetDrive(config.drive().drive(),
|
||||
config.drive().high_density(),
|
||||
config.drive().index_mode());
|
||||
usbSeek(destCylinder);
|
||||
|
||||
std::cout << "Measuring rotational speed...\n";
|
||||
|
||||
@@ -201,6 +201,7 @@ static nanoseconds_t guessClock(const Fluxmap& fluxmap)
|
||||
|
||||
int mainInspect(int argc, const char* argv[])
|
||||
{
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, {});
|
||||
|
||||
std::unique_ptr<FluxSource> fluxSource(FluxSource::create(config.flux_source()));
|
||||
|
||||
@@ -61,6 +61,7 @@ int mainRawRead(int argc, const char* argv[])
|
||||
|
||||
if (argc == 1)
|
||||
showProfiles("rawread", formats);
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, formats);
|
||||
|
||||
if (config.flux_sink().has_drive())
|
||||
|
||||
@@ -65,6 +65,7 @@ int mainRawWrite(int argc, const char* argv[])
|
||||
|
||||
if (argc == 1)
|
||||
showProfiles("rawwrite", formats);
|
||||
config.mutable_flux_sink()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, formats);
|
||||
|
||||
if (config.flux_source().has_drive())
|
||||
|
||||
@@ -67,6 +67,7 @@ int mainRead(int argc, const char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
showProfiles("read", formats);
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, formats);
|
||||
|
||||
if (config.decoder().copy_flux_to().has_drive())
|
||||
|
||||
@@ -23,8 +23,8 @@ int mainRpm(int argc, const char* argv[])
|
||||
if (!config.flux_source().has_drive())
|
||||
Error() << "this only makes sense with a real disk drive";
|
||||
|
||||
usbSetDrive(config.flux_source().drive().drive(), false, config.flux_source().drive().index_mode());
|
||||
nanoseconds_t period = usbGetRotationalPeriod(config.flux_source().drive().hard_sector_count());
|
||||
usbSetDrive(config.drive().drive(), false, config.drive().index_mode());
|
||||
nanoseconds_t period = usbGetRotationalPeriod(config.drive().hard_sector_count());
|
||||
if (period != 0)
|
||||
std::cout << "Rotational period is " << period/1000000 << " ms (" << 60e9/period << " rpm)" << std::endl;
|
||||
else
|
||||
|
||||
@@ -30,7 +30,7 @@ int mainSeek(int argc, const char* argv[])
|
||||
if (!config.flux_source().has_drive())
|
||||
Error() << "this only makes sense with a real disk drive";
|
||||
|
||||
usbSetDrive(config.flux_source().drive().drive(), false, config.flux_source().drive().index_mode());
|
||||
usbSetDrive(config.drive().drive(), false, config.drive().index_mode());
|
||||
usbSeek(cylinder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ int mainWrite(int argc, const char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
showProfiles("write", formats);
|
||||
config.mutable_flux_sink()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, formats);
|
||||
|
||||
std::unique_ptr<ImageReader> reader(ImageReader::create(config.image_reader()));
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'PC 1200kB 5.25" 80-track 15-sector DSHD'
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Japanese PC 1232kB 5.25"/3.5" 77-track 8-sector DSHD'
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Micropolis MetaFloppy Mod I 143kB 5.25" SSDD hard-sectored'
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Micropolis MetaFloppy Mod I 287kB 5.25" DSDD hard-sectored'
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Micropolis MetaFloppy Mod II 315kB 5.25" SSDD hard-sectored'
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Micropolis MetaFloppy Mod II 630kB 5.25" DSDD hard-sectored'
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'N88-BASIC 5.25"/3.5" 77-track 26-sector DSHD'
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -10,17 +10,9 @@ image_writer {
|
||||
nsi {}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: 1
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: true
|
||||
}
|
||||
|
||||
encoder {
|
||||
|
||||
@@ -10,17 +10,9 @@ image_writer {
|
||||
nsi {}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: 1
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: true
|
||||
}
|
||||
|
||||
encoder {
|
||||
|
||||
@@ -10,17 +10,9 @@ image_writer {
|
||||
nsi {}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: 1
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: true
|
||||
}
|
||||
|
||||
encoder {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Digital RX50 400kB 5.25" 80-track 10-sector SSQD'
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -242,10 +242,7 @@ void MainWindow::PrepareConfig()
|
||||
void MainWindow::SetHighDensity()
|
||||
{
|
||||
bool hd = highDensityToggle->GetValue();
|
||||
if (config.flux_source().has_drive())
|
||||
config.mutable_flux_source()->mutable_drive()->set_high_density(hd);
|
||||
if (config.flux_sink().has_drive())
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(hd);
|
||||
config.mutable_drive()->set_high_density(hd);
|
||||
}
|
||||
|
||||
void MainWindow::ShowConfig()
|
||||
|
||||
Reference in New Issue
Block a user