diff --git a/.travis.yml b/.travis.yml index 41a78d345..e87700b5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,12 @@ matrix: install: "./.travis/macos/deps.sh" script: "./.travis/macos/build.sh" after_success: "./.travis/macos/upload.sh" + - os: linux + env: NAME="linux build (frozen versions of dependencies)" + sudo: required + dist: trusty + services: docker + script: "./.travis/linux-frozen/build.sh" deploy: provider: releases diff --git a/.travis/linux-frozen/build.sh b/.travis/linux-frozen/build.sh new file mode 100755 index 000000000..25c655516 --- /dev/null +++ b/.travis/linux-frozen/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash -ex + +docker pull ubuntu:16.04 +docker run -v $(pwd):/citra ubuntu:16.04 /bin/bash -ex /citra/.travis/linux-frozen/docker.sh diff --git a/.travis/linux-frozen/docker.sh b/.travis/linux-frozen/docker.sh new file mode 100755 index 000000000..edca9a7a1 --- /dev/null +++ b/.travis/linux-frozen/docker.sh @@ -0,0 +1,27 @@ +#!/bin/bash -ex + +cd /citra + +apt-get update +apt-get install -y build-essential wget git python-launchpadlib libssl-dev + +# Install specific versions of packages with their dependencies +# The apt repositories remove older versions regularly, so we can't use +# apt-get and have to pull the packages directly from the archives. +/citra/.travis/linux-frozen/install_package.py \ + libsdl2-dev 2.0.4+dfsg1-2ubuntu2 xenial \ + qtbase5-dev 5.2.1+dfsg-1ubuntu14.3 trusty \ + libqt5opengl5-dev 5.2.1+dfsg-1ubuntu14.3 trusty \ + libcurl4-openssl-dev 7.47.0-1ubuntu2.3 xenial \ + libicu52 52.1-3ubuntu0.6 trusty + +# Get a recent version of CMake +wget https://cmake.org/files/v3.9/cmake-3.9.0-Linux-x86_64.sh +echo y | sh cmake-3.9.0-Linux-x86_64.sh --prefix=cmake +export PATH=/citra/cmake/cmake-3.9.0-Linux-x86_64/bin:$PATH + +mkdir build && cd build +cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_BUILD_TYPE=Release +make -j4 + +ctest -VV -C Release diff --git a/.travis/linux-frozen/install_package.py b/.travis/linux-frozen/install_package.py new file mode 100755 index 000000000..bdf333716 --- /dev/null +++ b/.travis/linux-frozen/install_package.py @@ -0,0 +1,38 @@ +#!/usr/bin/python + +import sys, re, subprocess +from launchpadlib.launchpad import Launchpad + +cachedir = '/.launchpadlib/cache/' +launchpad = Launchpad.login_anonymously('grab build info', 'production', cachedir, version='devel') + +processed_packages = [] +deb_file_list = [] + +def get_url(pkg, distro): + build_link = launchpad.archives.getByReference(reference='ubuntu').getPublishedBinaries(binary_name=pkg[0], distro_arch_series='https://api.launchpad.net/devel/ubuntu/'+distro+'/amd64', version=pkg[1], exact_match=True, order_by_date=True).entries[0]['build_link'] + deb_name = pkg[0] + '_' + pkg[1] + '_amd64.deb' + deb_link = build_link + '/+files/' + deb_name + return [deb_link, deb_name] + +def list_dependencies(deb_file): + t=subprocess.check_output(['bash', '-c', 'dpkg -I ' + deb_file + ' | grep -oP "^ Depends\: \K.*$"']) + deps=[i.strip() for i in t.split(',')] + equals_re = re.compile(r'^(.*) \(= (.*)\)$') + return [equals_re.sub(r'\1=\2', i).split('=') for i in filter(equals_re.match, deps)] + +def get_package(pkg, distro): + if pkg in processed_packages: + return + print 'Getting ' + pkg[0] + '...' + url = get_url(pkg, distro) + subprocess.check_call(['wget', '--quiet', url[0], '-O', url[1]]) + for dep in list_dependencies(url[1]): + get_package(dep, distro) + processed_packages.append(pkg) + deb_file_list.append('./' + url[1]) + +for i in xrange(1, len(sys.argv), 3): + get_package([sys.argv[i], sys.argv[i + 1]], sys.argv[i + 2]) + +subprocess.check_call(['apt-get', 'install', '-y'] + deb_file_list) diff --git a/dist/icons/citra.png b/dist/icons/citra.png new file mode 100644 index 000000000..83fa499dd Binary files /dev/null and b/dist/icons/citra.png differ diff --git a/dist/icons/icons.qrc b/dist/icons/icons.qrc index f0c44862f..a25804907 100644 --- a/dist/icons/icons.qrc +++ b/dist/icons/icons.qrc @@ -2,5 +2,6 @@ checked.png failed.png + citra.png diff --git a/externals/dynarmic b/externals/dynarmic index 8f15e3f70..d3fb60328 160000 --- a/externals/dynarmic +++ b/externals/dynarmic @@ -1 +1 @@ -Subproject commit 8f15e3f70cb96e56705e5de6ba97b5d09423a56b +Subproject commit d3fb603287dd4b0b06b921b639ef79dc9a850cdc diff --git a/externals/xbyak b/externals/xbyak index fe4765d2f..c5da3778e 160000 --- a/externals/xbyak +++ b/externals/xbyak @@ -1 +1 @@ -Subproject commit fe4765d2fed4e990ea5e9661b6bc5fc9bf48ec16 +Subproject commit c5da3778e7f84013fe8c26fcf18a67881bd1e825 diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index add7566c2..e4929a0b4 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -23,8 +23,10 @@ set(SRCS debugger/profiler.cpp debugger/registers.cpp debugger/wait_tree.cpp + updater/updater.cpp util/spinbox.cpp util/util.cpp + aboutdialog.cpp bootmanager.cpp game_list.cpp hotkeys.cpp @@ -55,8 +57,11 @@ set(HEADERS debugger/profiler.h debugger/registers.h debugger/wait_tree.h + updater/updater.h + updater/updater_p.h util/spinbox.h util/util.h + aboutdialog.h bootmanager.h game_list.h game_list_p.h @@ -75,6 +80,7 @@ set(UIS configuration/configure_system.ui configuration/configure_web.ui debugger/registers.ui + aboutdialog.ui hotkeys.ui main.ui ) diff --git a/src/citra_qt/aboutdialog.cpp b/src/citra_qt/aboutdialog.cpp new file mode 100644 index 000000000..9c11168ba --- /dev/null +++ b/src/citra_qt/aboutdialog.cpp @@ -0,0 +1,19 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "aboutdialog.h" +#include "common/scm_rev.h" +#include "ui_aboutdialog.h" + +AboutDialog::AboutDialog(QWidget* parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), + ui(new Ui::AboutDialog) { + ui->setupUi(this); + ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( + Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); +} + +AboutDialog::~AboutDialog() { + delete ui; +} diff --git a/src/citra_qt/aboutdialog.h b/src/citra_qt/aboutdialog.h new file mode 100644 index 000000000..606789774 --- /dev/null +++ b/src/citra_qt/aboutdialog.h @@ -0,0 +1,25 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { +class AboutDialog; +} + +class AboutDialog : public QDialog { + Q_OBJECT + +public: + explicit AboutDialog(QWidget* parent = 0); + ~AboutDialog(); + +private: + Ui::AboutDialog* ui; +}; + +#endif // ABOUTDIALOG_H diff --git a/src/citra_qt/aboutdialog.ui b/src/citra_qt/aboutdialog.ui new file mode 100644 index 000000000..32f0a0356 --- /dev/null +++ b/src/citra_qt/aboutdialog.ui @@ -0,0 +1,156 @@ + + + AboutDialog + + + + 0 + 0 + 752 + 300 + + + + About Citra + + + + + 630 + 250 + 101 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + 20 + 20 + 241 + 251 + + + + <html><head/><body><p><img src=":/icons/citra.png"/></p></body></html> + + + + + + 270 + 10 + 100 + 50 + + + + <html><head/><body><p><span style=" font-size:28pt;">Citra</span></p></body></html> + + + + + + 270 + 70 + 451 + 21 + + + + <html><head/><body><p>%1 | %2-%3</p></body></html> + + + + + + 270 + 90 + 461 + 131 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">Citra is a free and open source 3DS emulator </span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">licensed under GPLv2.0 or any later version.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">This software should not be used to play games </span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">you have not legally obtained.</span></p></body></html> + + + + + + 270 + 230 + 361 + 16 + + + + <html><head/><body><p><a href="https://citra-emu.org/"><span style=" text-decoration: underline; color:#0000ff;">Website</span></a> | <a href="https://community.citra-emu.org/"><span style=" text-decoration: underline; color:#0000ff;">Forum</span></a> | <a href="https://github.com/citra-emu"><span style=" text-decoration: underline; color:#0000ff;">Source Code</span></a> | <a href="//github.com/citra-emu/citra/graphs/contributors"><span style=" text-decoration: underline; color:#0000ff;">Contributors</span></a> | <a href="https://github.com/citra-emu/citra/blob/master/license.txt"><span style=" text-decoration: underline; color:#0000ff;">License</span></a></p></body></html> + + + + + + 150 + 270 + 450 + 16 + + + + <html><head/><body><p><span style=" font-size:7pt;">&quot;3DS&quot; is a trademark of Nintendo. Citra is not affiliated with Nintendo in any way.</span></p></body></html> + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 601c8b0e3..97993e426 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -157,6 +157,12 @@ void Config::ReadValues() { qt_config->beginGroup("UI"); UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); + qt_config->beginGroup("Updater"); + UISettings::values.check_for_update_on_start = + qt_config->value("check_for_update_on_start", true).toBool(); + UISettings::values.update_on_close = qt_config->value("update_on_close", false).toBool(); + qt_config->endGroup(); + qt_config->beginGroup("UILayout"); UISettings::values.geometry = qt_config->value("geometry").toByteArray(); UISettings::values.state = qt_config->value("state").toByteArray(); @@ -307,6 +313,11 @@ void Config::SaveValues() { qt_config->beginGroup("UI"); qt_config->setValue("theme", UISettings::values.theme); + qt_config->beginGroup("Updater"); + qt_config->setValue("check_for_update_on_start", UISettings::values.check_for_update_on_start); + qt_config->setValue("update_on_close", UISettings::values.update_on_close); + qt_config->endGroup(); + qt_config->beginGroup("UILayout"); qt_config->setValue("geometry", UISettings::values.geometry); qt_config->setValue("state", UISettings::values.state); diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index 939379717..f8f6c305d 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -20,6 +20,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) this->setConfiguration(); ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->updateBox->setVisible(UISettings::values.updater_found); } ConfigureGeneral::~ConfigureGeneral() {} @@ -29,6 +30,9 @@ void ConfigureGeneral::setConfiguration() { ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit); + ui->toggle_update_check->setChecked(UISettings::values.check_for_update_on_start); + ui->toggle_auto_update->setChecked(UISettings::values.update_on_close); + // The first item is "auto-select" with actual value -1, so plus one here will do the trick ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1); @@ -40,6 +44,10 @@ void ConfigureGeneral::applyConfiguration() { UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.theme = ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); + + UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked(); + UISettings::values.update_on_close = ui->toggle_auto_update->isChecked(); + Settings::values.region_value = ui->region_combobox->currentIndex() - 1; Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); Settings::Apply(); diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui index eedf2cbb0..00e5b49f5 100644 --- a/src/citra_qt/configuration/configure_general.ui +++ b/src/citra_qt/configuration/configure_general.ui @@ -25,16 +25,16 @@ - + - Search sub-directories for games + Confirm exit while emulation is running - + - Confirm exit while emulation is running + Search sub-directories for games @@ -44,24 +44,51 @@ - - - Performance - - - - - - - - Enable CPU JIT - - - - - + + + Updates + + + + + + + + Check for updates on start + + + + + + + Silently auto update after closing + + + - + + + + + + + + Performance + + + + + + + + Enable CPU JIT + + + + + + + @@ -149,8 +176,7 @@ - - + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index e0de66e02..0e9e8f1c5 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include "citra_qt/aboutdialog.h" #include "citra_qt/bootmanager.h" #include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_dialog.h" @@ -29,6 +30,7 @@ #include "citra_qt/hotkeys.h" #include "citra_qt/main.h" #include "citra_qt/ui_settings.h" +#include "citra_qt/updater/updater.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -99,6 +101,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { InitializeDebugWidgets(); InitializeRecentFileMenuActions(); InitializeHotkeys(); + ShowUpdaterWidgets(); SetDefaultUIGeometry(); RestoreUIState(); @@ -117,6 +120,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { // Show one-time "callout" messages to the user ShowCallouts(); + if (UISettings::values.check_for_update_on_start) { + CheckForUpdates(); + } + QStringList args = QApplication::arguments(); if (args.length() >= 2) { BootGame(args[1]); @@ -138,6 +145,10 @@ void GMainWindow::InitializeWidgets() { game_list = new GameList(this); ui.horizontalLayout->addWidget(game_list); + // Setup updater + updater = new Updater(this); + UISettings::values.updater_found = updater->HasUpdater(); + // Create status bar message_label = new QLabel(); // Configured separately for left alignment @@ -267,6 +278,13 @@ void GMainWindow::InitializeHotkeys() { }); } +void GMainWindow::ShowUpdaterWidgets() { + ui.action_Check_For_Updates->setVisible(UISettings::values.updater_found); + ui.action_Open_Maintenance_Tool->setVisible(UISettings::values.updater_found); + + connect(updater, &Updater::CheckUpdatesDone, this, &GMainWindow::OnUpdateFound); +} + void GMainWindow::SetDefaultUIGeometry() { // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half const QRect screenRect = QApplication::desktop()->screenGeometry(this); @@ -340,6 +358,15 @@ void GMainWindow::ConnectMenuEvents() { connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key()); connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); + + // Help + connect(ui.action_FAQ, &QAction::triggered, + []() { QDesktopServices::openUrl(QUrl("https://citra-emu.org/wiki/faq/")); }); + connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnMenuAboutCitra); + connect(ui.action_Check_For_Updates, &QAction::triggered, this, + &GMainWindow::OnCheckForUpdates); + connect(ui.action_Open_Maintenance_Tool, &QAction::triggered, this, + &GMainWindow::OnOpenUpdater); } void GMainWindow::OnDisplayTitleBars(bool show) { @@ -362,6 +389,73 @@ void GMainWindow::OnDisplayTitleBars(bool show) { } } +void GMainWindow::OnCheckForUpdates() { + explicit_update_check = true; + CheckForUpdates(); +} + +void GMainWindow::CheckForUpdates() { + if (updater->CheckForUpdates()) { + LOG_INFO(Frontend, "Update check started"); + } else { + LOG_WARNING(Frontend, "Unable to start check for updates"); + } +} + +void GMainWindow::OnUpdateFound(bool found, bool error) { + if (error) { + LOG_WARNING(Frontend, "Update check failed"); + return; + } + + if (!found) { + LOG_INFO(Frontend, "No updates found"); + + // If the user explicitly clicked the "Check for Updates" button, we are + // going to want to show them a prompt anyway. + if (explicit_update_check) { + explicit_update_check = false; + ShowNoUpdatePrompt(); + } + return; + } + + if (emulation_running && !explicit_update_check) { + LOG_INFO(Frontend, "Update found, deferring as game is running"); + defer_update_prompt = true; + return; + } + + LOG_INFO(Frontend, "Update found!"); + explicit_update_check = false; + + ShowUpdatePrompt(); +} + +void GMainWindow::ShowUpdatePrompt() { + defer_update_prompt = false; + + auto result = QMessageBox::question( + this, tr("Update available!"), + tr("An update for Citra is available. Do you wish to install it now?

" + "This will terminate emulation, if it is running."), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (result == QMessageBox::Yes) { + updater->LaunchUIOnExit(); + close(); + } +} + +void GMainWindow::ShowNoUpdatePrompt() { + QMessageBox::information(this, tr("No update found"), tr("No update has been found for Citra."), + QMessageBox::Ok, QMessageBox::Ok); +} + +void GMainWindow::OnOpenUpdater() { + updater->LaunchUI(); +} + bool GMainWindow::LoadROM(const QString& filename) { // Shutdown previous session if the emu thread is still active... if (emu_thread != nullptr) @@ -517,6 +611,10 @@ void GMainWindow::ShutdownGame() { emu_frametime_label->setVisible(false); emulation_running = false; + + if (defer_update_prompt) { + ShowUpdatePrompt(); + } } void GMainWindow::StoreRecentFile(const QString& filename) { @@ -801,6 +899,11 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det } } +void GMainWindow::OnMenuAboutCitra() { + AboutDialog about{this}; + about.exec(); +} + bool GMainWindow::ConfirmClose() { if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) return true; diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 4efab9a4a..878b2becf 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -2,8 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#ifndef _CITRA_QT_MAIN_HXX_ -#define _CITRA_QT_MAIN_HXX_ +#pragma once #include #include @@ -24,7 +23,9 @@ class GRenderWindow; class MicroProfileDialog; class ProfilerWidget; class RegistersWidget; +class Updater; class WaitTreeWidget; +class AboutDialog; class GMainWindow : public QMainWindow { Q_OBJECT @@ -81,6 +82,10 @@ private: void ShutdownGame(); void ShowCallouts(); + void ShowUpdaterWidgets(); + void ShowUpdatePrompt(); + void ShowNoUpdatePrompt(); + void CheckForUpdates(); /** * Stores the filename in the recently loaded files list. @@ -131,6 +136,11 @@ private slots: void ToggleWindowMode(); void OnCreateGraphicsSurfaceViewer(); void OnCoreError(Core::System::ResultStatus, std::string); + /// Called whenever a user selects Help->About Citra + void OnMenuAboutCitra(); + void OnUpdateFound(bool found, bool error); + void OnCheckForUpdates(); + void OnOpenUpdater(); private: void UpdateStatusBar(); @@ -163,6 +173,10 @@ private: GraphicsVertexShaderWidget* graphicsVertexShaderWidget; GraphicsTracingWidget* graphicsTracingWidget; WaitTreeWidget* waitTreeWidget; + Updater* updater; + + bool explicit_update_check = false; + bool defer_update_prompt = false; QAction* actions_recent_files[max_recent_files_item]; @@ -171,5 +185,3 @@ protected: void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; }; - -#endif // _CITRA_QT_MAIN_HXX_ diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 617343e51..417a8a6a6 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -96,6 +96,10 @@ &Help + + + + @@ -142,6 +146,11 @@ &Stop + + + FAQ + + About Citra @@ -205,6 +214,19 @@ Fullscreen + + + Modify Citra Install + + + Opens the maintenance tool to modify your Citra installation + + + + + Check for Updates + + diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h index ffbe14bc3..dba4c5d3c 100644 --- a/src/citra_qt/ui_settings.h +++ b/src/citra_qt/ui_settings.h @@ -39,6 +39,10 @@ struct Values { bool confirm_before_closing; bool first_start; + bool updater_found; + bool update_on_close; + bool check_for_update_on_start; + QString roms_path; QString symbols_path; QString gamedir; diff --git a/src/citra_qt/updater/updater.cpp b/src/citra_qt/updater/updater.cpp new file mode 100644 index 000000000..b5a448ca3 --- /dev/null +++ b/src/citra_qt/updater/updater.cpp @@ -0,0 +1,304 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. +// +// Based on the original work by Felix Barx +// Copyright (c) 2015, Felix Barz +// All rights reserved. + +#include +#include +#include +#include +#include +#include "citra_qt/ui_settings.h" +#include "citra_qt/updater/updater.h" +#include "citra_qt/updater/updater_p.h" +#include "common/logging/log.h" + +#ifdef Q_OS_OSX +#define DEFAULT_TOOL_PATH QStringLiteral("../../../../maintenancetool") +#else +#define DEFAULT_TOOL_PATH QStringLiteral("../maintenancetool") +#endif + +Updater::Updater(QObject* parent) : Updater(DEFAULT_TOOL_PATH, parent) {} + +Updater::Updater(const QString& maintenance_tool_path, QObject* parent) + : QObject(parent), backend(std::make_unique(this)) { + backend->tool_path = UpdaterPrivate::ToSystemExe(maintenance_tool_path); +} + +Updater::~Updater() = default; + +bool Updater::ExitedNormally() const { + return backend->normal_exit; +} + +int Updater::ErrorCode() const { + return backend->last_error_code; +} + +QByteArray Updater::ErrorLog() const { + return backend->last_error_log; +} + +bool Updater::IsRunning() const { + return backend->running; +} + +QList Updater::LatestUpdateInfo() const { + return backend->update_info; +} + +bool Updater::HasUpdater() const { + return backend->HasUpdater(); +} + +bool Updater::CheckForUpdates() { + return backend->StartUpdateCheck(); +} + +void Updater::AbortUpdateCheck(int max_delay, bool async) { + backend->StopUpdateCheck(max_delay, async); +} + +void Updater::LaunchUI() { + backend->LaunchUI(); +} + +void Updater::SilentlyUpdate() { + backend->SilentlyUpdate(); +} + +void Updater::LaunchUIOnExit() { + backend->LaunchUIOnExit(); +} + +Updater::UpdateInfo::UpdateInfo() = default; + +Updater::UpdateInfo::UpdateInfo(const Updater::UpdateInfo&) = default; + +Updater::UpdateInfo::UpdateInfo(QString name, QString version, quint64 size) + : name(std::move(name)), version(std::move(version)), size(size) {} + +UpdaterPrivate::UpdaterPrivate(Updater* parent_ptr) : QObject(nullptr), parent(parent_ptr) { + connect(qApp, &QCoreApplication::aboutToQuit, this, &UpdaterPrivate::AboutToExit, + Qt::DirectConnection); + qRegisterMetaType("QProcess::ExitStatus"); +} + +UpdaterPrivate::~UpdaterPrivate() { + if (main_process && main_process->state() != QProcess::NotRunning) { + main_process->kill(); + main_process->waitForFinished(1000); + } +} + +QString UpdaterPrivate::ToSystemExe(QString base_path) { +#if defined(Q_OS_WIN32) + if (!base_path.endsWith(QStringLiteral(".exe"))) + return base_path + QStringLiteral(".exe"); + else + return base_path; +#elif defined(Q_OS_OSX) + if (base_path.endsWith(QStringLiteral(".app"))) + base_path.truncate(base_path.lastIndexOf(QStringLiteral("."))); + return base_path + QStringLiteral(".app/Contents/MacOS/") + QFileInfo(base_path).fileName(); +#elif defined(Q_OS_UNIX) + return base_path; +#endif +} + +bool UpdaterPrivate::HasUpdater() const { + QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path); + return tool_info.exists(); +} + +bool UpdaterPrivate::StartUpdateCheck() { + if (running || !HasUpdater()) { + return false; + } + + update_info.clear(); + normal_exit = true; + last_error_code = EXIT_SUCCESS; + last_error_log.clear(); + + QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path); + main_process = new QProcess(this); + main_process->setProgram(tool_info.absoluteFilePath()); + main_process->setArguments({QStringLiteral("--checkupdates"), QStringLiteral("-v")}); + + connect(main_process, + static_cast(&QProcess::finished), this, + &UpdaterPrivate::UpdaterReady, Qt::QueuedConnection); + connect(main_process, static_cast(&QProcess::error), + this, &UpdaterPrivate::UpdaterError, Qt::QueuedConnection); + + main_process->start(QIODevice::ReadOnly); + running = true; + + emit parent->UpdateInfoChanged(update_info); + emit parent->RunningChanged(true); + return true; +} + +void UpdaterPrivate::StopUpdateCheck(int delay, bool async) { + if (main_process == nullptr || main_process->state() == QProcess::NotRunning) { + return; + } + + if (delay > 0) { + main_process->terminate(); + if (async) { + QTimer* timer = new QTimer(this); + timer->setSingleShot(true); + + connect(timer, &QTimer::timeout, [=]() { + StopUpdateCheck(0, false); + timer->deleteLater(); + }); + + timer->start(delay); + } else { + if (!main_process->waitForFinished(delay)) { + main_process->kill(); + main_process->waitForFinished(100); + } + } + } else { + main_process->kill(); + main_process->waitForFinished(100); + } +} + +XMLParseResult UpdaterPrivate::ParseResult(const QByteArray& output, + QList& out) { + const auto out_string = QString::fromUtf8(output); + const auto xml_begin = out_string.indexOf(QStringLiteral("")); + if (xml_begin < 0) + return XMLParseResult::NoUpdate; + const auto xml_end = out_string.indexOf(QStringLiteral(""), xml_begin); + if (xml_end < 0) + return XMLParseResult::NoUpdate; + + QList updates; + QXmlStreamReader reader(out_string.mid(xml_begin, (xml_end + 10) - xml_begin)); + + reader.readNextStartElement(); + // should always work because it was search for + if (reader.name() != QStringLiteral("updates")) { + return XMLParseResult::InvalidXML; + } + + while (reader.readNextStartElement()) { + if (reader.name() != QStringLiteral("update")) + return XMLParseResult::InvalidXML; + + auto ok = false; + Updater::UpdateInfo info( + reader.attributes().value(QStringLiteral("name")).toString(), + reader.attributes().value(QStringLiteral("version")).toString(), + reader.attributes().value(QStringLiteral("size")).toULongLong(&ok)); + + if (info.name.isEmpty() || info.version.isNull() || !ok) + return XMLParseResult::InvalidXML; + if (reader.readNextStartElement()) + return XMLParseResult::InvalidXML; + + updates.append(info); + } + + if (reader.hasError()) { + LOG_ERROR(Frontend, "Cannot read xml for update: %s", + reader.errorString().toStdString().c_str()); + return XMLParseResult::InvalidXML; + } + + out = updates; + return XMLParseResult::Success; +} + +void UpdaterPrivate::UpdaterReady(int exit_code, QProcess::ExitStatus exit_status) { + if (main_process == nullptr) { + return; + } + + if (exit_status != QProcess::NormalExit) { + UpdaterError(QProcess::Crashed); + return; + } + + normal_exit = true; + last_error_code = exit_code; + last_error_log = main_process->readAllStandardError(); + const auto update_out = main_process->readAllStandardOutput(); + main_process->deleteLater(); + main_process = nullptr; + + running = false; + emit parent->RunningChanged(false); + + QList update_info; + auto err = ParseResult(update_out, update_info); + bool has_error = false; + + if (err == XMLParseResult::Success) { + if (!update_info.isEmpty()) + emit parent->UpdateInfoChanged(update_info); + } else if (err == XMLParseResult::InvalidXML) { + has_error = true; + } + + emit parent->CheckUpdatesDone(!update_info.isEmpty(), has_error); +} + +void UpdaterPrivate::UpdaterError(QProcess::ProcessError error) { + if (main_process) { + normal_exit = false; + last_error_code = error; + last_error_log = main_process->errorString().toUtf8(); + main_process->deleteLater(); + main_process = nullptr; + + running = false; + emit parent->RunningChanged(false); + emit parent->CheckUpdatesDone(false, true); + } +} + +void UpdaterPrivate::LaunchWithArguments(const QStringList& args) { + if (!HasUpdater()) { + return; + } + + QFileInfo tool_info(QCoreApplication::applicationDirPath(), tool_path); + + if (!QProcess::startDetached(tool_info.absoluteFilePath(), args, tool_info.absolutePath())) { + LOG_WARNING(Frontend, "Unable to start program %s", + tool_info.absoluteFilePath().toStdString().c_str()); + } +} + +void UpdaterPrivate::LaunchUI() { + LOG_INFO(Frontend, "Launching update UI..."); + LaunchWithArguments(run_arguments); +} + +void UpdaterPrivate::SilentlyUpdate() { + LOG_INFO(Frontend, "Launching silent update..."); + LaunchWithArguments(silent_arguments); +} + +void UpdaterPrivate::AboutToExit() { + if (launch_ui_on_exit) { + LaunchUI(); + } else if (UISettings::values.update_on_close) { + SilentlyUpdate(); + } +} + +void UpdaterPrivate::LaunchUIOnExit() { + launch_ui_on_exit = true; +} diff --git a/src/citra_qt/updater/updater.h b/src/citra_qt/updater/updater.h new file mode 100644 index 000000000..e54590534 --- /dev/null +++ b/src/citra_qt/updater/updater.h @@ -0,0 +1,169 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. +// +// Based on the original work by Felix Barx +// Copyright (c) 2015, Felix Barz +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of QtAutoUpdater nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +class UpdaterPrivate; + +/// The main updater. Can check for updates and run the maintenancetool as updater +class Updater : public QObject { + Q_OBJECT; + + /// Specifies whether the updater is currently checking for updates or not + Q_PROPERTY(bool running READ IsRunning NOTIFY RunningChanged); + /// Holds extended information about the last update check + Q_PROPERTY(QList update_info READ LatestUpdateInfo NOTIFY UpdateInfoChanged); + +public: + /// Provides information about updates for components + struct UpdateInfo { + /// The name of the component that has an update + QString name; + /// The new version for that compontent + QString version; + /// The update download size (in Bytes) + quint64 size = 0; + + /** + * Default Constructor + */ + UpdateInfo(); + + /** + * Copy Constructor + */ + UpdateInfo(const UpdateInfo& other); + + /** + * Constructor that takes name, version and size. + */ + UpdateInfo(QString name, QString version, quint64 size); + }; + + /** + * Default constructor + **/ + explicit Updater(QObject* parent = nullptr); + + /** + * Constructor with an explicitly set path + **/ + explicit Updater(const QString& maintenance_tool_path, QObject* parent = nullptr); + + /** + * Destroys the updater and kills the update check (if running) + **/ + ~Updater(); + + /** + * Returns `true`, if the updater exited normally + **/ + bool ExitedNormally() const; + + /** + * Returns the mainetancetools error code from the last update check, if any. + **/ + int ErrorCode() const; + + /** + * Returns the error output (stderr) of the last update + **/ + QByteArray ErrorLog() const; + + /** + * Returns if a update check is running. + **/ + bool IsRunning() const; + + /** + * Returns the latest update information available, if any. + **/ + QList LatestUpdateInfo() const; + + /** + * Launches the updater UI formally + **/ + void LaunchUI(); + + /** + * Silently updates the application in the background + **/ + void SilentlyUpdate(); + + /** + * Checks to see if a updater application is available + **/ + bool HasUpdater() const; + + /** + * Instead of silently updating, explictly open the UI on shutdown + **/ + void LaunchUIOnExit(); + +public slots: + /** + * Starts checking for updates + **/ + bool CheckForUpdates(); + + /** + * Aborts checking for updates + **/ + void AbortUpdateCheck(int max_delay = 5000, bool async = false); + +signals: + /** + * Will be emitted as soon as the updater finished checking for updates + **/ + void CheckUpdatesDone(bool has_updates, bool has_error); + + /** + * Emitted when a update check operation changes stage + **/ + void RunningChanged(bool running); + + /** + * Emitted when new update information has been found + **/ + void UpdateInfoChanged(QList update_info); + +private: + std::unique_ptr backend; +}; diff --git a/src/citra_qt/updater/updater_p.h b/src/citra_qt/updater/updater_p.h new file mode 100644 index 000000000..175a95623 --- /dev/null +++ b/src/citra_qt/updater/updater_p.h @@ -0,0 +1,66 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. +// +// Based on the original work by Felix Barx +// Copyright (c) 2015, Felix Barz +// All rights reserved. + +#pragma once + +#include +#include "citra_qt/updater/updater.h" + +enum class XMLParseResult { + Success, + NoUpdate, + InvalidXML, +}; + +class UpdaterPrivate : public QObject { + Q_OBJECT; + +public: + explicit UpdaterPrivate(Updater* parent_ptr); + ~UpdaterPrivate(); + + static QString ToSystemExe(QString base_path); + + bool HasUpdater() const; + + bool StartUpdateCheck(); + void StopUpdateCheck(int delay, bool async); + + void LaunchWithArguments(const QStringList& args); + void LaunchUI(); + void SilentlyUpdate(); + + void LaunchUIOnExit(); + +public slots: + void UpdaterReady(int exit_code, QProcess::ExitStatus exit_status); + void UpdaterError(QProcess::ProcessError error); + + void AboutToExit(); + +private: + XMLParseResult ParseResult(const QByteArray& output, QList& out); + + Updater* parent; + + QString tool_path{}; + QList update_info{}; + bool normal_exit = true; + int last_error_code = 0; + QByteArray last_error_log = EXIT_SUCCESS; + + bool running = false; + QProcess* main_process = nullptr; + + bool launch_ui_on_exit = false; + + QStringList run_arguments{"--updater"}; + QStringList silent_arguments{"--silentUpdate"}; + + friend class Updater; +}; diff --git a/src/core/core.cpp b/src/core/core.cpp index 0c7a72987..41a972134 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -96,14 +96,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file ResultStatus init_result{Init(emu_window, system_mode.first.get())}; if (init_result != ResultStatus::Success) { - LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result); + LOG_CRITICAL(Core, "Failed to initialize system (Error %u)!", + static_cast(init_result)); System::Shutdown(); return init_result; } const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)}; if (Loader::ResultStatus::Success != load_result) { - LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result); + LOG_CRITICAL(Core, "Failed to load ROM (Error %u)!", static_cast(load_result)); System::Shutdown(); switch (load_result) { diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index d6be16ef6..68704e72e 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -302,7 +302,7 @@ static void RemoveBreakpoint(BreakpointType type, PAddr addr) { auto bp = p.find(addr); if (bp != p.end()) { LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", - bp->second.len, bp->second.addr, type); + bp->second.len, bp->second.addr, static_cast(type)); p.erase(addr); } } @@ -348,8 +348,8 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) { if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { LOG_DEBUG(Debug_GDBStub, - "Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type, - addr, bp->second.addr, bp->second.addr + len, len); + "Found breakpoint type %d @ %08x, range: %08x - %08x (%u bytes)\n", + static_cast(type), addr, bp->second.addr, bp->second.addr + len, len); return true; } } @@ -738,8 +738,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) { breakpoint.len = len; p.insert({addr, breakpoint}); - LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len, - breakpoint.addr); + LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", + static_cast(type), breakpoint.len, breakpoint.addr); return true; } diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 9c43ed2fd..879dfff0d 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -62,7 +62,7 @@ ResultCode Applet::Create(Service::APT::AppletId id) { applets[id] = std::make_shared(id); break; default: - LOG_ERROR(Service_APT, "Could not create applet %u", id); + LOG_ERROR(Service_APT, "Could not create applet %u", static_cast(id)); // TODO(Subv): Find the right error code return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotSupported, ErrorLevel::Permanent); @@ -82,7 +82,7 @@ std::shared_ptr Applet::Get(Service::APT::AppletId id) { static void AppletUpdateEvent(u64 applet_id, int cycles_late) { Service::APT::AppletId id = static_cast(applet_id); std::shared_ptr applet = Applet::Get(id); - ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", id); + ASSERT_MSG(applet != nullptr, "Applet doesn't exist! applet_id=%08X", static_cast(id)); applet->Update(); diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 776d342f0..fc71adc25 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -74,7 +74,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, } default: - LOG_ERROR(Kernel, "unknown type=%d", type); + LOG_ERROR(Kernel, "unknown type=%d", static_cast(type)); return ERR_INVALID_ENUM_VALUE_FND; } diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 64aa61460..004764c63 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -13,6 +13,7 @@ enum { OutOfHandles = 19, SessionClosedByRemote = 26, PortNameTooLong = 30, + WrongLockingThread = 31, NoPendingSessions = 35, WrongPermission = 46, InvalidBufferDescriptor = 48, diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 2cbca5e5b..d559c06be 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -7,6 +7,7 @@ #include #include "common/assert.h" #include "core/core.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/thread.h" @@ -58,19 +59,34 @@ void Mutex::Acquire(Thread* thread) { lock_count++; } -void Mutex::Release() { - // Only release if the mutex is held - if (lock_count > 0) { - lock_count--; - - // Yield to the next thread only if we've fully released the mutex - if (lock_count == 0) { - holding_thread->held_mutexes.erase(this); - holding_thread->UpdatePriority(); - holding_thread = nullptr; - WakeupAllWaitingThreads(); - Core::System::GetInstance().PrepareReschedule(); +ResultCode Mutex::Release(Thread* thread) { + // We can only release the mutex if it's held by the calling thread. + if (thread != holding_thread) { + if (holding_thread) { + LOG_ERROR( + Kernel, + "Tried to release a mutex (owned by thread id %u) from a different thread id %u", + holding_thread->thread_id, thread->thread_id); } + return ResultCode(ErrCodes::WrongLockingThread, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } + + // Note: It should not be possible for the situation where the mutex has a holding thread with a + // zero lock count to occur. The real kernel still checks for this, so we do too. + if (lock_count <= 0) + return ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::Kernel, + ErrorSummary::InvalidState, ErrorLevel::Permanent); + + lock_count--; + + // Yield to the next thread only if we've fully released the mutex + if (lock_count == 0) { + holding_thread->held_mutexes.erase(this); + holding_thread->UpdatePriority(); + holding_thread = nullptr; + WakeupAllWaitingThreads(); + Core::System::GetInstance().PrepareReschedule(); } } @@ -102,4 +118,4 @@ void Mutex::UpdatePriority() { } } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index bacacd690..3afb99c59 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -8,6 +8,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" namespace Kernel { @@ -52,7 +53,12 @@ public: void AddWaitingThread(SharedPtr thread) override; void RemoveWaitingThread(Thread* thread) override; - void Release(); + /** + * Attempts to release the mutex from the specified thread. + * @param thread Thread that wants to release the mutex. + * @returns The result code of the operation. + */ + ResultCode Release(Thread* thread); private: Mutex(); @@ -65,4 +71,4 @@ private: */ void ReleaseThreadMutexes(Thread* thread); -} // namespace +} // namespace Kernel diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp index e3dd23949..bf9cad3d4 100644 --- a/src/core/hle/service/ac/ac.cpp +++ b/src/core/hle/service/ac/ac.cpp @@ -2,11 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include - #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/result.h" @@ -17,169 +16,170 @@ namespace Service { namespace AC { +void Module::Interface::CreateDefaultConfig(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1, 0, 0); -struct ACConfig { - std::array data; -}; + std::size_t desc_size; + VAddr ac_config_addr = rp.PeekStaticBuffer(0, &desc_size); -static ACConfig default_config{}; + ASSERT_MSG(desc_size >= sizeof(Module::ACConfig), + "Output buffer size can't fit ACConfig structure"); -static bool ac_connected = false; + Memory::WriteBlock(ac_config_addr, &ac->default_config, sizeof(ACConfig)); -static Kernel::SharedPtr close_event; -static Kernel::SharedPtr connect_event; -static Kernel::SharedPtr disconnect_event; - -void CreateDefaultConfig(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u32 ac_config_addr = cmd_buff[65]; - - ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), - "Output buffer size not equal ACConfig size"); - - Memory::WriteBlock(ac_config_addr, &default_config, sizeof(ACConfig)); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushStaticBuffer(ac_config_addr, sizeof(ACConfig), 0); LOG_WARNING(Service_AC, "(STUBBED) called"); } -void ConnectAsync(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::ConnectAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x4, 0, 6); - connect_event = Kernel::g_handle_table.Get(cmd_buff[4]); - if (connect_event) { - connect_event->name = "AC:connect_event"; - connect_event->Signal(); - ac_connected = true; - } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + rp.Skip(2, false); // ProcessId descriptor + ac->connect_event = rp.PopObject(); - LOG_WARNING(Service_AC, "(STUBBED) called"); -} - -void GetConnectResult(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - - LOG_WARNING(Service_AC, "(STUBBED) called"); -} - -void CloseAsync(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - if (ac_connected && disconnect_event) { - disconnect_event->Signal(); + if (ac->connect_event) { + ac->connect_event->name = "AC:connect_event"; + ac->connect_event->Signal(); + ac->ac_connected = true; } - close_event = Kernel::g_handle_table.Get(cmd_buff[4]); - if (close_event) { - close_event->name = "AC:close_event"; - close_event->Signal(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +void Module::Interface::GetConnectResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x5, 0, 2); + rp.Skip(2, false); // ProcessId descriptor + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::Interface::CloseAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x8, 0, 4); + rp.Skip(2, false); // ProcessId descriptor + + ac->close_event = rp.PopObject(); + + if (ac->ac_connected && ac->disconnect_event) { + ac->disconnect_event->Signal(); } - ac_connected = false; + if (ac->close_event) { + ac->close_event->name = "AC:close_event"; + ac->close_event->Signal(); + } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_AC, "(STUBBED) called"); + ac->ac_connected = false; + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); } -void GetCloseResult(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::GetCloseResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x9, 0, 2); + rp.Skip(2, false); // ProcessId descriptor - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_AC, "(STUBBED) called"); } -void GetWifiStatus(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::GetWifiStatus(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xD, 0, 0); // TODO(purpasmart96): This function is only a stub, // it returns a valid result without implementing full functionality. - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; // Connection type set to none + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(0); // Connection type set to none LOG_WARNING(Service_AC, "(STUBBED) called"); } -void GetInfraPriority(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::GetInfraPriority(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x27, 0, 2); + VAddr ac_config = rp.PopStaticBuffer(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; // Infra Priority, default 0 + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(0); // Infra Priority, default 0 LOG_WARNING(Service_AC, "(STUBBED) called"); } -void SetRequestEulaVersion(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::SetRequestEulaVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2D, 2, 2); - u32 major = cmd_buff[1] & 0xFF; - u32 minor = cmd_buff[2] & 0xFF; + u32 major = rp.Pop(); + u32 minor = rp.Pop(); - ASSERT_MSG(cmd_buff[3] == (sizeof(ACConfig) << 14 | 2), - "Input buffer size not equal ACConfig size"); - ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), - "Output buffer size not equal ACConfig size"); + VAddr ac_config = rp.PopStaticBuffer(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = 0; // Infra Priority + // TODO(Subv): Copy over the input ACConfig to the stored ACConfig. + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushStaticBuffer(ac_config, sizeof(ACConfig), 0); LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor); } -void RegisterDisconnectEvent(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::RegisterDisconnectEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x30, 0, 4); + rp.Skip(2, false); // ProcessId descriptor - disconnect_event = Kernel::g_handle_table.Get(cmd_buff[4]); - if (disconnect_event) { - disconnect_event->name = "AC:disconnect_event"; + ac->disconnect_event = rp.PopObject(); + if (ac->disconnect_event) { + ac->disconnect_event->name = "AC:disconnect_event"; } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_AC, "(STUBBED) called"); } -void IsConnected(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x3E, 1, 2); + u32 unk = rp.Pop(); + u32 unk_descriptor = rp.Pop(); + u32 unk_param = rp.Pop(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = ac_connected; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(ac->ac_connected); - LOG_WARNING(Service_AC, "(STUBBED) called"); + LOG_WARNING(Service_AC, "(STUBBED) called unk=%08X descriptor=%08X param=%08X", unk, + unk_descriptor, unk_param); } -void SetClientVersion(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void Module::Interface::SetClientVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x40, 1, 2); - const u32 version = cmd_buff[1]; - self->SetVersion(version); + u32 version = rp.Pop(); + rp.Skip(2, false); // ProcessId descriptor LOG_WARNING(Service_AC, "(STUBBED) called, version: 0x%08X", version); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); } -void Init() { - AddService(new AC_I); - AddService(new AC_U); +Module::Interface::Interface(std::shared_ptr ac, const char* name, u32 max_session) + : ac(std::move(ac)), ServiceFramework(name, max_session) {} - ac_connected = false; - - close_event = nullptr; - connect_event = nullptr; - disconnect_event = nullptr; -} - -void Shutdown() { - ac_connected = false; - - close_event = nullptr; - connect_event = nullptr; - disconnect_event = nullptr; +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto ac = std::make_shared(); + std::make_shared(ac)->InstallAsService(service_manager); + std::make_shared(ac)->InstallAsService(service_manager); } } // namespace AC diff --git a/src/core/hle/service/ac/ac.h b/src/core/hle/service/ac/ac.h index 6185faf9b..1990a67ae 100644 --- a/src/core/hle/service/ac/ac.h +++ b/src/core/hle/service/ac/ac.h @@ -4,131 +4,156 @@ #pragma once +#include +#include +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class Event; +} + namespace Service { - -class Interface; - namespace AC { +class Module final { +public: + class Interface : public ServiceFramework { + public: + Interface(std::shared_ptr ac, const char* name, u32 max_session); -/** - * AC::CreateDefaultConfig service function - * Inputs: - * 64 : ACConfig size << 14 | 2 - * 65 : pointer to ACConfig struct - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void CreateDefaultConfig(Interface* self); + /** + * AC::CreateDefaultConfig service function + * Inputs: + * 64 : ACConfig size << 14 | 2 + * 65 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void CreateDefaultConfig(Kernel::HLERequestContext& ctx); -/** - * AC::ConnectAsync service function - * Inputs: - * 1 : ProcessId Header - * 3 : Copy Handle Header - * 4 : Connection Event handle - * 5 : ACConfig size << 14 | 2 - * 6 : pointer to ACConfig struct - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void ConnectAsync(Interface* self); + /** + * AC::ConnectAsync service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Connection Event handle + * 5 : ACConfig size << 14 | 2 + * 6 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void ConnectAsync(Kernel::HLERequestContext& ctx); -/** - * AC::GetConnectResult service function - * Inputs: - * 1 : ProcessId Header - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void GetConnectResult(Interface* self); + /** + * AC::GetConnectResult service function + * Inputs: + * 1 : ProcessId Header + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void GetConnectResult(Kernel::HLERequestContext& ctx); -/** - * AC::CloseAsync service function - * Inputs: - * 1 : ProcessId Header - * 3 : Copy Handle Header - * 4 : Event handle, should be signaled when AC connection is closed - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void CloseAsync(Interface* self); + /** + * AC::CloseAsync service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Event handle, should be signaled when AC connection is closed + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void CloseAsync(Kernel::HLERequestContext& ctx); -/** - * AC::GetCloseResult service function - * Inputs: - * 1 : ProcessId Header - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void GetCloseResult(Interface* self); + /** + * AC::GetCloseResult service function + * Inputs: + * 1 : ProcessId Header + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void GetCloseResult(Kernel::HLERequestContext& ctx); -/** - * AC::GetWifiStatus service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. - */ -void GetWifiStatus(Interface* self); + /** + * AC::GetWifiStatus service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. + */ + void GetWifiStatus(Kernel::HLERequestContext& ctx); -/** - * AC::GetInfraPriority service function - * Inputs: - * 1 : ACConfig size << 14 | 2 - * 2 : pointer to ACConfig struct - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Infra Priority - */ -void GetInfraPriority(Interface* self); + /** + * AC::GetInfraPriority service function + * Inputs: + * 1 : ACConfig size << 14 | 2 + * 2 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Infra Priority + */ + void GetInfraPriority(Kernel::HLERequestContext& ctx); -/** - * AC::SetRequestEulaVersion service function - * Inputs: - * 1 : Eula Version major - * 2 : Eula Version minor - * 3 : ACConfig size << 14 | 2 - * 4 : Input pointer to ACConfig struct - * 64 : ACConfig size << 14 | 2 - * 65 : Output pointer to ACConfig struct - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Infra Priority - */ -void SetRequestEulaVersion(Interface* self); + /** + * AC::SetRequestEulaVersion service function + * Inputs: + * 1 : Eula Version major + * 2 : Eula Version minor + * 3 : ACConfig size << 14 | 2 + * 4 : Input pointer to ACConfig struct + * 64 : ACConfig size << 14 | 2 + * 65 : Output pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Infra Priority + */ + void SetRequestEulaVersion(Kernel::HLERequestContext& ctx); -/** - * AC::RegisterDisconnectEvent service function - * Inputs: - * 1 : ProcessId Header - * 3 : Copy Handle Header - * 4 : Event handle, should be signaled when AC connection is closed - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void RegisterDisconnectEvent(Interface* self); + /** + * AC::RegisterDisconnectEvent service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Event handle, should be signaled when AC connection is closed + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx); -/** - * AC::IsConnected service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : bool, is connected - */ -void IsConnected(Interface* self); + /** + * AC::IsConnected service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : bool, is connected + */ + void IsConnected(Kernel::HLERequestContext& ctx); -/** - * AC::SetClientVersion service function - * Inputs: - * 1 : Used SDK Version - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void SetClientVersion(Interface* self); + /** + * AC::SetClientVersion service function + * Inputs: + * 1 : Used SDK Version + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void SetClientVersion(Kernel::HLERequestContext& ctx); -/// Initialize AC service -void Init(); + protected: + std::shared_ptr ac; + }; -/// Shutdown AC service -void Shutdown(); +protected: + struct ACConfig { + std::array data; + }; + + ACConfig default_config{}; + + bool ac_connected = false; + + Kernel::SharedPtr close_event; + Kernel::SharedPtr connect_event; + Kernel::SharedPtr disconnect_event; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager); } // namespace AC } // namespace Service diff --git a/src/core/hle/service/ac/ac_i.cpp b/src/core/hle/service/ac/ac_i.cpp index b22fe3698..c715bcdd0 100644 --- a/src/core/hle/service/ac/ac_i.cpp +++ b/src/core/hle/service/ac/ac_i.cpp @@ -2,37 +2,36 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/ac/ac.h" #include "core/hle/service/ac/ac_i.h" namespace Service { namespace AC { -const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, - {0x00040006, ConnectAsync, "ConnectAsync"}, - {0x00050002, GetConnectResult, "GetConnectResult"}, - {0x00070002, nullptr, "CancelConnectAsync"}, - {0x00080004, CloseAsync, "CloseAsync"}, - {0x00090002, GetCloseResult, "GetCloseResult"}, - {0x000A0000, nullptr, "GetLastErrorCode"}, - {0x000C0000, nullptr, "GetStatus"}, - {0x000D0000, GetWifiStatus, "GetWifiStatus"}, - {0x000E0042, nullptr, "GetCurrentAPInfo"}, - {0x00100042, nullptr, "GetCurrentNZoneInfo"}, - {0x00110042, nullptr, "GetNZoneApNumService"}, - {0x001D0042, nullptr, "ScanAPs"}, - {0x00240042, nullptr, "AddDenyApType"}, - {0x00270002, GetInfraPriority, "GetInfraPriority"}, - {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, - {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, - {0x003C0042, nullptr, "GetAPSSIDList"}, - {0x003E0042, IsConnected, "IsConnected"}, - {0x00400042, SetClientVersion, "SetClientVersion"}, -}; - -AC_I::AC_I() { - Register(FunctionTable); +// TODO(Subv): Find out the correct number of concurrent sessions allowed +AC_I::AC_I(std::shared_ptr ac) : Module::Interface(std::move(ac), "ac:i", 1) { + static const FunctionInfo functions[] = { + {0x00010000, &AC_I::CreateDefaultConfig, "CreateDefaultConfig"}, + {0x00040006, &AC_I::ConnectAsync, "ConnectAsync"}, + {0x00050002, &AC_I::GetConnectResult, "GetConnectResult"}, + {0x00070002, nullptr, "CancelConnectAsync"}, + {0x00080004, &AC_I::CloseAsync, "CloseAsync"}, + {0x00090002, &AC_I::GetCloseResult, "GetCloseResult"}, + {0x000A0000, nullptr, "GetLastErrorCode"}, + {0x000C0000, nullptr, "GetStatus"}, + {0x000D0000, &AC_I::GetWifiStatus, "GetWifiStatus"}, + {0x000E0042, nullptr, "GetCurrentAPInfo"}, + {0x00100042, nullptr, "GetCurrentNZoneInfo"}, + {0x00110042, nullptr, "GetNZoneApNumService"}, + {0x001D0042, nullptr, "ScanAPs"}, + {0x00240042, nullptr, "AddDenyApType"}, + {0x00270002, &AC_I::GetInfraPriority, "GetInfraPriority"}, + {0x002D0082, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"}, + {0x00300004, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"}, + {0x003C0042, nullptr, "GetAPSSIDList"}, + {0x003E0042, &AC_I::IsConnected, "IsConnected"}, + {0x00400042, &AC_I::SetClientVersion, "SetClientVersion"}, + }; + RegisterHandlers(functions); } } // namespace AC diff --git a/src/core/hle/service/ac/ac_i.h b/src/core/hle/service/ac/ac_i.h index 465bba59c..c6f9d6d49 100644 --- a/src/core/hle/service/ac/ac_i.h +++ b/src/core/hle/service/ac/ac_i.h @@ -4,18 +4,15 @@ #pragma once -#include "core/hle/service/service.h" +#include +#include "core/hle/service/ac/ac.h" namespace Service { namespace AC { -class AC_I final : public Interface { +class AC_I final : public Module::Interface { public: - AC_I(); - - std::string GetPortName() const override { - return "ac:i"; - } + explicit AC_I(std::shared_ptr ac); }; } // namespace AC diff --git a/src/core/hle/service/ac/ac_u.cpp b/src/core/hle/service/ac/ac_u.cpp index 346671b4a..988f27b9d 100644 --- a/src/core/hle/service/ac/ac_u.cpp +++ b/src/core/hle/service/ac/ac_u.cpp @@ -2,37 +2,36 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/service/ac/ac.h" #include "core/hle/service/ac/ac_u.h" namespace Service { namespace AC { -const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, - {0x00040006, ConnectAsync, "ConnectAsync"}, - {0x00050002, GetConnectResult, "GetConnectResult"}, - {0x00070002, nullptr, "CancelConnectAsync"}, - {0x00080004, CloseAsync, "CloseAsync"}, - {0x00090002, GetCloseResult, "GetCloseResult"}, - {0x000A0000, nullptr, "GetLastErrorCode"}, - {0x000C0000, nullptr, "GetStatus"}, - {0x000D0000, GetWifiStatus, "GetWifiStatus"}, - {0x000E0042, nullptr, "GetCurrentAPInfo"}, - {0x00100042, nullptr, "GetCurrentNZoneInfo"}, - {0x00110042, nullptr, "GetNZoneApNumService"}, - {0x001D0042, nullptr, "ScanAPs"}, - {0x00240042, nullptr, "AddDenyApType"}, - {0x00270002, GetInfraPriority, "GetInfraPriority"}, - {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, - {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, - {0x003C0042, nullptr, "GetAPSSIDList"}, - {0x003E0042, IsConnected, "IsConnected"}, - {0x00400042, SetClientVersion, "SetClientVersion"}, -}; - -AC_U::AC_U() { - Register(FunctionTable); +// TODO(Subv): Find out the correct number of concurrent sessions allowed +AC_U::AC_U(std::shared_ptr ac) : Module::Interface(std::move(ac), "ac:u", 1) { + static const FunctionInfo functions[] = { + {0x00010000, &AC_U::CreateDefaultConfig, "CreateDefaultConfig"}, + {0x00040006, &AC_U::ConnectAsync, "ConnectAsync"}, + {0x00050002, &AC_U::GetConnectResult, "GetConnectResult"}, + {0x00070002, nullptr, "CancelConnectAsync"}, + {0x00080004, &AC_U::CloseAsync, "CloseAsync"}, + {0x00090002, &AC_U::GetCloseResult, "GetCloseResult"}, + {0x000A0000, nullptr, "GetLastErrorCode"}, + {0x000C0000, nullptr, "GetStatus"}, + {0x000D0000, &AC_U::GetWifiStatus, "GetWifiStatus"}, + {0x000E0042, nullptr, "GetCurrentAPInfo"}, + {0x00100042, nullptr, "GetCurrentNZoneInfo"}, + {0x00110042, nullptr, "GetNZoneApNumService"}, + {0x001D0042, nullptr, "ScanAPs"}, + {0x00240042, nullptr, "AddDenyApType"}, + {0x00270002, &AC_U::GetInfraPriority, "GetInfraPriority"}, + {0x002D0082, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"}, + {0x00300004, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"}, + {0x003C0042, nullptr, "GetAPSSIDList"}, + {0x003E0042, &AC_U::IsConnected, "IsConnected"}, + {0x00400042, &AC_U::SetClientVersion, "SetClientVersion"}, + }; + RegisterHandlers(functions); } } // namespace AC diff --git a/src/core/hle/service/ac/ac_u.h b/src/core/hle/service/ac/ac_u.h index f9d21e112..6643f3ce5 100644 --- a/src/core/hle/service/ac/ac_u.h +++ b/src/core/hle/service/ac/ac_u.h @@ -4,18 +4,15 @@ #pragma once -#include "core/hle/service/service.h" +#include +#include "core/hle/service/ac/ac.h" namespace Service { namespace AC { -class AC_U final : public Interface { +class AC_U final : public Module::Interface { public: - AC_U(); - - std::string GetPortName() const override { - return "ac:u"; - } + explicit AC_U(std::shared_ptr ac); }; } // namespace AC diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 59ea9823d..e82a32868 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -554,7 +554,7 @@ void ReceiveParameter(Service::Interface* self) { "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", buffer_size, static_buff_size); - LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); if (!next_parameter) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -603,7 +603,7 @@ void GlanceParameter(Service::Interface* self) { "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", buffer_size, static_buff_size); - LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); if (!next_parameter) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -767,7 +767,7 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040 AppletId applet_id = static_cast(rp.Pop()); - LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast(applet_id)); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -775,7 +775,8 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { - LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); + LOG_WARNING(Service_APT, "applet has already been started id=%08X", + static_cast(applet_id)); rb.Push(RESULT_SUCCESS); } else { rb.Push(HLE::Applets::Applet::Create(applet_id)); @@ -800,7 +801,7 @@ void PreloadLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 AppletId applet_id = static_cast(rp.Pop()); - LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast(applet_id)); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -808,7 +809,8 @@ void PreloadLibraryApplet(Service::Interface* self) { auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { - LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); + LOG_WARNING(Service_APT, "applet has already been started id=%08X", + static_cast(applet_id)); rb.Push(RESULT_SUCCESS); } else { rb.Push(HLE::Applets::Applet::Create(applet_id)); @@ -823,7 +825,7 @@ void StartLibraryApplet(Service::Interface* self) { Kernel::Handle handle = rp.PopHandle(); VAddr buffer_addr = rp.PopStaticBuffer(); - LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast(applet_id)); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -867,7 +869,7 @@ void SetScreenCapPostPermission(Service::Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); // No error LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", - screen_capture_post_permission); + static_cast(screen_capture_post_permission)); } void GetScreenCapPostPermission(Service::Interface* self) { @@ -877,7 +879,7 @@ void GetScreenCapPostPermission(Service::Interface* self) { rb.Push(RESULT_SUCCESS); // No error rb.Push(static_cast(screen_capture_post_permission)); LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", - screen_capture_post_permission); + static_cast(screen_capture_post_permission)); } void GetAppletInfo(Service::Interface* self) { @@ -899,7 +901,7 @@ void GetAppletInfo(Service::Interface* self) { rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, ErrorLevel::Status)); } - LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id); + LOG_WARNING(Service_APT, "(stubbed) called appid=%u", static_cast(app_id)); } void GetStartupArgument(Service::Interface* self) { @@ -928,7 +930,7 @@ void GetStartupArgument(Service::Interface* self) { } LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x", - startup_argument_type, parameter_size); + static_cast(startup_argument_type), parameter_size); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 42f8950f9..5ff2262a0 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -301,7 +301,7 @@ static void WriteProcessPipe(Service::Interface* self) { } ASSERT_MSG(Memory::IsValidVirtualAddress(buffer), - "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); std::vector message(size); for (u32 i = 0; i < size; i++) { @@ -359,8 +359,8 @@ static void ReadPipeIfPossible(Service::Interface* self) { DSP::HLE::DspPipe pipe = static_cast(pipe_index); ASSERT_MSG(Memory::IsValidVirtualAddress(addr), - "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, - size, addr); + "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, + unknown, size, addr); cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -403,8 +403,8 @@ static void ReadPipe(Service::Interface* self) { DSP::HLE::DspPipe pipe = static_cast(pipe_index); ASSERT_MSG(Memory::IsValidVirtualAddress(addr), - "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, - size, addr); + "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, + unknown, size, addr); if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { std::vector response = DSP::HLE::PipeRead(pipe, size); diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 02911160b..ec1a65223 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -105,8 +106,8 @@ void File::HandleSyncRequest(Kernel::SharedPtr server_ses offset, length, address); if (offset + length > backend->GetSize()) { - LOG_ERROR(Service_FS, - "Reading from out of bounds offset=0x%llX length=0x%08X file_size=0x%llX", + LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%" PRIx64 + " length=0x%08X file_size=0x%" PRIx64, offset, length, backend->GetSize()); } @@ -191,7 +192,7 @@ void File::HandleSyncRequest(Kernel::SharedPtr server_ses // Unknown command... default: - LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); + LOG_ERROR(Service_FS, "Unknown command=0x%08X!", static_cast(cmd)); ResultCode error = UnimplementedFunction(ErrorModule::FS); cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. return; @@ -231,7 +232,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr serve // Unknown command... default: - LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); + LOG_ERROR(Service_FS, "Unknown command=0x%08X!", static_cast(cmd)); ResultCode error = UnimplementedFunction(ErrorModule::FS); cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. return; @@ -297,7 +298,7 @@ ResultCode RegisterArchiveType(std::unique_ptr&& factor auto& archive = result.first->second; LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), - id_code); + static_cast(id_code)); return RESULT_SUCCESS; } @@ -472,7 +473,7 @@ ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) { } else if (media_type == MediaType::SDMC) { media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX); } else { - LOG_ERROR(Service_FS, "Unsupported media type %u", media_type); + LOG_ERROR(Service_FS, "Unsupported media type %u", static_cast(media_type)); return ResultCode(-1); // TODO(Subv): Find the right error code } diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index b9eab7838..4bd43d15d 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "common/assert.h" #include "common/common_types.h" #include "common/file_util.h" @@ -311,8 +312,8 @@ static void CreateFile(Service::Interface* self) { FileSys::Path file_path(filename_type, filename_size, filename_ptr); - LOG_DEBUG(Service_FS, "type=%u size=%llu data=%s", static_cast(filename_type), file_size, - file_path.DebugStr().c_str()); + LOG_DEBUG(Service_FS, "type=%u size=%" PRIx64 " data=%s", static_cast(filename_type), + file_size, file_path.DebugStr().c_str()); cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; } diff --git a/src/core/hle/service/ndm/ndm.cpp b/src/core/hle/service/ndm/ndm.cpp index 096c0cdac..9f57b3a5a 100644 --- a/src/core/hle/service/ndm/ndm.cpp +++ b/src/core/hle/service/ndm/ndm.cpp @@ -34,7 +34,7 @@ void EnterExclusiveState(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast(exclusive_state)); } void LeaveExclusiveState(Service::Interface* self) { @@ -43,7 +43,7 @@ void LeaveExclusiveState(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast(exclusive_state)); } void QueryExclusiveMode(Service::Interface* self) { @@ -52,7 +52,7 @@ void QueryExclusiveMode(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = static_cast(exclusive_state); - LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); + LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast(exclusive_state)); } void LockState(Service::Interface* self) { @@ -61,7 +61,7 @@ void LockState(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled); + LOG_WARNING(Service_NDM, "(STUBBED) called"); } void UnlockState(Service::Interface* self) { @@ -70,7 +70,7 @@ void UnlockState(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled); + LOG_WARNING(Service_NDM, "(STUBBED) called"); } void SuspendDaemons(Service::Interface* self) { @@ -86,7 +86,7 @@ void SuspendDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x6, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X", static_cast(daemon_bit_mask)); } void ResumeDaemons(Service::Interface* self) { @@ -101,7 +101,7 @@ void ResumeDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X", static_cast(daemon_bit_mask)); } void SuspendScheduler(Service::Interface* self) { @@ -157,7 +157,7 @@ void SetScanInterval(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval); + LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X", scan_interval); } void GetScanInterval(Service::Interface* self) { @@ -166,7 +166,7 @@ void GetScanInterval(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x11, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = scan_interval; - LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval); + LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X", scan_interval); } void SetRetryInterval(Service::Interface* self) { @@ -175,7 +175,7 @@ void SetRetryInterval(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval); + LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X", retry_interval); } void GetRetryInterval(Service::Interface* self) { @@ -184,7 +184,7 @@ void GetRetryInterval(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x13, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = retry_interval; - LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval); + LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X", retry_interval); } void OverrideDefaultDaemons(Service::Interface* self) { @@ -200,7 +200,8 @@ void OverrideDefaultDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", + static_cast(default_daemon_bit_mask)); } void ResetDefaultDaemons(Service::Interface* self) { @@ -209,7 +210,8 @@ void ResetDefaultDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X", + static_cast(default_daemon_bit_mask)); } void GetDefaultDaemons(Service::Interface* self) { @@ -218,7 +220,8 @@ void GetDefaultDaemons(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = static_cast(default_daemon_bit_mask); - LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask); + LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X", + static_cast(default_daemon_bit_mask)); } void ClearHalfAwakeMacFilter(Service::Interface* self) { diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 87a6b0eca..1bf13a296 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,12 @@ namespace Service { namespace NWM { +namespace ErrCodes { +enum { + NotInitialized = 2, +}; +} // namespace ErrCodes + // Event that is signaled every time the connection status changes. static Kernel::SharedPtr connection_status_event; @@ -37,6 +44,8 @@ static Kernel::SharedPtr recv_buffer_memory; // Connection status of this 3DS. static ConnectionStatus connection_status{}; +static std::atomic initialized(false); + /* Node information about the current network. * The amount of elements in this vector is always the maximum number * of nodes specified in the network configuration. @@ -47,8 +56,17 @@ static NodeList node_info; // Node information about our own system. static NodeInfo current_node; -// Mapping of bind node ids to their respective events. -static std::unordered_map> bind_node_events; +struct BindNodeData { + u32 bind_node_id; ///< Id of the bind node associated with this data. + u8 channel; ///< Channel that this bind node was bound to. + u16 network_node_id; ///< Node id this bind node is associated with, only packets from this + /// network node will be received. + Kernel::SharedPtr event; ///< Receive event for this bind node. + std::deque> received_packets; ///< List of packets received on this channel. +}; + +// Mapping of data channels to their internal data. +static std::unordered_map channel_data; // The WiFi network channel that the network is currently on. // Since we're not actually interacting with physical radio waves, this is just a dummy value. @@ -75,6 +93,9 @@ constexpr size_t MaxBeaconFrames = 15; // List of the last beacons received from the network. static std::list received_beacons; +// Network node id used when a SecureData packet is addressed to every connected node. +constexpr u16 BroadcastNetworkNodeId = 0xFFFF; + /** * Returns a list of received 802.11 beacon frames from the specified sender since the last call. */ @@ -159,7 +180,9 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { } static void HandleEAPoLPacket(const Network::WifiPacket& packet) { - std::lock_guard lock(connection_status_mutex); + std::unique_lock hle_lock(HLE::g_hle_lock, std::defer_lock); + std::unique_lock lock(connection_status_mutex, std::defer_lock); + std::lock(hle_lock, lock); if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { @@ -205,10 +228,9 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) { SendPacket(eapol_logoff); // TODO(B3N30): Broadcast updated node list // The 3ds does this presumably to support spectators. - std::lock_guard lock(HLE::g_hle_lock); connection_status_event->Signal(); } else { - if (connection_status.status != static_cast(NetworkStatus::NotConnected)) { + if (connection_status.status != static_cast(NetworkStatus::Connecting)) { LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", connection_status.status); return; @@ -237,11 +259,63 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) { // Some games require ConnectToNetwork to block, for now it doesn't // If blocking is implemented this lock needs to be changed, // otherwise it might cause deadlocks - std::lock_guard lock(HLE::g_hle_lock); connection_status_event->Signal(); } } +static void HandleSecureDataPacket(const Network::WifiPacket& packet) { + auto secure_data = ParseSecureDataHeader(packet.data); + std::unique_lock hle_lock(HLE::g_hle_lock, std::defer_lock); + std::unique_lock lock(connection_status_mutex, std::defer_lock); + std::lock(hle_lock, lock); + + if (secure_data.src_node_id == connection_status.network_node_id) { + // Ignore packets that came from ourselves. + return; + } + + if (secure_data.dest_node_id != connection_status.network_node_id && + secure_data.dest_node_id != BroadcastNetworkNodeId) { + // The packet wasn't addressed to us, we can only act as a router if we're the host. + // However, we might have received this packet due to a broadcast from the host, in that + // case just ignore it. + ASSERT_MSG(packet.destination_address == Network::BroadcastMac || + connection_status.status == static_cast(NetworkStatus::ConnectedAsHost), + "Can't be a router if we're not a host"); + + if (connection_status.status == static_cast(NetworkStatus::ConnectedAsHost) && + secure_data.dest_node_id != BroadcastNetworkNodeId) { + // Broadcast the packet so the right receiver can get it. + // TODO(B3N30): Is there a flag that makes this kind of routing be unicast instead of + // multicast? Perhaps this is a way to allow spectators to see some of the packets. + Network::WifiPacket out_packet = packet; + out_packet.destination_address = Network::BroadcastMac; + SendPacket(out_packet); + } + return; + } + + // The packet is addressed to us (or to everyone using the broadcast node id), handle it. + // TODO(B3N30): We don't currently send nor handle management frames. + ASSERT(!secure_data.is_management); + + // TODO(B3N30): Allow more than one bind node per channel. + auto channel_info = channel_data.find(secure_data.data_channel); + // Ignore packets from channels we're not interested in. + if (channel_info == channel_data.end()) + return; + + if (channel_info->second.network_node_id != BroadcastNetworkNodeId && + channel_info->second.network_node_id != secure_data.src_node_id) + return; + + // Add the received packet to the data queue. + channel_info->second.received_packets.emplace_back(packet.data); + + // Signal the data event. We can do this directly because we locked g_hle_lock + channel_info->second.event->Signal(); +} + /* * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11 * authentication frame with SEQ1. @@ -251,7 +325,7 @@ void StartConnectionSequence(const MacAddress& server) { WifiPacket auth_request; { std::lock_guard lock(connection_status_mutex); - ASSERT(connection_status.status == static_cast(NetworkStatus::NotConnected)); + connection_status.status = static_cast(NetworkStatus::Connecting); // TODO(Subv): Handle timeout. @@ -329,7 +403,7 @@ static void HandleDataFrame(const Network::WifiPacket& packet) { HandleEAPoLPacket(packet); break; case EtherType::SecureData: - // TODO(B3N30): Handle SecureData packets + HandleSecureDataPacket(packet); break; } } @@ -482,6 +556,8 @@ static void InitializeWithVersion(Interface* self) { recv_buffer_memory = Kernel::g_handle_table.Get(sharedmem_handle); + initialized = true; + ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); { @@ -535,6 +611,48 @@ static void GetConnectionStatus(Interface* self) { LOG_DEBUG(Service_NWM, "called"); } +/** + * NWM_UDS::GetNodeInformation service function. + * Returns the node inforamtion structure for the currently connected node. + * Inputs: + * 0 : Command header. + * 1 : Node ID. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2-11 : NodeInfo structure. + */ +static void GetNodeInformation(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 1, 0); + u16 network_node_id = rp.Pop(); + + if (!initialized) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotInitialized, ErrorModule::UDS, + ErrorSummary::StatusChanged, ErrorLevel::Status)); + return; + } + + { + std::lock_guard lock(connection_status_mutex); + auto itr = std::find_if(node_info.begin(), node_info.end(), + [network_node_id](const NodeInfo& node) { + return node.network_node_id == network_node_id; + }); + if (itr == node_info.end()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(11, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(*itr); + } + LOG_DEBUG(Service_NWM, "called"); +} + /** * NWM_UDS::Bind service function. * Binds a BindNodeId to a data channel and retrieves a data event. @@ -557,29 +675,85 @@ static void Bind(Interface* self) { u8 data_channel = rp.Pop(); u16 network_node_id = rp.Pop(); - // TODO(Subv): Store the data channel and verify it when receiving data frames. - LOG_DEBUG(Service_NWM, "called"); - if (data_channel == 0) { + if (data_channel == 0 || bind_node_id == 0) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, ErrorSummary::WrongArgument, ErrorLevel::Usage)); return; } + constexpr size_t MaxBindNodes = 16; + if (channel_data.size() >= MaxBindNodes) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::OutOfMemory, ErrorModule::UDS, + ErrorSummary::OutOfResource, ErrorLevel::Status)); + return; + } + + constexpr u32 MinRecvBufferSize = 0x5F4; + if (recv_buffer_size < MinRecvBufferSize) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } + // Create a new event for this bind node. - // TODO(Subv): Signal this event when new data is received on this data channel. auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::BindNodeEvent" + std::to_string(bind_node_id)); - bind_node_events[bind_node_id] = event; + std::lock_guard lock(connection_status_mutex); + + ASSERT(channel_data.find(data_channel) == channel_data.end()); + // TODO(B3N30): Support more than one bind node per channel. + channel_data[data_channel] = {bind_node_id, data_channel, network_node_id, event}; IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap()); } +/** + * NWM_UDS::Unbind service function. + * Unbinds a BindNodeId from a data channel. + * Inputs: + * 1 : BindNodeId + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void Unbind(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 1, 0); + + u32 bind_node_id = rp.Pop(); + if (bind_node_id == 0) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } + + std::lock_guard lock(connection_status_mutex); + + auto itr = + std::find_if(channel_data.begin(), channel_data.end(), [bind_node_id](const auto& data) { + return data.second.bind_node_id == bind_node_id; + }); + + if (itr != channel_data.end()) { + channel_data.erase(itr); + } + + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(bind_node_id); + // TODO(B3N30): Find out what the other return values are + rb.Push(0); + rb.Push(0); + rb.Push(0); +} + /** * NWM_UDS::BeginHostingNetwork service function. * Creates a network and starts broadcasting its presence. @@ -606,13 +780,14 @@ static void BeginHostingNetwork(Interface* self) { LOG_DEBUG(Service_NWM, "called"); - Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo)); - - // The real UDS module throws a fatal error if this assert fails. - ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); - { std::lock_guard lock(connection_status_mutex); + + Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo)); + + // The real UDS module throws a fatal error if this assert fails. + ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); + connection_status.status = static_cast(NetworkStatus::ConnectedAsHost); // Ensure the application data size is less than the maximum value. @@ -626,11 +801,13 @@ static void BeginHostingNetwork(Interface* self) { connection_status.max_nodes = network_info.max_nodes; // Resize the nodes list to hold max_nodes. + node_info.clear(); node_info.resize(network_info.max_nodes); // There's currently only one node in the network (the host). connection_status.total_nodes = 1; network_info.total_nodes = 1; + // The host is always the first node connection_status.network_node_id = 1; current_node.network_node_id = 1; @@ -639,12 +816,22 @@ static void BeginHostingNetwork(Interface* self) { connection_status.node_bitmask |= 1; // Notify the application that the first node was set. connection_status.changed_nodes |= 1; - node_info[0] = current_node; - } - // If the game has a preferred channel, use that instead. - if (network_info.channel != 0) - network_channel = network_info.channel; + if (auto room_member = Network::GetRoomMember().lock()) { + if (room_member->IsConnected()) { + network_info.host_mac_address = room_member->GetMacAddress(); + } else { + network_info.host_mac_address = {{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; + } + } + node_info[0] = current_node; + + // If the game has a preferred channel, use that instead. + if (network_info.channel != 0) + network_channel = network_info.channel; + else + network_info.channel = DefaultNetworkChannel; + } connection_status_event->Signal(); @@ -652,8 +839,7 @@ static void BeginHostingNetwork(Interface* self) { CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU), beacon_broadcast_event, 0); - LOG_WARNING(Service_NWM, - "An UDS network has been created, but broadcasting it is unimplemented."); + LOG_DEBUG(Service_NWM, "An UDS network has been created."); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -722,31 +908,25 @@ static void SendTo(Interface* self) { size_t desc_size; const VAddr input_address = rp.PopStaticBuffer(&desc_size, false); - ASSERT(desc_size == data_size); + ASSERT(desc_size >= data_size); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - u16 network_node_id; - - { - std::lock_guard lock(connection_status_mutex); - if (connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && - connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { - rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, - ErrorSummary::InvalidState, ErrorLevel::Status)); - return; - } - - if (dest_node_id == connection_status.network_node_id) { - rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, - ErrorSummary::WrongArgument, ErrorLevel::Status)); - return; - } - - network_node_id = connection_status.network_node_id; + std::lock_guard lock(connection_status_mutex); + if (connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && + connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; } - // TODO(Subv): Do something with the flags. + if (dest_node_id == connection_status.network_node_id) { + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + return; + } + + // TODO(B3N30): Do something with the flags. constexpr size_t MaxSize = 0x5C6; if (data_size > MaxSize) { @@ -758,20 +938,116 @@ static void SendTo(Interface* self) { std::vector data(data_size); Memory::ReadBlock(input_address, data.data(), data.size()); - // TODO(Subv): Increment the sequence number after each sent packet. + // TODO(B3N30): Increment the sequence number after each sent packet. u16 sequence_number = 0; - std::vector data_payload = - GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number); + std::vector data_payload = GenerateDataPayload( + data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); - // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt + // TODO(B3N30): Retrieve the MAC address of the dest_node_id and our own to encrypt // and encapsulate the payload. - // TODO(Subv): Send the frame. + Network::WifiPacket packet; + // Data frames are sent to the host, who then decides where to route it to. If we're the host, + // just directly broadcast the frame. + if (connection_status.status == static_cast(NetworkStatus::ConnectedAsHost)) + packet.destination_address = Network::BroadcastMac; + else + packet.destination_address = network_info.host_mac_address; + packet.channel = network_channel; + packet.data = std::move(data_payload); + packet.type = Network::WifiPacket::PacketType::Data; + + SendPacket(packet); rb.Push(RESULT_SUCCESS); +} - LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u", - static_cast(dest_node_id), data_size, flags, static_cast(data_channel)); +/** + * NWM_UDS::PullPacket service function. + * Receives a data frame from the specified bind node id + * Inputs: + * 0 : Command header. + * 1 : Bind node id. + * 2 : Max out buff size >> 2. + * 3 : Max out buff size. + * 64 : Output buffer descriptor + * 65 : Output buffer address + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Received data size + * 3 : u16 Source network node id + * 4 : Buffer descriptor + * 5 : Buffer address + */ +static void PullPacket(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 3, 0); + + u32 bind_node_id = rp.Pop(); + u32 max_out_buff_size_aligned = rp.Pop(); + u32 max_out_buff_size = rp.Pop(); + + size_t desc_size; + const VAddr output_address = rp.PeekStaticBuffer(0, &desc_size); + ASSERT(desc_size == max_out_buff_size); + + std::lock_guard lock(connection_status_mutex); + if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost) && + connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && + connection_status.status != static_cast(NetworkStatus::ConnectedAsSpectator)) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + auto channel = + std::find_if(channel_data.begin(), channel_data.end(), [bind_node_id](const auto& data) { + return data.second.bind_node_id == bind_node_id; + }); + + if (channel == channel_data.end()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } + + if (channel->second.received_packets.empty()) { + Memory::ZeroBlock(output_address, desc_size); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + rb.Push(0); + rb.PushStaticBuffer(output_address, desc_size, 0); + return; + } + + const auto& next_packet = channel->second.received_packets.front(); + + auto secure_data = ParseSecureDataHeader(next_packet); + auto data_size = secure_data.GetActualDataSize(); + + if (data_size > max_out_buff_size) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); + Memory::ZeroBlock(output_address, desc_size); + // Write the actual data. + Memory::WriteBlock(output_address, + next_packet.data() + sizeof(LLCHeader) + sizeof(SecureDataHeader), + data_size); + + rb.Push(RESULT_SUCCESS); + rb.Push(data_size); + rb.Push(secure_data.src_node_id); + rb.PushStaticBuffer(output_address, desc_size, 0); + + channel->second.received_packets.pop_front(); } /** @@ -986,14 +1262,14 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00090442, nullptr, "ConnectNetwork (deprecated)"}, {0x000A0000, nullptr, "DisconnectNetwork"}, {0x000B0000, GetConnectionStatus, "GetConnectionStatus"}, - {0x000D0040, nullptr, "GetNodeInformation"}, + {0x000D0040, GetNodeInformation, "GetNodeInformation"}, {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"}, {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, {0x00100042, SetApplicationData, "SetApplicationData"}, {0x00110040, nullptr, "GetApplicationData"}, {0x00120100, Bind, "Bind"}, - {0x00130040, nullptr, "Unbind"}, - {0x001400C0, nullptr, "PullPacket"}, + {0x00130040, Unbind, "Unbind"}, + {0x001400C0, PullPacket, "PullPacket"}, {0x00150080, nullptr, "SetMaxSendDelay"}, {0x00170182, SendTo, "SendTo"}, {0x001A0000, GetChannel, "GetChannel"}, @@ -1018,9 +1294,10 @@ NWM_UDS::NWM_UDS() { NWM_UDS::~NWM_UDS() { network_info = {}; - bind_node_events.clear(); + channel_data.clear(); connection_status_event = nullptr; recv_buffer_memory = nullptr; + initialized = false; { std::lock_guard lock(connection_status_mutex); diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index cbeb75dfa..6a693c079 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp @@ -279,6 +279,15 @@ std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 return buffer; } +SecureDataHeader ParseSecureDataHeader(const std::vector& data) { + SecureDataHeader header; + + // Skip the LLC header + std::memcpy(&header, data.data() + sizeof(LLCHeader), sizeof(header)); + + return header; +} + std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { EAPoLStartPacket eapol_start{}; eapol_start.association_id = association_id; diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index 76bccb1bf..59906f677 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h @@ -51,6 +51,10 @@ struct SecureDataHeader { u16_be sequence_number; u16_be dest_node_id; u16_be src_node_id; + + u32 GetActualDataSize() const { + return protocol_size - sizeof(SecureDataHeader); + } }; static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size"); @@ -118,6 +122,11 @@ static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wro std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number); +/* + * Returns the SecureDataHeader stored in an 802.11 data frame. + */ +SecureDataHeader ParseSecureDataHeader(const std::vector& data); + /* * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS * communication. diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index f267aad74..8fc801b77 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -216,11 +216,11 @@ void Init() { SM::ServiceManager::InstallInterfaces(SM::g_service_manager); NS::InstallInterfaces(*SM::g_service_manager); + AC::InstallInterfaces(*SM::g_service_manager); AddNamedPort(new ERR::ERR_F); FS::ArchiveInit(); - AC::Init(); ACT::Init(); AM::Init(); APT::Init(); @@ -273,7 +273,6 @@ void Shutdown() { BOSS::Shutdown(); APT::Shutdown(); AM::Shutdown(); - AC::Shutdown(); FS::ArchiveShutdown(); SM::g_service_manager = nullptr; diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index 5c955cf54..4c3dd69ce 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -92,7 +92,7 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { if (name_len > Service::kMaxPortSize) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERR_INVALID_NAME_SIZE); - LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len); + LOG_ERROR(Service_SRV, "called name_len=0x%zX -> ERR_INVALID_NAME_SIZE", name_len); return; } std::string name(name_buf.data(), name_len); @@ -122,7 +122,8 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { rb.Push(ERR_MAX_CONNECTIONS_REACHED); rb.PushObjects(std::move(client_port).Unwrap()); } else { - LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); + LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), + session.Code().raw); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(session.Code()); } diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index acc36bedb..21136772a 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -779,11 +779,7 @@ static void Connect(Interface* self) { } static void InitializeSockets(Interface* self) { -// TODO(Subv): Implement -#ifdef _WIN32 - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); -#endif + // TODO(Subv): Implement u32* cmd_buffer = Kernel::GetCommandBuffer(); cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); @@ -794,10 +790,6 @@ static void ShutdownSockets(Interface* self) { // TODO(Subv): Implement CleanupSockets(); -#ifdef _WIN32 - WSACleanup(); -#endif - u32* cmd_buffer = Kernel::GetCommandBuffer(); cmd_buffer[1] = 0; } @@ -904,6 +896,11 @@ const Interface::FunctionInfo FunctionTable[] = { SOC_U::SOC_U() { Register(FunctionTable); + +#ifdef _WIN32 + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); +#endif } SOC_U::~SOC_U() { diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 57172ddd6..3f1a0c452 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -91,7 +91,7 @@ static void SetInputFormat(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast(conversion.input_format)); } static void GetInputFormat(Interface* self) { @@ -101,7 +101,7 @@ static void GetInputFormat(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.input_format); - LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast(conversion.input_format)); } static void SetOutputFormat(Interface* self) { @@ -112,7 +112,7 @@ static void SetOutputFormat(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast(conversion.output_format)); } static void GetOutputFormat(Interface* self) { @@ -122,7 +122,7 @@ static void GetOutputFormat(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.output_format); - LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast(conversion.output_format)); } static void SetRotation(Interface* self) { @@ -133,7 +133,7 @@ static void SetRotation(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast(conversion.rotation)); } static void GetRotation(Interface* self) { @@ -143,7 +143,7 @@ static void GetRotation(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.rotation); - LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast(conversion.rotation)); } static void SetBlockAlignment(Interface* self) { @@ -154,7 +154,8 @@ static void SetBlockAlignment(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", + static_cast(conversion.block_alignment)); } static void GetBlockAlignment(Interface* self) { @@ -164,7 +165,8 @@ static void GetBlockAlignment(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = static_cast(conversion.block_alignment); - LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", + static_cast(conversion.block_alignment)); } /** @@ -664,9 +666,10 @@ cleanup: Service_Y2R, "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX", - params->input_format, params->output_format, params->rotation, params->block_alignment, - params->input_line_width, params->input_lines, params->standard_coefficient, - params->padding, params->alpha); + static_cast(params->input_format), static_cast(params->output_format), + static_cast(params->rotation), static_cast(params->block_alignment), + params->input_line_width, params->input_lines, + static_cast(params->standard_coefficient), params->padding, params->alpha); } static void PingProcess(Interface* self) { diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index e8ca419d5..fe06af8f8 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -818,9 +818,7 @@ static ResultCode ReleaseMutex(Kernel::Handle handle) { if (mutex == nullptr) return ERR_INVALID_HANDLE; - mutex->Release(); - - return RESULT_SUCCESS; + return mutex->Release(Kernel::GetCurrentThread()); } /// Get the ID of the specified process diff --git a/src/core/hw/aes/ccm.cpp b/src/core/hw/aes/ccm.cpp index dc7035ab6..b28b579f3 100644 --- a/src/core/hw/aes/ccm.cpp +++ b/src/core/hw/aes/ccm.cpp @@ -46,7 +46,7 @@ public: std::vector EncryptSignCCM(const std::vector& pdata, const CCMNonce& nonce, size_t slot_id) { if (!IsNormalKeyAvailable(slot_id)) { - LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id); + LOG_ERROR(HW_AES, "Key slot %zu not available. Will use zero key.", slot_id); } const AESKey normal = GetNormalKey(slot_id); std::vector cipher(pdata.size() + CCM_MAC_SIZE); @@ -67,7 +67,7 @@ std::vector EncryptSignCCM(const std::vector& pdata, const CCMNonce& non std::vector DecryptVerifyCCM(const std::vector& cipher, const CCMNonce& nonce, size_t slot_id) { if (!IsNormalKeyAvailable(slot_id)) { - LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id); + LOG_ERROR(HW_AES, "Key slot %zu not available. Will use zero key.", slot_id); } const AESKey normal = GetNormalKey(slot_id); const std::size_t pdata_size = cipher.size() - CCM_MAC_SIZE; diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 83ad9d898..03751dcac 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -65,7 +65,7 @@ static Math::Vec4 DecodePixel(Regs::PixelFormat input_format, const u8* src_ return Color::DecodeRGBA4(src_pixel); default: - LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", input_format); + LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", static_cast(input_format)); return {0, 0, 0, 0}; } } @@ -303,7 +303,7 @@ static void DisplayTransfer(const Regs::DisplayTransferConfig& config) { default: LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", - config.output_format.Value()); + static_cast(config.output_format.Value())); break; } } diff --git a/src/video_core/regs_framebuffer.h b/src/video_core/regs_framebuffer.h index 7b565f911..8020faee8 100644 --- a/src/video_core/regs_framebuffer.h +++ b/src/video_core/regs_framebuffer.h @@ -193,7 +193,7 @@ struct FramebufferRegs { case ColorFormat::RGBA4: return 2; default: - LOG_CRITICAL(HW_GPU, "Unknown color format %u", format); + LOG_CRITICAL(HW_GPU, "Unknown color format %u", static_cast(format)); UNIMPLEMENTED(); } } @@ -258,7 +258,7 @@ struct FramebufferRegs { return 4; } - ASSERT_MSG(false, "Unknown depth format %u", format); + ASSERT_MSG(false, "Unknown depth format %u", static_cast(format)); } // Returns the number of bits per depth component of the specified depth format @@ -271,7 +271,7 @@ struct FramebufferRegs { return 24; } - ASSERT_MSG(false, "Unknown depth format %u", format); + ASSERT_MSG(false, "Unknown depth format %u", static_cast(format)); } INSERT_PADDING_WORDS(0x20); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7e09e4712..16f0726ad 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1338,7 +1338,8 @@ void RasterizerOpenGL::SyncCullMode() { break; default: - LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %d", regs.rasterizer.cull_mode.Value()); + LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %u", + static_cast(regs.rasterizer.cull_mode.Value())); UNIMPLEMENTED(); break; } diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 9fe183944..531247d2a 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -265,7 +265,7 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config, break; default: out += "vec4(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); + LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", static_cast(source)); break; } } @@ -323,7 +323,7 @@ static void AppendColorModifier(std::string& out, const PicaShaderConfig& config break; default: out += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); + LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", static_cast(modifier)); break; } } @@ -372,7 +372,7 @@ static void AppendAlphaModifier(std::string& out, const PicaShaderConfig& config break; default: out += "0.0"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); + LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", static_cast(modifier)); break; } } @@ -416,7 +416,8 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper break; default: out += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); + LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", + static_cast(operation)); break; } out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0 @@ -456,7 +457,8 @@ static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation oper break; default: out += "0.0"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation); + LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", + static_cast(operation)); break; } out += ", 0.0, 1.0)"; @@ -486,7 +488,7 @@ static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareF default: out += "false"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); + LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", static_cast(func)); break; } } diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index c7fa1f873..640c6ea51 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -103,7 +103,7 @@ inline GLenum BlendEquation(Pica::FramebufferRegs::BlendEquation equation) { // Range check table for input if (static_cast(equation) >= ARRAY_SIZE(blend_equation_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown blend equation %d", equation); + LOG_CRITICAL(Render_OpenGL, "Unknown blend equation %u", static_cast(equation)); UNREACHABLE(); return GL_FUNC_ADD; @@ -133,7 +133,7 @@ inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) { // Range check table for input if (static_cast(factor) >= ARRAY_SIZE(blend_func_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor); + LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %u", static_cast(factor)); UNREACHABLE(); return GL_ONE; @@ -164,7 +164,7 @@ inline GLenum LogicOp(Pica::FramebufferRegs::LogicOp op) { // Range check table for input if (static_cast(op) >= ARRAY_SIZE(logic_op_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op); + LOG_CRITICAL(Render_OpenGL, "Unknown logic op %u", static_cast(op)); UNREACHABLE(); return GL_COPY; @@ -187,7 +187,7 @@ inline GLenum CompareFunc(Pica::FramebufferRegs::CompareFunc func) { // Range check table for input if (static_cast(func) >= ARRAY_SIZE(compare_func_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func); + LOG_CRITICAL(Render_OpenGL, "Unknown compare function %u", static_cast(func)); UNREACHABLE(); return GL_ALWAYS; @@ -210,7 +210,7 @@ inline GLenum StencilOp(Pica::FramebufferRegs::StencilAction action) { // Range check table for input if (static_cast(action) >= ARRAY_SIZE(stencil_op_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action); + LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %u", static_cast(action)); UNREACHABLE(); return GL_KEEP; diff --git a/src/video_core/shader/shader_jit_x64_compiler.cpp b/src/video_core/shader/shader_jit_x64_compiler.cpp index 1b31623bd..5a856dcaa 100644 --- a/src/video_core/shader/shader_jit_x64_compiler.cpp +++ b/src/video_core/shader/shader_jit_x64_compiler.cpp @@ -853,7 +853,7 @@ void JitShader::Compile_NextInstr() { } else { // Unhandled instruction LOG_CRITICAL(HW_GPU, "Unhandled instruction: 0x%02x (0x%08x)", - instr.opcode.Value().EffectiveOpCode(), instr.hex); + static_cast(instr.opcode.Value().EffectiveOpCode()), instr.hex); } } diff --git a/src/video_core/swrasterizer/framebuffer.cpp b/src/video_core/swrasterizer/framebuffer.cpp index f34eab6cf..fb41ce4f5 100644 --- a/src/video_core/swrasterizer/framebuffer.cpp +++ b/src/video_core/swrasterizer/framebuffer.cpp @@ -58,7 +58,7 @@ void DrawPixel(int x, int y, const Math::Vec4& color) { default: LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", - framebuffer.color_format.Value()); + static_cast(framebuffer.color_format.Value())); UNIMPLEMENTED(); } } @@ -94,7 +94,7 @@ const Math::Vec4 GetPixel(int x, int y) { default: LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", - framebuffer.color_format.Value()); + static_cast(framebuffer.color_format.Value())); UNIMPLEMENTED(); } @@ -123,7 +123,8 @@ u32 GetDepth(int x, int y) { case FramebufferRegs::DepthFormat::D24S8: return Color::DecodeD24S8(src_pixel).x; default: - LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); + LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", + static_cast(framebuffer.depth_format.Value())); UNIMPLEMENTED(); return 0; } @@ -151,7 +152,7 @@ u8 GetStencil(int x, int y) { LOG_WARNING( HW_GPU, "GetStencil called for function which doesn't have a stencil component (format %u)", - framebuffer.depth_format); + static_cast(framebuffer.depth_format.Value())); return 0; } } @@ -184,7 +185,8 @@ void SetDepth(int x, int y, u32 value) { break; default: - LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); + LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", + static_cast(framebuffer.depth_format.Value())); UNIMPLEMENTED(); break; } @@ -215,7 +217,8 @@ void SetStencil(int x, int y, u8 value) { break; default: - LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); + LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", + static_cast(framebuffer.depth_format.Value())); UNIMPLEMENTED(); break; } @@ -294,7 +297,7 @@ Math::Vec4 EvaluateBlendEquation(const Math::Vec4& src, const Math::Vec4 break; default: - LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation); + LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation 0x%x", static_cast(equation)); UNIMPLEMENTED(); } diff --git a/src/video_core/swrasterizer/lighting.cpp b/src/video_core/swrasterizer/lighting.cpp index 5fa748611..04f81ef40 100644 --- a/src/video_core/swrasterizer/lighting.cpp +++ b/src/video_core/swrasterizer/lighting.cpp @@ -43,7 +43,8 @@ std::tuple, Math::Vec4> ComputeFragmentsColors( surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f); surface_tangent = perturbation; } else { - LOG_ERROR(HW_GPU, "Unknown bump mode %u", lighting.config0.bump_mode.Value()); + LOG_ERROR(HW_GPU, "Unknown bump mode %u", + static_cast(lighting.config0.bump_mode.Value())); } } else { surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f); diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp index 586587eb8..533ee6f01 100644 --- a/src/video_core/swrasterizer/rasterizer.cpp +++ b/src/video_core/swrasterizer/rasterizer.cpp @@ -801,7 +801,7 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve return std::min(combiner_output.a(), static_cast(255 - dest.a())); default: - LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", factor); + LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", static_cast(factor)); UNIMPLEMENTED(); break; }