Initial commit

This commit is contained in:
MattKC 2024-05-09 11:02:08 -07:00
commit 7e604bb009
13 changed files with 698 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
build/
.vscode/

6
CMakeLists.txt Normal file
View file

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.29)
project(Vanilla C CXX)
add_subdirectory(server)
add_subdirectory(client)

22
client/CMakeLists.txt Normal file
View file

@ -0,0 +1,22 @@
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Multimedia Concurrent)
find_package(SDL2 REQUIRED)
add_executable(vanilla
syncdialog.cpp
syncprogressdialog.cpp
vanilla.cpp
main.cpp
)
target_link_libraries(vanilla PRIVATE
Qt6::Core
Qt6::Widgets
Qt6::Multimedia
Qt6::Concurrent
SDL2
vanillaserver
)
target_include_directories(vanilla PRIVATE
"${CMAKE_SOURCE_DIR}/server"
)

14
client/main.cpp Normal file
View file

@ -0,0 +1,14 @@
#include <QApplication>
#include <QMainWindow>
#include "vanilla.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Vanilla mw;
mw.show();
return app.exec();
}

149
client/syncdialog.cpp Normal file
View file

@ -0,0 +1,149 @@
#include "syncdialog.h"
#include <QDialogButtonBox>
#include <QPushButton>
#include "syncprogressdialog.h"
QString SyncDialog::g_symbols[g_symbolCount] = {
QString::fromUtf8("\xE2\x99\xA0"), // Spade = 0
QString::fromUtf8("\xE2\x99\xA6"), // Diamond = 1
QString::fromUtf8("\xE2\x99\xA5"), // Heart = 2
QString::fromUtf8("\xE2\x99\xA3") // Club = 3
};
SyncDialog::SyncDialog(QWidget *parent) : QDialog(parent)
{
for (int i = 0; i < g_symbolCount; i++) {
m_code[i] = -1;
}
QVBoxLayout *outerLayout = new QVBoxLayout(this);
QLabel *infoLabel = new QLabel(tr(
"<html>"
"<h3>Touch the symbols in the order they are displayed on the TV screen from left to right.</h3>"
"<p>If the symbols are not displayed on the TV screen, press the SYNC button on the Wii U console.</p>"
"</html>"));
infoLabel->setAlignment(Qt::AlignCenter);
infoLabel->setWordWrap(true);
outerLayout->addWidget(infoLabel);
QHBoxLayout *symbolLayout = new QHBoxLayout();
QFont f = font();
f.setPointSize(40);
QFontMetrics fm(f);
symbolLayout->addStretch();
for (int i = 0; i < g_symbolCount; i++) {
QLabel *b = new QLabel(this);
b->setFont(f);
b->setAlignment(Qt::AlignCenter);
b->setFixedSize(fm.height(), fm.height());
symbolLayout->addWidget(b);
m_labels[i] = b;
}
symbolLayout->addStretch();
outerLayout->addLayout(symbolLayout);
m_buttonLayout = new QHBoxLayout();
m_buttonLayout->addStretch();
QPushButton *b;
for (int i = 0; i < g_symbolCount; i++) {
b = createButton(g_symbols[i], i);
}
QFrame *vline = new QFrame();
vline->setFixedSize(3, b->height());
vline->setFrameShape(QFrame::VLine);
vline->setFrameShadow(QFrame::Sunken);
m_buttonLayout->addWidget(vline);
createButton(QString::fromUtf8("\xE2\xAC\x85"), -1);
m_buttonLayout->addStretch();
outerLayout->addLayout(m_buttonLayout);
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Cancel, this);
buttons->setCenterButtons(true);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::close);
outerLayout->addWidget(buttons);
setWindowTitle(tr("Sync"));
updateLabels();
}
QPushButton *SyncDialog::createButton(const QString &text, int id)
{
QFont f = this->font();
f.setPointSize(16);
QPushButton *b = new QPushButton(text, this);
b->setProperty("id", id);
b->setFixedSize(b->sizeHint().height(), b->sizeHint().height());
b->setFont(f);
m_buttonLayout->addWidget(b);
connect(b, &QPushButton::clicked, this, &SyncDialog::buttonClicked);
return b;
}
void SyncDialog::buttonClicked()
{
QPushButton *b = static_cast<QPushButton *>(sender());
int id = b->property("id").toInt();
if (id == -1) {
for (int i = g_symbolCount-1; i >= 0; i--) {
if (m_code[i] != -1) {
m_code[i] = -1;
updateLabels();
break;
}
}
} else {
for (int i = 0; i < g_symbolCount; i++) {
if (m_code[i] == -1) {
m_code[i] = id;
updateLabels();
if (i == g_symbolCount - 1) {
launchSync();
}
break;
}
}
}
}
void SyncDialog::updateLabels()
{
static QString empty = QString::fromUtf8("\xE2\x80\xA2");
for (int i = 0; i < g_symbolCount; i++) {
m_labels[i]->setText((m_code[i] == -1) ? empty : g_symbols[m_code[i]]);
}
}
int intPow(int x, unsigned int p)
{
if (p == 0) return 1;
if (p == 1) return x;
int tmp = intPow(x, p/2);
if (p%2 == 0) return tmp * tmp;
else return x * tmp * tmp;
}
void SyncDialog::launchSync()
{
SyncProgressDialog *progressDialog = new SyncProgressDialog(this->parentWidget());
progressDialog->open();
uint16_t code = 0;
for (int i = 0; i < g_symbolCount; i++) {
code += m_code[i] * intPow(10, g_symbolCount - 1 - i);
}
progressDialog->start(m_wirelessInterface, code);
this->close();
}
void SyncDialog::setup(const QString &wirelessInterface)
{
m_wirelessInterface = wirelessInterface;
}

33
client/syncdialog.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef SYNC_DIALOG_H
#define SYNC_DIALOG_H
#include <QDialog>
#include <QHBoxLayout>
#include <QLabel>
class SyncDialog : public QDialog
{
public:
SyncDialog(QWidget *parent = nullptr);
void setup(const QString &wirelessInterface);
private:
void updateLabels();
QPushButton *createButton(const QString &text, int id);
void launchSync();
static const int g_symbolCount = 4;
static QString g_symbols[g_symbolCount];
QLabel *m_labels[g_symbolCount];
int8_t m_code[g_symbolCount];
QHBoxLayout *m_buttonLayout;
QString m_wirelessInterface;
private slots:
void buttonClicked();
};
#endif // SYNC_DIALOG_H

View file

@ -0,0 +1,45 @@
#include "syncprogressdialog.h"
#include <QDialogButtonBox>
#include <QLabel>
#include <QMessageBox>
#include <QtConcurrent/QtConcurrent>
#include <QVBoxLayout>
extern "C" {
#include "server.h"
}
SyncProgressDialog::SyncProgressDialog(QWidget *parent) : QDialog(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(tr("Connecting to the Wii U console...")));
QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Cancel, this);
buttons->setCenterButtons(true);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::close);
layout->addWidget(buttons);
setWindowTitle(tr("Syncing..."));
}
int startVanillaServer(const QString &wirelessInterface, uint16_t code)
{
return vanilla_sync_with_console(wirelessInterface.toUtf8(), code);
}
void SyncProgressDialog::start(const QString &wirelessInterface, uint16_t code)
{
QFutureWatcher<int> *watcher = new QFutureWatcher<int>(this);
connect(watcher, &QFutureWatcher<int>::finished, this, &SyncProgressDialog::serverReturned);
watcher->setFuture(QtConcurrent::run(startVanillaServer, wirelessInterface, code));
}
void SyncProgressDialog::serverReturned()
{
QFutureWatcher<int> *watcher = static_cast<QFutureWatcher<int> *>(sender());
QMessageBox::information(this, QString(), tr("Server returned code: %1").arg(watcher->result()));
watcher->deleteLater();
this->close();
}

View file

@ -0,0 +1,18 @@
#ifndef SYNC_PROGRESS_H
#define SYNC_PROGRESS_H
#include <QDialog>
class SyncProgressDialog : public QDialog
{
public:
SyncProgressDialog(QWidget *parent = nullptr);
void start(const QString &wirelessInterface, uint16_t code);
private slots:
void serverReturned();
};
#endif // SYNC_PROGRESS_H

196
client/vanilla.cpp Normal file
View file

@ -0,0 +1,196 @@
#include "vanilla.h"
#include <QAudioDevice>
#include <QComboBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QMediaDevices>
#include <QMessageBox>
#include <QPushButton>
#include <QSplitter>
#include <SDL2/SDL.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/wireless.h>
#include "syncdialog.h"
Vanilla::Vanilla(QWidget *parent) : QWidget(parent)
{
if (SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) {
QMessageBox::critical(this, tr("SDL2 Error"), tr("SDL2 failed to initialize. Controller support will be unavailable."));
}
QHBoxLayout *layout = new QHBoxLayout(this);
QSplitter *splitter = new QSplitter(this);
layout->addWidget(splitter);
QWidget *configSection = new QWidget(this);
splitter->addWidget(configSection);
QVBoxLayout *configOuterLayout = new QVBoxLayout(configSection);
{
QGroupBox *connectionConfigGroupBox = new QGroupBox(tr("Connection"), configSection);
configOuterLayout->addWidget(connectionConfigGroupBox);
QGridLayout *configLayout = new QGridLayout(connectionConfigGroupBox);
int row = 0;
configLayout->addWidget(new QLabel(tr("Wi-Fi Adapter: "), connectionConfigGroupBox), row, 0);
m_wirelessInterfaceComboBox = new QComboBox(connectionConfigGroupBox);
configLayout->addWidget(m_wirelessInterfaceComboBox, row, 1);
row++;
QPushButton *syncBtn = new QPushButton(tr("Sync"), connectionConfigGroupBox);
connect(syncBtn, &QPushButton::clicked, this, &Vanilla::showSyncDialog);
configLayout->addWidget(syncBtn, row, 0, 1, 2);
row++;
configLayout->addWidget(new QPushButton(tr("Connect"), connectionConfigGroupBox), row, 0, 1, 2);
}
{
QGroupBox *displayConfigGroupBox = new QGroupBox(tr("Display"), configSection);
configOuterLayout->addWidget(displayConfigGroupBox);
QGridLayout *configLayout = new QGridLayout(displayConfigGroupBox);
int row = 0;
configLayout->addWidget(new QPushButton(tr("Full Screen"), configSection), row, 0, 1, 2);
}
{
QGroupBox *soundConfigGroupBox = new QGroupBox(tr("Sound"), configSection);
configOuterLayout->addWidget(soundConfigGroupBox);
QGridLayout *configLayout = new QGridLayout(soundConfigGroupBox);
int row = 0;
configLayout->addWidget(new QLabel(tr("Microphone: "), soundConfigGroupBox), row, 0);
m_microphoneComboBox = new QComboBox(soundConfigGroupBox);
configLayout->addWidget(m_microphoneComboBox, row, 1);
}
{
QGroupBox *inputConfigGroupBox = new QGroupBox(tr("Input"), configSection);
configOuterLayout->addWidget(inputConfigGroupBox);
QGridLayout *configLayout = new QGridLayout(inputConfigGroupBox);
int row = 0;
configLayout->addWidget(new QLabel(tr("Input: "), inputConfigGroupBox), row, 0);
m_controllerComboBox = new QComboBox(inputConfigGroupBox);
configLayout->addWidget(m_controllerComboBox, row, 1);
row++;
QPushButton *controllerMappingButton = new QPushButton(tr("Configure"), m_controllerComboBox);
configLayout->addWidget(controllerMappingButton, row, 0, 1, 2);
}
configOuterLayout->addStretch();
QWidget *displaySection = new QWidget(this);
displaySection->setMinimumSize(854, 480);
splitter->addWidget(displaySection);
populateWirelessInterfaces();
populateMicrophones();
populateControllers();
setWindowTitle(tr("Vanilla"));
}
int IsWireless(const char* ifname, char* protocol)
{
int sock = -1;
iwreq pwrq;
memset(&pwrq, 0, sizeof(pwrq));
strncpy(pwrq.ifr_name, ifname, IFNAMSIZ);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
return 0;
}
if (ioctl(sock, SIOCGIWNAME, &pwrq) != -1) {
if (protocol) strncpy(protocol, pwrq.u.name, IFNAMSIZ);
close(sock);
return 1;
}
close(sock);
return 0;
}
void Vanilla::populateWirelessInterfaces()
{
m_wirelessInterfaceComboBox->clear();
struct ifaddrs *addrs,*tmp;
getifaddrs(&addrs);
tmp = addrs;
while (tmp)
{
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET) {
if (IsWireless(tmp->ifa_name, nullptr))
m_wirelessInterfaceComboBox->addItem(tmp->ifa_name);
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
}
void Vanilla::populateMicrophones()
{
m_microphoneComboBox->clear();
auto inputs = QMediaDevices::audioInputs();
for (auto input : inputs) {
m_microphoneComboBox->addItem(input.description(), input.id());
}
}
void Vanilla::populateControllers()
{
m_controllerComboBox->clear();
m_controllerComboBox->addItem(tr("Keyboard"), -1);
for (int i = 0; i < SDL_NumJoysticks(); i++) {
SDL_Joystick *j = SDL_JoystickOpen(i);
if (j) {
m_controllerComboBox->addItem(SDL_JoystickName(j), i);
SDL_JoystickClose(j);
}
}
}
void Vanilla::showSyncDialog()
{
SyncDialog *d = new SyncDialog(this);
d->setup(m_wirelessInterfaceComboBox->currentText());
connect(d, &SyncDialog::finished, d, &SyncDialog::deleteLater);
d->open();
}

28
client/vanilla.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef VANILLA_H
#define VANILLA_H
#include <QComboBox>
#include <QProcess>
#include <QWidget>
class Vanilla : public QWidget
{
public:
Vanilla(QWidget *parent = nullptr);
private:
void populateWirelessInterfaces();
void populateMicrophones();
void populateControllers();
QComboBox *m_wirelessInterfaceComboBox;
QComboBox *m_microphoneComboBox;
QComboBox *m_controllerComboBox;
QProcess *m_process;
private slots:
void showSyncDialog();
};
#endif // VANILLA_H

16
server/CMakeLists.txt Normal file
View file

@ -0,0 +1,16 @@
add_library(vanillaserver SHARED
server.c
)
target_include_directories(vanillaserver PRIVATE
/home/matt/src/drc-hostap/src/common
/home/matt/src/drc-hostap/src/utils
)
target_link_directories(vanillaserver PRIVATE
/home/matt/src/drc-hostap/wpa_supplicant
)
target_link_libraries(vanillaserver PRIVATE
wpa_client
)

154
server/server.c Normal file
View file

@ -0,0 +1,154 @@
#include "server.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <wpa_ctrl.h>
int start_wpa_supplicant(const char *wireless_interface, const char *config_file, pid_t *pid)
{
int pipes[2];
pipe(pipes);
(*pid) = fork();
if ((*pid) == 0) {
dup2(pipes[1], STDOUT_FILENO);
dup2(pipes[1], STDERR_FILENO);
close(pipes[0]);
close(pipes[1]);
// Currently in the child
// char *app = "/home/matt/src/hostap/wpa_supplicant/wpa_supplicant";
char *app = "/usr/local/bin/wpa_supplicant_drc";
if (execl(app, app, "-Dnl80211", "-i", wireless_interface, "-c", config_file, NULL) < 0) {
_exit(1);
}
} else if ((*pid) < 0) {
// Fork error
return VANILLA_ERROR_BAD_FORK;
} else {
// Continuation of parent
close(pipes[1]);
// Give WPA supplicant some time to start (TODO: probably should wait for success message)
sleep(1);
/*char buf[4096];
int nbytes = read(pipes[0], buf, sizeof(buf));
printf("Output: (%.*s)\n", nbytes, buf);*/
return VANILLA_ERROR_SUCCESS;
}
}
void wpa_message_callback(char *msg, size_t len)
{
printf("wpa message: %s\n", msg);
}
void wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf, size_t *buf_len)
{
wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, buf_len, wpa_message_callback);
}
int vanilla_sync_with_console(const char *wireless_interface, uint16_t code)
{
static const char *wireless_conf_file = "/tmp/wireless.conf";
FILE *config = fopen(wireless_conf_file, "wb");
if (!config) {
return VANILLA_ERROR_CONFIG_FILE;
}
static const char *ctrl_interface = "/var/run/wpa_supplicant_drc";
fprintf(config, "ctrl_interface=%s\nupdate_config=1", ctrl_interface);
fclose(config);
// Start modified WPA supplicant
pid_t pid;
int err = start_wpa_supplicant(wireless_interface, wireless_conf_file, &pid);
if (err != VANILLA_ERROR_SUCCESS) {
return err;
}
// Get control interface
int ret;
char buf[16384];
sprintf(buf, "%s/%s", ctrl_interface, wireless_interface);
struct wpa_ctrl *ctrl = wpa_ctrl_open(buf);
if (!ctrl) {
ret = VANILLA_ERROR_UNKNOWN;
goto die;
}
int found_console = 0;
do {
size_t actual_buf_len = sizeof(buf);
wpa_ctrl_command(ctrl, "SCAN", buf, &actual_buf_len);
printf("%.*s", actual_buf_len, buf);
// printf("sleeping for 10 seconds...\n");
sleep(10);
// printf("retrieving scan results...\n");
actual_buf_len = sizeof(buf);
wpa_ctrl_command(ctrl, "SCAN_RESULTS", buf, &actual_buf_len);
const char *line = strtok(buf, "\n");
while (line) {
if (strstr(line, "WiiU")) {
printf("Found a Wii U! Determine if WPS pin is correct...");
int attempts = 0;
do {
if (attempts > 0) {
sleep(5);
}
char wps_buf[100];
sprintf(wps_buf, "WPS_PIN %.*s %04d5678", 17, line, code);
actual_buf_len = sizeof(buf);
wpa_ctrl_command(ctrl, wps_buf, buf, &actual_buf_len);
attempts++;
} while (!strncmp(buf, "FAIL_BUSY", 9));
printf("%.*s", actual_buf_len, buf);
//printf("%s\n", wps_buf);
printf("saving config...\n");
actual_buf_len = sizeof(buf);
wpa_ctrl_command(ctrl, "SAVE_CONFIG", buf, &actual_buf_len);
}
printf("%s\n", line);
line = strtok(NULL, "\n");
}
} while (!found_console);
/*if (found_bssid) {
ret = VANILLA_ERROR_SUCCESS;
} else {
ret = VANILLA_ERROR_CANCELLED;
}*/
ret = VANILLA_ERROR_SUCCESS;
die:
kill(pid, SIGINT);
return ret;
// Scan
// Check results for WiiU
// Send WPS Pin
// Save config
// Close
}

15
server/server.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef VANILLA_SERVER_H
#define VANILLA_SERVER_H
#include <stdint.h>
#define VANILLA_ERROR_SUCCESS 0
#define VANILLA_ERROR_UNKNOWN -100
#define VANILLA_ERROR_CONFIG_FILE -101
#define VANILLA_ERROR_BAD_FORK -102
#define VANILLA_ERROR_TERMINATED -103
#define VANILLA_ERROR_CANCELLED -104
int vanilla_sync_with_console(const char *wireless_interface, uint16_t code);
#endif // VANILLA_SERVER_H