Now supports multiple radios on OEM server

merge-requests/9/merge
Phil Taylor 2022-01-23 15:06:31 +00:00
rodzic ff47fbd82f
commit 264ad231c0
6 zmienionych plików z 148 dodań i 81 usunięć

Wyświetl plik

@ -598,15 +598,36 @@ void servermain::loadSettings()
serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt();
serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt();
serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt();
int numUsers = settings->value("ServerNumUsers", 2).toInt();
serverConfig.users.clear();
for (int f = 0; f < numUsers; f++)
{
SERVERUSER user;
user.username = settings->value("ServerUsername_" + QString::number(f), "").toString();
user.password = settings->value("ServerPassword_" + QString::number(f), "").toString();
user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt();
serverConfig.users.append(user);
int numUsers = settings->beginReadArray("Users");
if (numUsers > 0) {
{
for (int f = 0; f < numUsers; f++)
{
settings->setArrayIndex(f);
SERVERUSER user;
user.username = settings->value("Username", "").toString();
user.password = settings->value("Password", "").toString();
user.userType = settings->value("UserType", 0).toInt();
serverConfig.users.append(user);
}
}
settings->endArray();
}
else {
/* Support old way of storing users*/
settings->endArray();
numUsers = settings->value("ServerNumUsers", 2).toInt();
for (int f = 0; f < numUsers; f++)
{
SERVERUSER user;
user.username = settings->value("ServerUsername_" + QString::number(f), "").toString();
user.password = settings->value("ServerPassword_" + QString::number(f), "").toString();
user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt();
serverConfig.users.append(user);
}
}
serverRxSetup.isinput = true;

Wyświetl plik

@ -6,8 +6,10 @@
udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) :
controlPort(prefs.controlLANPort),
civPort(50002),
audioPort(50003),
civPort(0),
audioPort(0),
civLocalPort(0),
audioLocalPort(0),
rxSetup(rx),
txSetup(tx)
{
@ -55,7 +57,7 @@ udpHandler::udpHandler(udpPreferences prefs, audioSetup rx, audioSetup tx) :
void udpHandler::init()
{
udpBase::init(); // Perform UDP socket initialization.
udpBase::init(0); // Perform UDP socket initialization.
// Connect socket to my dataReceived function.
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpHandler::dataReceived);
@ -219,7 +221,7 @@ void udpHandler::dataReceived()
gotAuthOK = true;
if (!streamOpened)
{
sendRequestStream();
sendRequestStream();
}
}
@ -272,6 +274,27 @@ void udpHandler::dataReceived()
else {
civPort = qFromBigEndian(in->civport);
audioPort = qFromBigEndian(in->audioport);
if (!streamOpened) {
civ = new udpCivData(localIP, radioIP, civPort, civLocalPort);
// TX is not supported
if (txSampleRates < 2) {
txSetup.samplerate = 0;
txSetup.codec = 0;
}
audio = new udpAudio(localIP, radioIP, audioPort, audioLocalPort, rxSetup, txSetup);
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16)));
QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char)));
streamOpened = true;
}
qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName;
}
}
break;
@ -365,32 +388,13 @@ void udpHandler::dataReceived()
sendControl(false, 0x00, in->seq); // Respond with an idle
}
else {
setCurrentRadio(0);
}
}
else if (!in->busy && numRadios == 1)
{
status.message = devName + " available";
if (civPort == 0) {
qInfo(logUdp()) << this->metaObject()->className() << "civPort not yet configured!";
}
if (radios[0].commoncap == 0x8010) {
memcpy(macaddress, radios[0].macaddress, 6);
useGuid = false;
}
else {
useGuid = true;
guid = radios[0].guid;
}
devName = radios[0].name;
audioType = radios[0].audio;
civId = radios[0].civ;
rxSampleRates = radios[0].rxsample;
txSampleRates = radios[0].txsample;
sendRequestStream(); // Send initial stream request.
setCurrentRadio(0);
}
}
else if (streamOpened)
@ -398,7 +402,7 @@ void udpHandler::dataReceived()
a CONNINFO packet, send our details to confirm we still want the stream */
{
// Received while stream is open.
sendRequestStream();
//sendRequestStream();
}
break;
}
@ -451,6 +455,21 @@ void udpHandler::dataReceived()
void udpHandler::setCurrentRadio(int radio) {
qInfo(logUdp()) << "Got Radio" << radio;
qInfo(logUdp()) << "Find available local ports";
/* This seems to be the only way to find some available local ports.
The problem is we need to know the local AND remote ports but send the
local port to the server first and it replies with the remote ports! */
if (civLocalPort == 0 || audioLocalPort == 0) {
QUdpSocket* tempudp = new QUdpSocket();
tempudp->bind(); // Bind to random port.
civLocalPort = tempudp->localPort();
tempudp->close();
tempudp->bind();
audioLocalPort = tempudp->localPort();
tempudp->close();
delete tempudp;
}
int baudrate = qFromBigEndian(radios[radio].baudrate);
emit haveBaudRate(baudrate);
@ -469,25 +488,6 @@ void udpHandler::setCurrentRadio(int radio) {
rxSampleRates = radios[radio].rxsample;
txSampleRates = radios[radio].txsample;
if (!streamOpened) {
civ = new udpCivData(localIP, radioIP, civPort);
// TX is not supported
if (txSampleRates < 2) {
txSetup.samplerate = 0;
txSetup.codec = 0;
}
audio = new udpAudio(localIP, radioIP, audioPort, rxSetup, txSetup);
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16)));
QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char)));
streamOpened = true;
}
qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName;
sendRequestStream();
}
@ -525,8 +525,8 @@ void udpHandler::sendRequestStream()
memcpy(&p.username, usernameEncoded.constData(), usernameEncoded.length());
p.rxsample = qToBigEndian((quint32)rxSetup.samplerate);
p.txsample = qToBigEndian((quint32)txSetup.samplerate);
p.civport = qToBigEndian((quint32)civPort);
p.audioport = qToBigEndian((quint32)audioPort);
p.civport = qToBigEndian((quint32)civLocalPort);
p.audioport = qToBigEndian((quint32)audioLocalPort);
p.txbuffer = qToBigEndian((quint32)txSetup.latency);
p.convert = 1;
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
@ -597,14 +597,14 @@ void udpHandler::sendToken(uint8_t magic)
// Class that manages all Civ Data to/from the rig
udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort)
udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort,quint16 localPort=0)
{
qInfo(logUdp()) << "Starting udpCivData";
localIP = local;
port = civPort;
radioIP = ip;
udpBase::init(); // Perform connection
udpBase::init(localPort); // Perform connection
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpCivData::dataReceived);
@ -793,7 +793,7 @@ void udpCivData::dataReceived()
// Audio stream
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, audioSetup rxSetup, audioSetup txSetup)
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 lport, audioSetup rxSetup, audioSetup txSetup)
{
qInfo(logUdp()) << "Starting udpAudio";
this->localIP = local;
@ -804,7 +804,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, audio
enableTx = false;
}
init(); // Perform connection
init(lport); // Perform connection
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived);
@ -1065,11 +1065,11 @@ void udpAudio::dataReceived()
void udpBase::init()
void udpBase::init(quint16 lport)
{
timeStarted.start();
udp = new QUdpSocket(this);
udp->bind(); // Bind to random port.
udp->bind(lport); // Bind to random port.
localPort = udp->localPort();
qInfo(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port;
uint32_t addr = localIP.toIPv4Address();
@ -1078,7 +1078,6 @@ void udpBase::init()
retransmitTimer = new QTimer();
connect(retransmitTimer, &QTimer::timeout, this, &udpBase::sendRetransmitRequest);
retransmitTimer->start(RETRANSMIT_PERIOD);
}
udpBase::~udpBase()

Wyświetl plik

@ -60,7 +60,9 @@ class udpBase : public QObject
public:
~udpBase();
void init();
void init(quint16 local);
void reconnect();
void dataReceived(QByteArray r);
void sendPing();
@ -142,7 +144,7 @@ class udpCivData : public udpBase
Q_OBJECT
public:
udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort);
udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, quint16 lport);
~udpCivData();
QMutex serialmutex;
@ -168,7 +170,7 @@ class udpAudio : public udpBase
Q_OBJECT
public:
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, audioSetup rxSetup, audioSetup txSetup);
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup);
~udpAudio();
int audioLatency = 0;
@ -271,6 +273,9 @@ private:
quint16 civPort;
quint16 audioPort;
quint16 civLocalPort;
quint16 audioLocalPort;
audioSetup rxSetup;
audioSetup txSetup;

Wyświetl plik

@ -1622,16 +1622,38 @@ void wfmain::loadSettings()
serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt();
serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt();
serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt();
int numUsers = settings->value("ServerNumUsers", 2).toInt();
serverConfig.users.clear();
for (int f = 0; f < numUsers; f++)
{
SERVERUSER user;
user.username = settings->value("ServerUsername_" + QString::number(f), "").toString();
user.password = settings->value("ServerPassword_" + QString::number(f), "").toString();
user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt();
serverConfig.users.append(user);
int numUsers = settings->beginReadArray("Users");
if (numUsers > 0) {
{
for (int f = 0; f < numUsers; f++)
{
settings->setArrayIndex(f);
SERVERUSER user;
user.username = settings->value("Username", "").toString();
user.password = settings->value("Password", "").toString();
user.userType = settings->value("UserType", 0).toInt();
serverConfig.users.append(user);
}
}
settings->endArray();
}
else {
/* Support old way of storing users*/
settings->endArray();
numUsers = settings->value("ServerNumUsers", 2).toInt();
for (int f = 0; f < numUsers; f++)
{
SERVERUSER user;
user.username = settings->value("ServerUsername_" + QString::number(f), "").toString();
user.password = settings->value("ServerPassword_" + QString::number(f), "").toString();
user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt();
serverConfig.users.append(user);
}
}
ui->serverEnableCheckbox->setChecked(serverConfig.enabled);
ui->serverControlPortText->setText(QString::number(serverConfig.controlPort));
@ -2004,18 +2026,32 @@ void wfmain::saveSettings()
settings->setValue("ServerControlPort", serverConfig.controlPort);
settings->setValue("ServerCivPort", serverConfig.civPort);
settings->setValue("ServerAudioPort", serverConfig.audioPort);
settings->setValue("ServerNumUsers", serverConfig.users.count());
settings->setValue("ServerAudioOutput", serverTxSetup.name);
settings->setValue("ServerAudioInput", serverRxSetup.name);
/* Remove old format users*/
int numUsers = settings->value("ServerNumUsers", 0).toInt();
if (numUsers > 0) {
settings->remove("ServerNumUsers");
for (int f = 0; f < numUsers; f++)
{
settings->remove("ServerUsername_" + QString::number(f));
settings->remove("ServerPassword_" + QString::number(f));
settings->remove("ServerUserType_" + QString::number(f));
}
}
settings->beginWriteArray("Users");
for (int f = 0; f < serverConfig.users.count(); f++)
{
settings->setValue("ServerUsername_" + QString::number(f), serverConfig.users[f].username);
settings->setValue("ServerPassword_" + QString::number(f), serverConfig.users[f].password);
settings->setValue("ServerUserType_" + QString::number(f), serverConfig.users[f].userType);
settings->setArrayIndex(f);
settings->setValue("Username", serverConfig.users[f].username);
settings->setValue("Password", serverConfig.users[f].password);
settings->setValue("UserType", serverConfig.users[f].userType);
}
settings->endArray();
qInfo() << "Server config stored";
settings->endGroup();

Wyświetl plik

@ -57,7 +57,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<ObjectFileName>release\</ObjectFileName>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ff47fbd";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessToFile>false</PreprocessToFile>
<ProgramDataBaseFileName></ProgramDataBaseFileName>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
@ -85,7 +85,7 @@
<WarningLevel>0</WarningLevel>
</Midl>
<ResourceCompile>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ff47fbd\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc></ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@ -99,7 +99,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<ObjectFileName>debug\</ObjectFileName>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="9f059c9";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="ff47fbd";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessToFile>false</PreprocessToFile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<SuppressStartupBanner>true</SuppressStartupBanner>
@ -124,7 +124,7 @@
<WarningLevel>0</WarningLevel>
</Midl>
<ResourceCompile>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"9f059c9\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";BUILD_WFSERVER;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"ff47fbd\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc></ItemDefinitionGroup>
<ItemGroup>

Wyświetl plik

@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.30804.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfview", "wfview.vcxproj", "{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfserver", "wfserver.vcxproj", "{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
@ -15,6 +17,10 @@ Global
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Debug|x86.Build.0 = Debug|Win32
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.ActiveCfg = Release|Win32
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.Build.0 = Release|Win32
{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.ActiveCfg = Debug|Win32
{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.Build.0 = Debug|Win32
{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.ActiveCfg = Release|Win32
{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE