Merge pull request #2 from citra-emu/master

pull request
This commit is contained in:
raven3 2017-11-05 22:55:15 +09:00 committed by GitHub
commit 88c794eff4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1842 additions and 487 deletions

View File

@ -28,6 +28,12 @@ matrix:
install: "./.travis/macos/deps.sh" install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh" script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.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: deploy:
provider: releases provider: releases

4
.travis/linux-frozen/build.sh Executable file
View File

@ -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

27
.travis/linux-frozen/docker.sh Executable file
View File

@ -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

View File

@ -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)

BIN
dist/icons/citra.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -2,5 +2,6 @@
<qresource prefix="icons"> <qresource prefix="icons">
<file>checked.png</file> <file>checked.png</file>
<file>failed.png</file> <file>failed.png</file>
<file>citra.png</file>
</qresource> </qresource>
</RCC> </RCC>

2
externals/dynarmic vendored

@ -1 +1 @@
Subproject commit 8f15e3f70cb96e56705e5de6ba97b5d09423a56b Subproject commit d3fb603287dd4b0b06b921b639ef79dc9a850cdc

2
externals/xbyak vendored

@ -1 +1 @@
Subproject commit fe4765d2fed4e990ea5e9661b6bc5fc9bf48ec16 Subproject commit c5da3778e7f84013fe8c26fcf18a67881bd1e825

View File

@ -23,8 +23,10 @@ set(SRCS
debugger/profiler.cpp debugger/profiler.cpp
debugger/registers.cpp debugger/registers.cpp
debugger/wait_tree.cpp debugger/wait_tree.cpp
updater/updater.cpp
util/spinbox.cpp util/spinbox.cpp
util/util.cpp util/util.cpp
aboutdialog.cpp
bootmanager.cpp bootmanager.cpp
game_list.cpp game_list.cpp
hotkeys.cpp hotkeys.cpp
@ -55,8 +57,11 @@ set(HEADERS
debugger/profiler.h debugger/profiler.h
debugger/registers.h debugger/registers.h
debugger/wait_tree.h debugger/wait_tree.h
updater/updater.h
updater/updater_p.h
util/spinbox.h util/spinbox.h
util/util.h util/util.h
aboutdialog.h
bootmanager.h bootmanager.h
game_list.h game_list.h
game_list_p.h game_list_p.h
@ -75,6 +80,7 @@ set(UIS
configuration/configure_system.ui configuration/configure_system.ui
configuration/configure_web.ui configuration/configure_web.ui
debugger/registers.ui debugger/registers.ui
aboutdialog.ui
hotkeys.ui hotkeys.ui
main.ui main.ui
) )

View File

@ -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;
}

View File

@ -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 <QDialog>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog {
Q_OBJECT
public:
explicit AboutDialog(QWidget* parent = 0);
~AboutDialog();
private:
Ui::AboutDialog* ui;
};
#endif // ABOUTDIALOG_H

156
src/citra_qt/aboutdialog.ui Normal file
View File

@ -0,0 +1,156 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>752</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>About Citra</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>630</x>
<y>250</y>
<width>101</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="labelLogo">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>241</width>
<height>251</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/citra.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QLabel" name="labelCitra">
<property name="geometry">
<rect>
<x>270</x>
<y>10</y>
<width>100</width>
<height>50</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;Citra&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QLabel" name="labelBuildInfo">
<property name="geometry">
<rect>
<x>270</x>
<y>70</y>
<width>451</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QLabel" name="labelAbout">
<property name="geometry">
<rect>
<x>270</x>
<y>90</y>
<width>461</width>
<height>131</height>
</rect>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Citra is a free and open source 3DS emulator &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;licensed under GPLv2.0 or any later version.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;This software should not be used to play games &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QLabel" name="labelLinks">
<property name="geometry">
<rect>
<x>270</x>
<y>230</y>
<width>361</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://citra-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://community.citra-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Forum&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/citra-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;//github.com/citra-emu/citra/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/citra-emu/citra/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QLabel" name="labelLiability">
<property name="geometry">
<rect>
<x>150</x>
<y>270</y>
<width>450</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;3DS&amp;quot; is a trademark of Nintendo. Citra is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</widget>
<resources>
<include location="../../dist/icons/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -157,6 +157,12 @@ void Config::ReadValues() {
qt_config->beginGroup("UI"); qt_config->beginGroup("UI");
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 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"); qt_config->beginGroup("UILayout");
UISettings::values.geometry = qt_config->value("geometry").toByteArray(); UISettings::values.geometry = qt_config->value("geometry").toByteArray();
UISettings::values.state = qt_config->value("state").toByteArray(); UISettings::values.state = qt_config->value("state").toByteArray();
@ -307,6 +313,11 @@ void Config::SaveValues() {
qt_config->beginGroup("UI"); qt_config->beginGroup("UI");
qt_config->setValue("theme", UISettings::values.theme); 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->beginGroup("UILayout");
qt_config->setValue("geometry", UISettings::values.geometry); qt_config->setValue("geometry", UISettings::values.geometry);
qt_config->setValue("state", UISettings::values.state); qt_config->setValue("state", UISettings::values.state);

View File

@ -20,6 +20,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
this->setConfiguration(); this->setConfiguration();
ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
ui->updateBox->setVisible(UISettings::values.updater_found);
} }
ConfigureGeneral::~ConfigureGeneral() {} ConfigureGeneral::~ConfigureGeneral() {}
@ -29,6 +30,9 @@ void ConfigureGeneral::setConfiguration() {
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit); 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 // 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); 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.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.theme = UISettings::values.theme =
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 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.region_value = ui->region_combobox->currentIndex() - 1;
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
Settings::Apply(); Settings::Apply();

View File

@ -25,16 +25,16 @@
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QCheckBox" name="toggle_deepscan"> <widget class="QCheckBox" name="toggle_check_exit">
<property name="text"> <property name="text">
<string>Search sub-directories for games</string> <string>Confirm exit while emulation is running</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="toggle_check_exit"> <widget class="QCheckBox" name="toggle_deepscan">
<property name="text"> <property name="text">
<string>Confirm exit while emulation is running</string> <string>Search sub-directories for games</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -44,24 +44,51 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="updateBox">
<property name="title"> <property name="title">
<string>Performance</string> <string>Updates</string>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_update">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_update">
<item> <item>
<widget class="QCheckBox" name="toggle_cpu_jit"> <widget class="QCheckBox" name="toggle_update_check">
<property name="text"> <property name="text">
<string>Enable CPU JIT</string> <string>Check for updates on start</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item>
</item> <widget class="QCheckBox" name="toggle_auto_update">
<property name="text">
<string>Silently auto update after closing</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Performance</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="toggle_cpu_jit">
<property name="text">
<string>Enable CPU JIT</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="groupBox_4"> <widget class="QGroupBox" name="groupBox_4">
@ -149,8 +176,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="theme_combobox"> <widget class="QComboBox" name="theme_combobox"/>
</widget>
</item> </item>
</layout> </layout>
</item> </item>

View File

@ -13,6 +13,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QtGui> #include <QtGui>
#include <QtWidgets> #include <QtWidgets>
#include "citra_qt/aboutdialog.h"
#include "citra_qt/bootmanager.h" #include "citra_qt/bootmanager.h"
#include "citra_qt/configuration/config.h" #include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/configure_dialog.h" #include "citra_qt/configuration/configure_dialog.h"
@ -29,6 +30,7 @@
#include "citra_qt/hotkeys.h" #include "citra_qt/hotkeys.h"
#include "citra_qt/main.h" #include "citra_qt/main.h"
#include "citra_qt/ui_settings.h" #include "citra_qt/ui_settings.h"
#include "citra_qt/updater/updater.h"
#include "common/logging/backend.h" #include "common/logging/backend.h"
#include "common/logging/filter.h" #include "common/logging/filter.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -99,6 +101,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
InitializeDebugWidgets(); InitializeDebugWidgets();
InitializeRecentFileMenuActions(); InitializeRecentFileMenuActions();
InitializeHotkeys(); InitializeHotkeys();
ShowUpdaterWidgets();
SetDefaultUIGeometry(); SetDefaultUIGeometry();
RestoreUIState(); RestoreUIState();
@ -117,6 +120,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
// Show one-time "callout" messages to the user // Show one-time "callout" messages to the user
ShowCallouts(); ShowCallouts();
if (UISettings::values.check_for_update_on_start) {
CheckForUpdates();
}
QStringList args = QApplication::arguments(); QStringList args = QApplication::arguments();
if (args.length() >= 2) { if (args.length() >= 2) {
BootGame(args[1]); BootGame(args[1]);
@ -138,6 +145,10 @@ void GMainWindow::InitializeWidgets() {
game_list = new GameList(this); game_list = new GameList(this);
ui.horizontalLayout->addWidget(game_list); ui.horizontalLayout->addWidget(game_list);
// Setup updater
updater = new Updater(this);
UISettings::values.updater_found = updater->HasUpdater();
// Create status bar // Create status bar
message_label = new QLabel(); message_label = new QLabel();
// Configured separately for left alignment // 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() { void GMainWindow::SetDefaultUIGeometry() {
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half // geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
const QRect screenRect = QApplication::desktop()->screenGeometry(this); 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); connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key()); ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key());
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 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) { 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?<br /><br />"
"This <b>will</b> 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) { bool GMainWindow::LoadROM(const QString& filename) {
// Shutdown previous session if the emu thread is still active... // Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr) if (emu_thread != nullptr)
@ -517,6 +611,10 @@ void GMainWindow::ShutdownGame() {
emu_frametime_label->setVisible(false); emu_frametime_label->setVisible(false);
emulation_running = false; emulation_running = false;
if (defer_update_prompt) {
ShowUpdatePrompt();
}
} }
void GMainWindow::StoreRecentFile(const QString& filename) { 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() { bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
return true; return true;

View File

@ -2,8 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#ifndef _CITRA_QT_MAIN_HXX_ #pragma once
#define _CITRA_QT_MAIN_HXX_
#include <memory> #include <memory>
#include <QMainWindow> #include <QMainWindow>
@ -24,7 +23,9 @@ class GRenderWindow;
class MicroProfileDialog; class MicroProfileDialog;
class ProfilerWidget; class ProfilerWidget;
class RegistersWidget; class RegistersWidget;
class Updater;
class WaitTreeWidget; class WaitTreeWidget;
class AboutDialog;
class GMainWindow : public QMainWindow { class GMainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
@ -81,6 +82,10 @@ private:
void ShutdownGame(); void ShutdownGame();
void ShowCallouts(); void ShowCallouts();
void ShowUpdaterWidgets();
void ShowUpdatePrompt();
void ShowNoUpdatePrompt();
void CheckForUpdates();
/** /**
* Stores the filename in the recently loaded files list. * Stores the filename in the recently loaded files list.
@ -131,6 +136,11 @@ private slots:
void ToggleWindowMode(); void ToggleWindowMode();
void OnCreateGraphicsSurfaceViewer(); void OnCreateGraphicsSurfaceViewer();
void OnCoreError(Core::System::ResultStatus, std::string); 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: private:
void UpdateStatusBar(); void UpdateStatusBar();
@ -163,6 +173,10 @@ private:
GraphicsVertexShaderWidget* graphicsVertexShaderWidget; GraphicsVertexShaderWidget* graphicsVertexShaderWidget;
GraphicsTracingWidget* graphicsTracingWidget; GraphicsTracingWidget* graphicsTracingWidget;
WaitTreeWidget* waitTreeWidget; WaitTreeWidget* waitTreeWidget;
Updater* updater;
bool explicit_update_check = false;
bool defer_update_prompt = false;
QAction* actions_recent_files[max_recent_files_item]; QAction* actions_recent_files[max_recent_files_item];
@ -171,5 +185,3 @@ protected:
void dragEnterEvent(QDragEnterEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override;
}; };
#endif // _CITRA_QT_MAIN_HXX_

View File

@ -96,6 +96,10 @@
<property name="title"> <property name="title">
<string>&amp;Help</string> <string>&amp;Help</string>
</property> </property>
<addaction name="action_Check_For_Updates"/>
<addaction name="action_Open_Maintenance_Tool"/>
<addaction name="separator"/>
<addaction name="action_FAQ"/>
<addaction name="action_About"/> <addaction name="action_About"/>
</widget> </widget>
<addaction name="menu_File"/> <addaction name="menu_File"/>
@ -142,6 +146,11 @@
<string>&amp;Stop</string> <string>&amp;Stop</string>
</property> </property>
</action> </action>
<action name="action_FAQ">
<property name="text">
<string>FAQ</string>
</property>
</action>
<action name="action_About"> <action name="action_About">
<property name="text"> <property name="text">
<string>About Citra</string> <string>About Citra</string>
@ -205,6 +214,19 @@
<string>Fullscreen</string> <string>Fullscreen</string>
</property> </property>
</action> </action>
<action name="action_Open_Maintenance_Tool">
<property name="text">
<string>Modify Citra Install</string>
</property>
<property name="toolTip">
<string>Opens the maintenance tool to modify your Citra installation</string>
</property>
</action>
<action name="action_Check_For_Updates">
<property name="text">
<string>Check for Updates</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
</ui> </ui>

View File

@ -39,6 +39,10 @@ struct Values {
bool confirm_before_closing; bool confirm_before_closing;
bool first_start; bool first_start;
bool updater_found;
bool update_on_close;
bool check_for_update_on_start;
QString roms_path; QString roms_path;
QString symbols_path; QString symbols_path;
QString gamedir; QString gamedir;

View File

@ -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 <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
#include <QXmlStreamReader>
#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<UpdaterPrivate>(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::UpdateInfo> 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>("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<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
&UpdaterPrivate::UpdaterReady, Qt::QueuedConnection);
connect(main_process, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&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<Updater::UpdateInfo>& out) {
const auto out_string = QString::fromUtf8(output);
const auto xml_begin = out_string.indexOf(QStringLiteral("<updates>"));
if (xml_begin < 0)
return XMLParseResult::NoUpdate;
const auto xml_end = out_string.indexOf(QStringLiteral("</updates>"), xml_begin);
if (xml_end < 0)
return XMLParseResult::NoUpdate;
QList<Updater::UpdateInfo> 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<Updater::UpdateInfo> 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;
}

View File

@ -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 <memory>
#include <QDateTime>
#include <QList>
#include <QScopedPointer>
#include <QString>
#include <QStringList>
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<UpdateInfo> 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<UpdateInfo> 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<Updater::UpdateInfo> update_info);
private:
std::unique_ptr<UpdaterPrivate> backend;
};

View File

@ -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 <QtCore/QProcess>
#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<Updater::UpdateInfo>& out);
Updater* parent;
QString tool_path{};
QList<Updater::UpdateInfo> 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;
};

View File

@ -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())}; ResultStatus init_result{Init(emu_window, system_mode.first.get())};
if (init_result != ResultStatus::Success) { 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<u32>(init_result));
System::Shutdown(); System::Shutdown();
return init_result; return init_result;
} }
const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)}; const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
if (Loader::ResultStatus::Success != load_result) { 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<u32>(load_result));
System::Shutdown(); System::Shutdown();
switch (load_result) { switch (load_result) {

View File

@ -302,7 +302,7 @@ static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
auto bp = p.find(addr); auto bp = p.find(addr);
if (bp != p.end()) { if (bp != p.end()) {
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", 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<int>(type));
p.erase(addr); 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)) { if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
LOG_DEBUG(Debug_GDBStub, LOG_DEBUG(Debug_GDBStub,
"Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type, "Found breakpoint type %d @ %08x, range: %08x - %08x (%u bytes)\n",
addr, bp->second.addr, bp->second.addr + len, len); static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
return true; return true;
} }
} }
@ -738,8 +738,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
breakpoint.len = len; breakpoint.len = len;
p.insert({addr, breakpoint}); p.insert({addr, breakpoint});
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len, LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n",
breakpoint.addr); static_cast<int>(type), breakpoint.len, breakpoint.addr);
return true; return true;
} }

View File

@ -62,7 +62,7 @@ ResultCode Applet::Create(Service::APT::AppletId id) {
applets[id] = std::make_shared<Mint>(id); applets[id] = std::make_shared<Mint>(id);
break; break;
default: default:
LOG_ERROR(Service_APT, "Could not create applet %u", id); LOG_ERROR(Service_APT, "Could not create applet %u", static_cast<u32>(id));
// TODO(Subv): Find the right error code // TODO(Subv): Find the right error code
return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet,
ErrorSummary::NotSupported, ErrorLevel::Permanent); ErrorSummary::NotSupported, ErrorLevel::Permanent);
@ -82,7 +82,7 @@ std::shared_ptr<Applet> Applet::Get(Service::APT::AppletId id) {
static void AppletUpdateEvent(u64 applet_id, int cycles_late) { static void AppletUpdateEvent(u64 applet_id, int cycles_late) {
Service::APT::AppletId id = static_cast<Service::APT::AppletId>(applet_id); Service::APT::AppletId id = static_cast<Service::APT::AppletId>(applet_id);
std::shared_ptr<Applet> applet = Applet::Get(id); std::shared_ptr<Applet> 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<u32>(id));
applet->Update(); applet->Update();

View File

@ -74,7 +74,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
} }
default: default:
LOG_ERROR(Kernel, "unknown type=%d", type); LOG_ERROR(Kernel, "unknown type=%d", static_cast<u32>(type));
return ERR_INVALID_ENUM_VALUE_FND; return ERR_INVALID_ENUM_VALUE_FND;
} }

View File

@ -13,6 +13,7 @@ enum {
OutOfHandles = 19, OutOfHandles = 19,
SessionClosedByRemote = 26, SessionClosedByRemote = 26,
PortNameTooLong = 30, PortNameTooLong = 30,
WrongLockingThread = 31,
NoPendingSessions = 35, NoPendingSessions = 35,
WrongPermission = 46, WrongPermission = 46,
InvalidBufferDescriptor = 48, InvalidBufferDescriptor = 48,

View File

@ -7,6 +7,7 @@
#include <boost/range/algorithm_ext/erase.hpp> #include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
@ -58,19 +59,34 @@ void Mutex::Acquire(Thread* thread) {
lock_count++; lock_count++;
} }
void Mutex::Release() { ResultCode Mutex::Release(Thread* thread) {
// Only release if the mutex is held // We can only release the mutex if it's held by the calling thread.
if (lock_count > 0) { if (thread != holding_thread) {
lock_count--; if (holding_thread) {
LOG_ERROR(
// Yield to the next thread only if we've fully released the mutex Kernel,
if (lock_count == 0) { "Tried to release a mutex (owned by thread id %u) from a different thread id %u",
holding_thread->held_mutexes.erase(this); holding_thread->thread_id, thread->thread_id);
holding_thread->UpdatePriority();
holding_thread = nullptr;
WakeupAllWaitingThreads();
Core::System::GetInstance().PrepareReschedule();
} }
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

View File

@ -8,6 +8,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/wait_object.h" #include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
namespace Kernel { namespace Kernel {
@ -52,7 +53,12 @@ public:
void AddWaitingThread(SharedPtr<Thread> thread) override; void AddWaitingThread(SharedPtr<Thread> thread) override;
void RemoveWaitingThread(Thread* 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: private:
Mutex(); Mutex();
@ -65,4 +71,4 @@ private:
*/ */
void ReleaseThreadMutexes(Thread* thread); void ReleaseThreadMutexes(Thread* thread);
} // namespace } // namespace Kernel

View File

@ -2,11 +2,10 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <array>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/ipc.h" #include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h" #include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -17,169 +16,170 @@
namespace Service { namespace Service {
namespace AC { namespace AC {
void Module::Interface::CreateDefaultConfig(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x1, 0, 0);
struct ACConfig { std::size_t desc_size;
std::array<u8, 0x200> data; 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<Kernel::Event> close_event; IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
static Kernel::SharedPtr<Kernel::Event> connect_event; rb.Push(RESULT_SUCCESS);
static Kernel::SharedPtr<Kernel::Event> disconnect_event; rb.PushStaticBuffer(ac_config_addr, sizeof(ACConfig), 0);
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
LOG_WARNING(Service_AC, "(STUBBED) called"); LOG_WARNING(Service_AC, "(STUBBED) called");
} }
void ConnectAsync(Interface* self) { void Module::Interface::ConnectAsync(Kernel::HLERequestContext& ctx) {
u32* cmd_buff = Kernel::GetCommandBuffer(); IPC::RequestParser rp(ctx, 0x4, 0, 6);
connect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); rp.Skip(2, false); // ProcessId descriptor
if (connect_event) { ac->connect_event = rp.PopObject<Kernel::Event>();
connect_event->name = "AC:connect_event";
connect_event->Signal();
ac_connected = true;
}
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
LOG_WARNING(Service_AC, "(STUBBED) called"); if (ac->connect_event) {
} ac->connect_event->name = "AC:connect_event";
ac->connect_event->Signal();
void GetConnectResult(Interface* self) { ac->ac_connected = true;
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();
} }
close_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
if (close_event) { rb.Push(RESULT_SUCCESS);
close_event->name = "AC:close_event";
close_event->Signal(); 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<Kernel::Event>();
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 ac->ac_connected = false;
LOG_WARNING(Service_AC, "(STUBBED) called");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
} }
void GetCloseResult(Interface* self) { void Module::Interface::GetCloseResult(Kernel::HLERequestContext& ctx) {
u32* cmd_buff = Kernel::GetCommandBuffer(); 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"); LOG_WARNING(Service_AC, "(STUBBED) called");
} }
void GetWifiStatus(Interface* self) { void Module::Interface::GetWifiStatus(Kernel::HLERequestContext& ctx) {
u32* cmd_buff = Kernel::GetCommandBuffer(); IPC::RequestParser rp(ctx, 0xD, 0, 0);
// TODO(purpasmart96): This function is only a stub, // TODO(purpasmart96): This function is only a stub,
// it returns a valid result without implementing full functionality. // it returns a valid result without implementing full functionality.
cmd_buff[1] = RESULT_SUCCESS.raw; // No error IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
cmd_buff[2] = 0; // Connection type set to none rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // Connection type set to none
LOG_WARNING(Service_AC, "(STUBBED) called"); LOG_WARNING(Service_AC, "(STUBBED) called");
} }
void GetInfraPriority(Interface* self) { void Module::Interface::GetInfraPriority(Kernel::HLERequestContext& ctx) {
u32* cmd_buff = Kernel::GetCommandBuffer(); IPC::RequestParser rp(ctx, 0x27, 0, 2);
VAddr ac_config = rp.PopStaticBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw; // No error IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
cmd_buff[2] = 0; // Infra Priority, default 0 rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // Infra Priority, default 0
LOG_WARNING(Service_AC, "(STUBBED) called"); LOG_WARNING(Service_AC, "(STUBBED) called");
} }
void SetRequestEulaVersion(Interface* self) { void Module::Interface::SetRequestEulaVersion(Kernel::HLERequestContext& ctx) {
u32* cmd_buff = Kernel::GetCommandBuffer(); IPC::RequestParser rp(ctx, 0x2D, 2, 2);
u32 major = cmd_buff[1] & 0xFF; u32 major = rp.Pop<u8>();
u32 minor = cmd_buff[2] & 0xFF; u32 minor = rp.Pop<u8>();
ASSERT_MSG(cmd_buff[3] == (sizeof(ACConfig) << 14 | 2), VAddr ac_config = rp.PopStaticBuffer();
"Input buffer size not equal ACConfig size");
ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2),
"Output buffer size not equal ACConfig size");
cmd_buff[1] = RESULT_SUCCESS.raw; // No error // TODO(Subv): Copy over the input ACConfig to the stored ACConfig.
cmd_buff[2] = 0; // Infra Priority
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); LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor);
} }
void RegisterDisconnectEvent(Interface* self) { void Module::Interface::RegisterDisconnectEvent(Kernel::HLERequestContext& ctx) {
u32* cmd_buff = Kernel::GetCommandBuffer(); IPC::RequestParser rp(ctx, 0x30, 0, 4);
rp.Skip(2, false); // ProcessId descriptor
disconnect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); ac->disconnect_event = rp.PopObject<Kernel::Event>();
if (disconnect_event) { if (ac->disconnect_event) {
disconnect_event->name = "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"); LOG_WARNING(Service_AC, "(STUBBED) called");
} }
void IsConnected(Interface* self) { void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) {
u32* cmd_buff = Kernel::GetCommandBuffer(); IPC::RequestParser rp(ctx, 0x3E, 1, 2);
u32 unk = rp.Pop<u32>();
u32 unk_descriptor = rp.Pop<u32>();
u32 unk_param = rp.Pop<u32>();
cmd_buff[1] = RESULT_SUCCESS.raw; // No error IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
cmd_buff[2] = ac_connected; 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) { void Module::Interface::SetClientVersion(Kernel::HLERequestContext& ctx) {
u32* cmd_buff = Kernel::GetCommandBuffer(); IPC::RequestParser rp(ctx, 0x40, 1, 2);
const u32 version = cmd_buff[1]; u32 version = rp.Pop<u32>();
self->SetVersion(version); rp.Skip(2, false); // ProcessId descriptor
LOG_WARNING(Service_AC, "(STUBBED) called, version: 0x%08X", version); 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() { Module::Interface::Interface(std::shared_ptr<Module> ac, const char* name, u32 max_session)
AddService(new AC_I); : ac(std::move(ac)), ServiceFramework(name, max_session) {}
AddService(new AC_U);
ac_connected = false; void InstallInterfaces(SM::ServiceManager& service_manager) {
auto ac = std::make_shared<Module>();
close_event = nullptr; std::make_shared<AC_I>(ac)->InstallAsService(service_manager);
connect_event = nullptr; std::make_shared<AC_U>(ac)->InstallAsService(service_manager);
disconnect_event = nullptr;
}
void Shutdown() {
ac_connected = false;
close_event = nullptr;
connect_event = nullptr;
disconnect_event = nullptr;
} }
} // namespace AC } // namespace AC

View File

@ -4,131 +4,156 @@
#pragma once #pragma once
#include <array>
#include <memory>
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/service.h"
namespace Kernel {
class Event;
}
namespace Service { namespace Service {
class Interface;
namespace AC { namespace AC {
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> ac, const char* name, u32 max_session);
/** /**
* AC::CreateDefaultConfig service function * AC::CreateDefaultConfig service function
* Inputs: * Inputs:
* 64 : ACConfig size << 14 | 2 * 64 : ACConfig size << 14 | 2
* 65 : pointer to ACConfig struct * 65 : pointer to ACConfig struct
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
*/ */
void CreateDefaultConfig(Interface* self); void CreateDefaultConfig(Kernel::HLERequestContext& ctx);
/** /**
* AC::ConnectAsync service function * AC::ConnectAsync service function
* Inputs: * Inputs:
* 1 : ProcessId Header * 1 : ProcessId Header
* 3 : Copy Handle Header * 3 : Copy Handle Header
* 4 : Connection Event handle * 4 : Connection Event handle
* 5 : ACConfig size << 14 | 2 * 5 : ACConfig size << 14 | 2
* 6 : pointer to ACConfig struct * 6 : pointer to ACConfig struct
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
*/ */
void ConnectAsync(Interface* self); void ConnectAsync(Kernel::HLERequestContext& ctx);
/** /**
* AC::GetConnectResult service function * AC::GetConnectResult service function
* Inputs: * Inputs:
* 1 : ProcessId Header * 1 : ProcessId Header
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
*/ */
void GetConnectResult(Interface* self); void GetConnectResult(Kernel::HLERequestContext& ctx);
/** /**
* AC::CloseAsync service function * AC::CloseAsync service function
* Inputs: * Inputs:
* 1 : ProcessId Header * 1 : ProcessId Header
* 3 : Copy Handle Header * 3 : Copy Handle Header
* 4 : Event handle, should be signaled when AC connection is closed * 4 : Event handle, should be signaled when AC connection is closed
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
*/ */
void CloseAsync(Interface* self); void CloseAsync(Kernel::HLERequestContext& ctx);
/** /**
* AC::GetCloseResult service function * AC::GetCloseResult service function
* Inputs: * Inputs:
* 1 : ProcessId Header * 1 : ProcessId Header
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
*/ */
void GetCloseResult(Interface* self); void GetCloseResult(Kernel::HLERequestContext& ctx);
/** /**
* AC::GetWifiStatus service function * AC::GetWifiStatus service function
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
* 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet. * 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
*/ */
void GetWifiStatus(Interface* self); void GetWifiStatus(Kernel::HLERequestContext& ctx);
/** /**
* AC::GetInfraPriority service function * AC::GetInfraPriority service function
* Inputs: * Inputs:
* 1 : ACConfig size << 14 | 2 * 1 : ACConfig size << 14 | 2
* 2 : pointer to ACConfig struct * 2 : pointer to ACConfig struct
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
* 2 : Infra Priority * 2 : Infra Priority
*/ */
void GetInfraPriority(Interface* self); void GetInfraPriority(Kernel::HLERequestContext& ctx);
/** /**
* AC::SetRequestEulaVersion service function * AC::SetRequestEulaVersion service function
* Inputs: * Inputs:
* 1 : Eula Version major * 1 : Eula Version major
* 2 : Eula Version minor * 2 : Eula Version minor
* 3 : ACConfig size << 14 | 2 * 3 : ACConfig size << 14 | 2
* 4 : Input pointer to ACConfig struct * 4 : Input pointer to ACConfig struct
* 64 : ACConfig size << 14 | 2 * 64 : ACConfig size << 14 | 2
* 65 : Output pointer to ACConfig struct * 65 : Output pointer to ACConfig struct
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
* 2 : Infra Priority * 2 : Infra Priority
*/ */
void SetRequestEulaVersion(Interface* self); void SetRequestEulaVersion(Kernel::HLERequestContext& ctx);
/** /**
* AC::RegisterDisconnectEvent service function * AC::RegisterDisconnectEvent service function
* Inputs: * Inputs:
* 1 : ProcessId Header * 1 : ProcessId Header
* 3 : Copy Handle Header * 3 : Copy Handle Header
* 4 : Event handle, should be signaled when AC connection is closed * 4 : Event handle, should be signaled when AC connection is closed
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
*/ */
void RegisterDisconnectEvent(Interface* self); void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx);
/** /**
* AC::IsConnected service function * AC::IsConnected service function
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
* 2 : bool, is connected * 2 : bool, is connected
*/ */
void IsConnected(Interface* self); void IsConnected(Kernel::HLERequestContext& ctx);
/** /**
* AC::SetClientVersion service function * AC::SetClientVersion service function
* Inputs: * Inputs:
* 1 : Used SDK Version * 1 : Used SDK Version
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 1 : Result of function, 0 on success, otherwise error code
*/ */
void SetClientVersion(Interface* self); void SetClientVersion(Kernel::HLERequestContext& ctx);
/// Initialize AC service protected:
void Init(); std::shared_ptr<Module> ac;
};
/// Shutdown AC service protected:
void Shutdown(); struct ACConfig {
std::array<u8, 0x200> data;
};
ACConfig default_config{};
bool ac_connected = false;
Kernel::SharedPtr<Kernel::Event> close_event;
Kernel::SharedPtr<Kernel::Event> connect_event;
Kernel::SharedPtr<Kernel::Event> disconnect_event;
};
void InstallInterfaces(SM::ServiceManager& service_manager);
} // namespace AC } // namespace AC
} // namespace Service } // namespace Service

View File

@ -2,37 +2,36 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "core/hle/service/ac/ac.h"
#include "core/hle/service/ac/ac_i.h" #include "core/hle/service/ac/ac_i.h"
namespace Service { namespace Service {
namespace AC { namespace AC {
const Interface::FunctionInfo FunctionTable[] = { // TODO(Subv): Find out the correct number of concurrent sessions allowed
{0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, AC_I::AC_I(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:i", 1) {
{0x00040006, ConnectAsync, "ConnectAsync"}, static const FunctionInfo functions[] = {
{0x00050002, GetConnectResult, "GetConnectResult"}, {0x00010000, &AC_I::CreateDefaultConfig, "CreateDefaultConfig"},
{0x00070002, nullptr, "CancelConnectAsync"}, {0x00040006, &AC_I::ConnectAsync, "ConnectAsync"},
{0x00080004, CloseAsync, "CloseAsync"}, {0x00050002, &AC_I::GetConnectResult, "GetConnectResult"},
{0x00090002, GetCloseResult, "GetCloseResult"}, {0x00070002, nullptr, "CancelConnectAsync"},
{0x000A0000, nullptr, "GetLastErrorCode"}, {0x00080004, &AC_I::CloseAsync, "CloseAsync"},
{0x000C0000, nullptr, "GetStatus"}, {0x00090002, &AC_I::GetCloseResult, "GetCloseResult"},
{0x000D0000, GetWifiStatus, "GetWifiStatus"}, {0x000A0000, nullptr, "GetLastErrorCode"},
{0x000E0042, nullptr, "GetCurrentAPInfo"}, {0x000C0000, nullptr, "GetStatus"},
{0x00100042, nullptr, "GetCurrentNZoneInfo"}, {0x000D0000, &AC_I::GetWifiStatus, "GetWifiStatus"},
{0x00110042, nullptr, "GetNZoneApNumService"}, {0x000E0042, nullptr, "GetCurrentAPInfo"},
{0x001D0042, nullptr, "ScanAPs"}, {0x00100042, nullptr, "GetCurrentNZoneInfo"},
{0x00240042, nullptr, "AddDenyApType"}, {0x00110042, nullptr, "GetNZoneApNumService"},
{0x00270002, GetInfraPriority, "GetInfraPriority"}, {0x001D0042, nullptr, "ScanAPs"},
{0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, {0x00240042, nullptr, "AddDenyApType"},
{0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x00270002, &AC_I::GetInfraPriority, "GetInfraPriority"},
{0x003C0042, nullptr, "GetAPSSIDList"}, {0x002D0082, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"},
{0x003E0042, IsConnected, "IsConnected"}, {0x00300004, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
{0x00400042, SetClientVersion, "SetClientVersion"}, {0x003C0042, nullptr, "GetAPSSIDList"},
}; {0x003E0042, &AC_I::IsConnected, "IsConnected"},
{0x00400042, &AC_I::SetClientVersion, "SetClientVersion"},
AC_I::AC_I() { };
Register(FunctionTable); RegisterHandlers(functions);
} }
} // namespace AC } // namespace AC

View File

@ -4,18 +4,15 @@
#pragma once #pragma once
#include "core/hle/service/service.h" #include <memory>
#include "core/hle/service/ac/ac.h"
namespace Service { namespace Service {
namespace AC { namespace AC {
class AC_I final : public Interface { class AC_I final : public Module::Interface {
public: public:
AC_I(); explicit AC_I(std::shared_ptr<Module> ac);
std::string GetPortName() const override {
return "ac:i";
}
}; };
} // namespace AC } // namespace AC

View File

@ -2,37 +2,36 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "core/hle/service/ac/ac.h"
#include "core/hle/service/ac/ac_u.h" #include "core/hle/service/ac/ac_u.h"
namespace Service { namespace Service {
namespace AC { namespace AC {
const Interface::FunctionInfo FunctionTable[] = { // TODO(Subv): Find out the correct number of concurrent sessions allowed
{0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, AC_U::AC_U(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:u", 1) {
{0x00040006, ConnectAsync, "ConnectAsync"}, static const FunctionInfo functions[] = {
{0x00050002, GetConnectResult, "GetConnectResult"}, {0x00010000, &AC_U::CreateDefaultConfig, "CreateDefaultConfig"},
{0x00070002, nullptr, "CancelConnectAsync"}, {0x00040006, &AC_U::ConnectAsync, "ConnectAsync"},
{0x00080004, CloseAsync, "CloseAsync"}, {0x00050002, &AC_U::GetConnectResult, "GetConnectResult"},
{0x00090002, GetCloseResult, "GetCloseResult"}, {0x00070002, nullptr, "CancelConnectAsync"},
{0x000A0000, nullptr, "GetLastErrorCode"}, {0x00080004, &AC_U::CloseAsync, "CloseAsync"},
{0x000C0000, nullptr, "GetStatus"}, {0x00090002, &AC_U::GetCloseResult, "GetCloseResult"},
{0x000D0000, GetWifiStatus, "GetWifiStatus"}, {0x000A0000, nullptr, "GetLastErrorCode"},
{0x000E0042, nullptr, "GetCurrentAPInfo"}, {0x000C0000, nullptr, "GetStatus"},
{0x00100042, nullptr, "GetCurrentNZoneInfo"}, {0x000D0000, &AC_U::GetWifiStatus, "GetWifiStatus"},
{0x00110042, nullptr, "GetNZoneApNumService"}, {0x000E0042, nullptr, "GetCurrentAPInfo"},
{0x001D0042, nullptr, "ScanAPs"}, {0x00100042, nullptr, "GetCurrentNZoneInfo"},
{0x00240042, nullptr, "AddDenyApType"}, {0x00110042, nullptr, "GetNZoneApNumService"},
{0x00270002, GetInfraPriority, "GetInfraPriority"}, {0x001D0042, nullptr, "ScanAPs"},
{0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, {0x00240042, nullptr, "AddDenyApType"},
{0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x00270002, &AC_U::GetInfraPriority, "GetInfraPriority"},
{0x003C0042, nullptr, "GetAPSSIDList"}, {0x002D0082, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"},
{0x003E0042, IsConnected, "IsConnected"}, {0x00300004, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
{0x00400042, SetClientVersion, "SetClientVersion"}, {0x003C0042, nullptr, "GetAPSSIDList"},
}; {0x003E0042, &AC_U::IsConnected, "IsConnected"},
{0x00400042, &AC_U::SetClientVersion, "SetClientVersion"},
AC_U::AC_U() { };
Register(FunctionTable); RegisterHandlers(functions);
} }
} // namespace AC } // namespace AC

View File

@ -4,18 +4,15 @@
#pragma once #pragma once
#include "core/hle/service/service.h" #include <memory>
#include "core/hle/service/ac/ac.h"
namespace Service { namespace Service {
namespace AC { namespace AC {
class AC_U final : public Interface { class AC_U final : public Module::Interface {
public: public:
AC_U(); explicit AC_U(std::shared_ptr<Module> ac);
std::string GetPortName() const override {
return "ac:u";
}
}; };
} // namespace AC } // namespace AC

View File

@ -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 is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
buffer_size, static_buff_size); 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) { if (!next_parameter) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 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 is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
buffer_size, static_buff_size); 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) { if (!next_parameter) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 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 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040
AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast<u32>(applet_id));
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -775,7 +775,8 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
auto applet = HLE::Applets::Applet::Get(applet_id); auto applet = HLE::Applets::Applet::Get(applet_id);
if (applet) { 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<u32>(applet_id));
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
} else { } else {
rb.Push(HLE::Applets::Applet::Create(applet_id)); 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 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040
AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast<u32>(applet_id));
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -808,7 +809,8 @@ void PreloadLibraryApplet(Service::Interface* self) {
auto applet = HLE::Applets::Applet::Get(applet_id); auto applet = HLE::Applets::Applet::Get(applet_id);
if (applet) { 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<u32>(applet_id));
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
} else { } else {
rb.Push(HLE::Applets::Applet::Create(applet_id)); rb.Push(HLE::Applets::Applet::Create(applet_id));
@ -823,7 +825,7 @@ void StartLibraryApplet(Service::Interface* self) {
Kernel::Handle handle = rp.PopHandle(); Kernel::Handle handle = rp.PopHandle();
VAddr buffer_addr = rp.PopStaticBuffer(); 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<u32>(applet_id));
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@ -867,7 +869,7 @@ void SetScreenCapPostPermission(Service::Interface* self) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); // No error rb.Push(RESULT_SUCCESS); // No error
LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
screen_capture_post_permission); static_cast<u32>(screen_capture_post_permission));
} }
void GetScreenCapPostPermission(Service::Interface* self) { void GetScreenCapPostPermission(Service::Interface* self) {
@ -877,7 +879,7 @@ void GetScreenCapPostPermission(Service::Interface* self) {
rb.Push(RESULT_SUCCESS); // No error rb.Push(RESULT_SUCCESS); // No error
rb.Push(static_cast<u32>(screen_capture_post_permission)); rb.Push(static_cast<u32>(screen_capture_post_permission));
LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
screen_capture_post_permission); static_cast<u32>(screen_capture_post_permission));
} }
void GetAppletInfo(Service::Interface* self) { void GetAppletInfo(Service::Interface* self) {
@ -899,7 +901,7 @@ void GetAppletInfo(Service::Interface* self) {
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
ErrorLevel::Status)); ErrorLevel::Status));
} }
LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id); LOG_WARNING(Service_APT, "(stubbed) called appid=%u", static_cast<u32>(app_id));
} }
void GetStartupArgument(Service::Interface* self) { 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", LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x",
startup_argument_type, parameter_size); static_cast<u32>(startup_argument_type), parameter_size);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);

View File

@ -301,7 +301,7 @@ static void WriteProcessPipe(Service::Interface* self) {
} }
ASSERT_MSG(Memory::IsValidVirtualAddress(buffer), 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<u8> message(size); std::vector<u8> message(size);
for (u32 i = 0; i < size; i++) { for (u32 i = 0; i < size; i++) {
@ -359,8 +359,8 @@ static void ReadPipeIfPossible(Service::Interface* self) {
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
ASSERT_MSG(Memory::IsValidVirtualAddress(addr), ASSERT_MSG(Memory::IsValidVirtualAddress(addr),
"Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index,
size, addr); unknown, size, addr);
cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[1] = RESULT_SUCCESS.raw; // No error
@ -403,8 +403,8 @@ static void ReadPipe(Service::Interface* self) {
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
ASSERT_MSG(Memory::IsValidVirtualAddress(addr), ASSERT_MSG(Memory::IsValidVirtualAddress(addr),
"Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index,
size, addr); unknown, size, addr);
if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <cinttypes>
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <system_error> #include <system_error>
@ -105,8 +106,8 @@ void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_ses
offset, length, address); offset, length, address);
if (offset + length > backend->GetSize()) { if (offset + length > backend->GetSize()) {
LOG_ERROR(Service_FS, LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%" PRIx64
"Reading from out of bounds offset=0x%llX length=0x%08X file_size=0x%llX", " length=0x%08X file_size=0x%" PRIx64,
offset, length, backend->GetSize()); offset, length, backend->GetSize());
} }
@ -191,7 +192,7 @@ void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_ses
// Unknown command... // Unknown command...
default: default:
LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); LOG_ERROR(Service_FS, "Unknown command=0x%08X!", static_cast<u32>(cmd));
ResultCode error = UnimplementedFunction(ErrorModule::FS); ResultCode error = UnimplementedFunction(ErrorModule::FS);
cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
return; return;
@ -231,7 +232,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> serve
// Unknown command... // Unknown command...
default: default:
LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); LOG_ERROR(Service_FS, "Unknown command=0x%08X!", static_cast<u32>(cmd));
ResultCode error = UnimplementedFunction(ErrorModule::FS); ResultCode error = UnimplementedFunction(ErrorModule::FS);
cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
return; return;
@ -297,7 +298,7 @@ ResultCode RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factor
auto& archive = result.first->second; auto& archive = result.first->second;
LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(),
id_code); static_cast<u32>(id_code));
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -472,7 +473,7 @@ ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) {
} else if (media_type == MediaType::SDMC) { } else if (media_type == MediaType::SDMC) {
media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX); media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX);
} else { } else {
LOG_ERROR(Service_FS, "Unsupported media type %u", media_type); LOG_ERROR(Service_FS, "Unsupported media type %u", static_cast<u32>(media_type));
return ResultCode(-1); // TODO(Subv): Find the right error code return ResultCode(-1); // TODO(Subv): Find the right error code
} }

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <cinttypes>
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/file_util.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); FileSys::Path file_path(filename_type, filename_size, filename_ptr);
LOG_DEBUG(Service_FS, "type=%u size=%llu data=%s", static_cast<u32>(filename_type), file_size, LOG_DEBUG(Service_FS, "type=%u size=%" PRIx64 " data=%s", static_cast<u32>(filename_type),
file_path.DebugStr().c_str()); file_size, file_path.DebugStr().c_str());
cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw;
} }

View File

@ -34,7 +34,7 @@ void EnterExclusiveState(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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<u32>(exclusive_state));
} }
void LeaveExclusiveState(Service::Interface* self) { void LeaveExclusiveState(Service::Interface* self) {
@ -43,7 +43,7 @@ void LeaveExclusiveState(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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<u32>(exclusive_state));
} }
void QueryExclusiveMode(Service::Interface* self) { void QueryExclusiveMode(Service::Interface* self) {
@ -52,7 +52,7 @@ void QueryExclusiveMode(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0); cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = static_cast<u32>(exclusive_state); cmd_buff[2] = static_cast<u32>(exclusive_state);
LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state); LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast<u32>(exclusive_state));
} }
void LockState(Service::Interface* self) { void LockState(Service::Interface* self) {
@ -61,7 +61,7 @@ void LockState(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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) { void UnlockState(Service::Interface* self) {
@ -70,7 +70,7 @@ void UnlockState(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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) { void SuspendDaemons(Service::Interface* self) {
@ -86,7 +86,7 @@ void SuspendDaemons(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x6, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x6, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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<u32>(daemon_bit_mask));
} }
void ResumeDaemons(Service::Interface* self) { void ResumeDaemons(Service::Interface* self) {
@ -101,7 +101,7 @@ void ResumeDaemons(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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<u32>(daemon_bit_mask));
} }
void SuspendScheduler(Service::Interface* self) { void SuspendScheduler(Service::Interface* self) {
@ -157,7 +157,7 @@ void SetScanInterval(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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) { void GetScanInterval(Service::Interface* self) {
@ -166,7 +166,7 @@ void GetScanInterval(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x11, 2, 0); cmd_buff[0] = IPC::MakeHeader(0x11, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = scan_interval; 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) { void SetRetryInterval(Service::Interface* self) {
@ -175,7 +175,7 @@ void SetRetryInterval(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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) { void GetRetryInterval(Service::Interface* self) {
@ -184,7 +184,7 @@ void GetRetryInterval(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x13, 2, 0); cmd_buff[0] = IPC::MakeHeader(0x13, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = retry_interval; 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) { void OverrideDefaultDaemons(Service::Interface* self) {
@ -200,7 +200,8 @@ void OverrideDefaultDaemons(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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<u32>(default_daemon_bit_mask));
} }
void ResetDefaultDaemons(Service::Interface* self) { void ResetDefaultDaemons(Service::Interface* self) {
@ -209,7 +210,8 @@ void ResetDefaultDaemons(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error 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<u32>(default_daemon_bit_mask));
} }
void GetDefaultDaemons(Service::Interface* self) { void GetDefaultDaemons(Service::Interface* self) {
@ -218,7 +220,8 @@ void GetDefaultDaemons(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = static_cast<u32>(default_daemon_bit_mask); cmd_buff[2] = static_cast<u32>(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<u32>(default_daemon_bit_mask));
} }
void ClearHalfAwakeMacFilter(Service::Interface* self) { void ClearHalfAwakeMacFilter(Service::Interface* self) {

View File

@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <atomic>
#include <cstring> #include <cstring>
#include <list> #include <list>
#include <mutex> #include <mutex>
@ -27,6 +28,12 @@
namespace Service { namespace Service {
namespace NWM { namespace NWM {
namespace ErrCodes {
enum {
NotInitialized = 2,
};
} // namespace ErrCodes
// Event that is signaled every time the connection status changes. // Event that is signaled every time the connection status changes.
static Kernel::SharedPtr<Kernel::Event> connection_status_event; static Kernel::SharedPtr<Kernel::Event> connection_status_event;
@ -37,6 +44,8 @@ static Kernel::SharedPtr<Kernel::SharedMemory> recv_buffer_memory;
// Connection status of this 3DS. // Connection status of this 3DS.
static ConnectionStatus connection_status{}; static ConnectionStatus connection_status{};
static std::atomic<bool> initialized(false);
/* Node information about the current network. /* Node information about the current network.
* The amount of elements in this vector is always the maximum number * The amount of elements in this vector is always the maximum number
* of nodes specified in the network configuration. * of nodes specified in the network configuration.
@ -47,8 +56,17 @@ static NodeList node_info;
// Node information about our own system. // Node information about our own system.
static NodeInfo current_node; static NodeInfo current_node;
// Mapping of bind node ids to their respective events. struct BindNodeData {
static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; 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<Kernel::Event> event; ///< Receive event for this bind node.
std::deque<std::vector<u8>> received_packets; ///< List of packets received on this channel.
};
// Mapping of data channels to their internal data.
static std::unordered_map<u32, BindNodeData> channel_data;
// The WiFi network channel that the network is currently on. // 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. // 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 <MaxBeaconFrames> beacons received from the network. // List of the last <MaxBeaconFrames> beacons received from the network.
static std::list<Network::WifiPacket> received_beacons; static std::list<Network::WifiPacket> 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. * 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) { static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
std::lock_guard<std::mutex> lock(connection_status_mutex); std::unique_lock<std::recursive_mutex> hle_lock(HLE::g_hle_lock, std::defer_lock);
std::unique_lock<std::mutex> lock(connection_status_mutex, std::defer_lock);
std::lock(hle_lock, lock);
if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) {
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
@ -205,10 +228,9 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
SendPacket(eapol_logoff); SendPacket(eapol_logoff);
// TODO(B3N30): Broadcast updated node list // TODO(B3N30): Broadcast updated node list
// The 3ds does this presumably to support spectators. // The 3ds does this presumably to support spectators.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
connection_status_event->Signal(); connection_status_event->Signal();
} else { } else {
if (connection_status.status != static_cast<u32>(NetworkStatus::NotConnected)) { if (connection_status.status != static_cast<u32>(NetworkStatus::Connecting)) {
LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u",
connection_status.status); connection_status.status);
return; return;
@ -237,11 +259,63 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
// Some games require ConnectToNetwork to block, for now it doesn't // Some games require ConnectToNetwork to block, for now it doesn't
// If blocking is implemented this lock needs to be changed, // If blocking is implemented this lock needs to be changed,
// otherwise it might cause deadlocks // otherwise it might cause deadlocks
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
connection_status_event->Signal(); connection_status_event->Signal();
} }
} }
static void HandleSecureDataPacket(const Network::WifiPacket& packet) {
auto secure_data = ParseSecureDataHeader(packet.data);
std::unique_lock<std::recursive_mutex> hle_lock(HLE::g_hle_lock, std::defer_lock);
std::unique_lock<std::mutex> 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<u32>(NetworkStatus::ConnectedAsHost),
"Can't be a router if we're not a host");
if (connection_status.status == static_cast<u32>(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 * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
* authentication frame with SEQ1. * authentication frame with SEQ1.
@ -251,7 +325,7 @@ void StartConnectionSequence(const MacAddress& server) {
WifiPacket auth_request; WifiPacket auth_request;
{ {
std::lock_guard<std::mutex> lock(connection_status_mutex); std::lock_guard<std::mutex> lock(connection_status_mutex);
ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected)); connection_status.status = static_cast<u32>(NetworkStatus::Connecting);
// TODO(Subv): Handle timeout. // TODO(Subv): Handle timeout.
@ -329,7 +403,7 @@ static void HandleDataFrame(const Network::WifiPacket& packet) {
HandleEAPoLPacket(packet); HandleEAPoLPacket(packet);
break; break;
case EtherType::SecureData: case EtherType::SecureData:
// TODO(B3N30): Handle SecureData packets HandleSecureDataPacket(packet);
break; break;
} }
} }
@ -482,6 +556,8 @@ static void InitializeWithVersion(Interface* self) {
recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle); recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle);
initialized = true;
ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); 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"); 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<u16>();
if (!initialized) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::NotInitialized, ErrorModule::UDS,
ErrorSummary::StatusChanged, ErrorLevel::Status));
return;
}
{
std::lock_guard<std::mutex> 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<NodeInfo>(*itr);
}
LOG_DEBUG(Service_NWM, "called");
}
/** /**
* NWM_UDS::Bind service function. * NWM_UDS::Bind service function.
* Binds a BindNodeId to a data channel and retrieves a data event. * 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<u8>(); u8 data_channel = rp.Pop<u8>();
u16 network_node_id = rp.Pop<u16>(); u16 network_node_id = rp.Pop<u16>();
// TODO(Subv): Store the data channel and verify it when receiving data frames.
LOG_DEBUG(Service_NWM, "called"); LOG_DEBUG(Service_NWM, "called");
if (data_channel == 0) { if (data_channel == 0 || bind_node_id == 0) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
ErrorSummary::WrongArgument, ErrorLevel::Usage)); ErrorSummary::WrongArgument, ErrorLevel::Usage));
return; 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. // 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, auto event = Kernel::Event::Create(Kernel::ResetType::OneShot,
"NWM::BindNodeEvent" + std::to_string(bind_node_id)); "NWM::BindNodeEvent" + std::to_string(bind_node_id));
bind_node_events[bind_node_id] = event; std::lock_guard<std::mutex> 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); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap()); 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<u32>();
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<std::mutex> 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<u32>(0);
rb.Push<u32>(0);
rb.Push<u32>(0);
}
/** /**
* NWM_UDS::BeginHostingNetwork service function. * NWM_UDS::BeginHostingNetwork service function.
* Creates a network and starts broadcasting its presence. * Creates a network and starts broadcasting its presence.
@ -606,13 +780,14 @@ static void BeginHostingNetwork(Interface* self) {
LOG_DEBUG(Service_NWM, "called"); 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<std::mutex> lock(connection_status_mutex); std::lock_guard<std::mutex> 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<u32>(NetworkStatus::ConnectedAsHost); connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
// Ensure the application data size is less than the maximum value. // 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; connection_status.max_nodes = network_info.max_nodes;
// Resize the nodes list to hold max_nodes. // Resize the nodes list to hold max_nodes.
node_info.clear();
node_info.resize(network_info.max_nodes); node_info.resize(network_info.max_nodes);
// There's currently only one node in the network (the host). // There's currently only one node in the network (the host).
connection_status.total_nodes = 1; connection_status.total_nodes = 1;
network_info.total_nodes = 1; network_info.total_nodes = 1;
// The host is always the first node // The host is always the first node
connection_status.network_node_id = 1; connection_status.network_node_id = 1;
current_node.network_node_id = 1; current_node.network_node_id = 1;
@ -639,12 +816,22 @@ static void BeginHostingNetwork(Interface* self) {
connection_status.node_bitmask |= 1; connection_status.node_bitmask |= 1;
// Notify the application that the first node was set. // Notify the application that the first node was set.
connection_status.changed_nodes |= 1; connection_status.changed_nodes |= 1;
node_info[0] = current_node;
}
// If the game has a preferred channel, use that instead. if (auto room_member = Network::GetRoomMember().lock()) {
if (network_info.channel != 0) if (room_member->IsConnected()) {
network_channel = network_info.channel; 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(); connection_status_event->Signal();
@ -652,8 +839,7 @@ static void BeginHostingNetwork(Interface* self) {
CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU), CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU),
beacon_broadcast_event, 0); beacon_broadcast_event, 0);
LOG_WARNING(Service_NWM, LOG_DEBUG(Service_NWM, "An UDS network has been created.");
"An UDS network has been created, but broadcasting it is unimplemented.");
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
@ -722,31 +908,25 @@ static void SendTo(Interface* self) {
size_t desc_size; size_t desc_size;
const VAddr input_address = rp.PopStaticBuffer(&desc_size, false); 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); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
u16 network_node_id; std::lock_guard<std::mutex> lock(connection_status_mutex);
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
{ connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
std::lock_guard<std::mutex> lock(connection_status_mutex); rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && ErrorSummary::InvalidState, ErrorLevel::Status));
connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { return;
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;
} }
// 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; constexpr size_t MaxSize = 0x5C6;
if (data_size > MaxSize) { if (data_size > MaxSize) {
@ -758,20 +938,116 @@ static void SendTo(Interface* self) {
std::vector<u8> data(data_size); std::vector<u8> data(data_size);
Memory::ReadBlock(input_address, data.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; u16 sequence_number = 0;
std::vector<u8> data_payload = std::vector<u8> data_payload = GenerateDataPayload(
GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number); 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. // 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<u32>(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); rb.Push(RESULT_SUCCESS);
}
LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u", /**
static_cast<u32>(dest_node_id), data_size, flags, static_cast<u32>(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>();
u32 max_out_buff_size_aligned = rp.Pop<u32>();
u32 max_out_buff_size = rp.Pop<u32>();
size_t desc_size;
const VAddr output_address = rp.PeekStaticBuffer(0, &desc_size);
ASSERT(desc_size == max_out_buff_size);
std::lock_guard<std::mutex> lock(connection_status_mutex);
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost) &&
connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
connection_status.status != static_cast<u32>(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<u32>(0);
rb.Push<u16>(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<u32>(data_size);
rb.Push<u16>(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)"}, {0x00090442, nullptr, "ConnectNetwork (deprecated)"},
{0x000A0000, nullptr, "DisconnectNetwork"}, {0x000A0000, nullptr, "DisconnectNetwork"},
{0x000B0000, GetConnectionStatus, "GetConnectionStatus"}, {0x000B0000, GetConnectionStatus, "GetConnectionStatus"},
{0x000D0040, nullptr, "GetNodeInformation"}, {0x000D0040, GetNodeInformation, "GetNodeInformation"},
{0x000E0006, nullptr, "DecryptBeaconData (deprecated)"}, {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"},
{0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
{0x00100042, SetApplicationData, "SetApplicationData"}, {0x00100042, SetApplicationData, "SetApplicationData"},
{0x00110040, nullptr, "GetApplicationData"}, {0x00110040, nullptr, "GetApplicationData"},
{0x00120100, Bind, "Bind"}, {0x00120100, Bind, "Bind"},
{0x00130040, nullptr, "Unbind"}, {0x00130040, Unbind, "Unbind"},
{0x001400C0, nullptr, "PullPacket"}, {0x001400C0, PullPacket, "PullPacket"},
{0x00150080, nullptr, "SetMaxSendDelay"}, {0x00150080, nullptr, "SetMaxSendDelay"},
{0x00170182, SendTo, "SendTo"}, {0x00170182, SendTo, "SendTo"},
{0x001A0000, GetChannel, "GetChannel"}, {0x001A0000, GetChannel, "GetChannel"},
@ -1018,9 +1294,10 @@ NWM_UDS::NWM_UDS() {
NWM_UDS::~NWM_UDS() { NWM_UDS::~NWM_UDS() {
network_info = {}; network_info = {};
bind_node_events.clear(); channel_data.clear();
connection_status_event = nullptr; connection_status_event = nullptr;
recv_buffer_memory = nullptr; recv_buffer_memory = nullptr;
initialized = false;
{ {
std::lock_guard<std::mutex> lock(connection_status_mutex); std::lock_guard<std::mutex> lock(connection_status_mutex);

View File

@ -279,6 +279,15 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16
return buffer; return buffer;
} }
SecureDataHeader ParseSecureDataHeader(const std::vector<u8>& data) {
SecureDataHeader header;
// Skip the LLC header
std::memcpy(&header, data.data() + sizeof(LLCHeader), sizeof(header));
return header;
}
std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) {
EAPoLStartPacket eapol_start{}; EAPoLStartPacket eapol_start{};
eapol_start.association_id = association_id; eapol_start.association_id = association_id;

View File

@ -51,6 +51,10 @@ struct SecureDataHeader {
u16_be sequence_number; u16_be sequence_number;
u16_be dest_node_id; u16_be dest_node_id;
u16_be src_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"); 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<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
u16 src_node, u16 sequence_number); u16 src_node, u16 sequence_number);
/*
* Returns the SecureDataHeader stored in an 802.11 data frame.
*/
SecureDataHeader ParseSecureDataHeader(const std::vector<u8>& data);
/* /*
* Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS
* communication. * communication.

View File

@ -216,11 +216,11 @@ void Init() {
SM::ServiceManager::InstallInterfaces(SM::g_service_manager); SM::ServiceManager::InstallInterfaces(SM::g_service_manager);
NS::InstallInterfaces(*SM::g_service_manager); NS::InstallInterfaces(*SM::g_service_manager);
AC::InstallInterfaces(*SM::g_service_manager);
AddNamedPort(new ERR::ERR_F); AddNamedPort(new ERR::ERR_F);
FS::ArchiveInit(); FS::ArchiveInit();
AC::Init();
ACT::Init(); ACT::Init();
AM::Init(); AM::Init();
APT::Init(); APT::Init();
@ -273,7 +273,6 @@ void Shutdown() {
BOSS::Shutdown(); BOSS::Shutdown();
APT::Shutdown(); APT::Shutdown();
AM::Shutdown(); AM::Shutdown();
AC::Shutdown();
FS::ArchiveShutdown(); FS::ArchiveShutdown();
SM::g_service_manager = nullptr; SM::g_service_manager = nullptr;

View File

@ -92,7 +92,7 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
if (name_len > Service::kMaxPortSize) { if (name_len > Service::kMaxPortSize) {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(ERR_INVALID_NAME_SIZE); 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; return;
} }
std::string name(name_buf.data(), name_len); 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.Push(ERR_MAX_CONNECTIONS_REACHED);
rb.PushObjects(std::move(client_port).Unwrap()); rb.PushObjects(std::move(client_port).Unwrap());
} else { } 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); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(session.Code()); rb.Push(session.Code());
} }

View File

@ -779,11 +779,7 @@ static void Connect(Interface* self) {
} }
static void InitializeSockets(Interface* self) { static void InitializeSockets(Interface* self) {
// TODO(Subv): Implement // TODO(Subv): Implement
#ifdef _WIN32
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
#endif
u32* cmd_buffer = Kernel::GetCommandBuffer(); u32* cmd_buffer = Kernel::GetCommandBuffer();
cmd_buffer[0] = IPC::MakeHeader(1, 1, 0); cmd_buffer[0] = IPC::MakeHeader(1, 1, 0);
@ -794,10 +790,6 @@ static void ShutdownSockets(Interface* self) {
// TODO(Subv): Implement // TODO(Subv): Implement
CleanupSockets(); CleanupSockets();
#ifdef _WIN32
WSACleanup();
#endif
u32* cmd_buffer = Kernel::GetCommandBuffer(); u32* cmd_buffer = Kernel::GetCommandBuffer();
cmd_buffer[1] = 0; cmd_buffer[1] = 0;
} }
@ -904,6 +896,11 @@ const Interface::FunctionInfo FunctionTable[] = {
SOC_U::SOC_U() { SOC_U::SOC_U() {
Register(FunctionTable); Register(FunctionTable);
#ifdef _WIN32
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
#endif
} }
SOC_U::~SOC_U() { SOC_U::~SOC_U() {

View File

@ -91,7 +91,7 @@ static void SetInputFormat(Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; 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<u8>(conversion.input_format));
} }
static void GetInputFormat(Interface* self) { static void GetInputFormat(Interface* self) {
@ -101,7 +101,7 @@ static void GetInputFormat(Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.input_format); cmd_buff[2] = static_cast<u32>(conversion.input_format);
LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast<u8>(conversion.input_format));
} }
static void SetOutputFormat(Interface* self) { static void SetOutputFormat(Interface* self) {
@ -112,7 +112,7 @@ static void SetOutputFormat(Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; 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<u8>(conversion.output_format));
} }
static void GetOutputFormat(Interface* self) { static void GetOutputFormat(Interface* self) {
@ -122,7 +122,7 @@ static void GetOutputFormat(Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.output_format); cmd_buff[2] = static_cast<u32>(conversion.output_format);
LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast<u8>(conversion.output_format));
} }
static void SetRotation(Interface* self) { static void SetRotation(Interface* self) {
@ -133,7 +133,7 @@ static void SetRotation(Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[1] = RESULT_SUCCESS.raw;
LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast<u8>(conversion.rotation));
} }
static void GetRotation(Interface* self) { static void GetRotation(Interface* self) {
@ -143,7 +143,7 @@ static void GetRotation(Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.rotation); cmd_buff[2] = static_cast<u32>(conversion.rotation);
LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast<u8>(conversion.rotation));
} }
static void SetBlockAlignment(Interface* self) { static void SetBlockAlignment(Interface* self) {
@ -154,7 +154,8 @@ static void SetBlockAlignment(Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw; 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<u8>(conversion.block_alignment));
} }
static void GetBlockAlignment(Interface* self) { static void GetBlockAlignment(Interface* self) {
@ -164,7 +165,8 @@ static void GetBlockAlignment(Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.block_alignment); cmd_buff[2] = static_cast<u32>(conversion.block_alignment);
LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu",
static_cast<u8>(conversion.block_alignment));
} }
/** /**
@ -664,9 +666,10 @@ cleanup:
Service_Y2R, Service_Y2R,
"called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " "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", "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, static_cast<u8>(params->input_format), static_cast<u8>(params->output_format),
params->input_line_width, params->input_lines, params->standard_coefficient, static_cast<u8>(params->rotation), static_cast<u8>(params->block_alignment),
params->padding, params->alpha); params->input_line_width, params->input_lines,
static_cast<u8>(params->standard_coefficient), params->padding, params->alpha);
} }
static void PingProcess(Interface* self) { static void PingProcess(Interface* self) {

View File

@ -818,9 +818,7 @@ static ResultCode ReleaseMutex(Kernel::Handle handle) {
if (mutex == nullptr) if (mutex == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
mutex->Release(); return mutex->Release(Kernel::GetCurrentThread());
return RESULT_SUCCESS;
} }
/// Get the ID of the specified process /// Get the ID of the specified process

View File

@ -46,7 +46,7 @@ public:
std::vector<u8> EncryptSignCCM(const std::vector<u8>& pdata, const CCMNonce& nonce, std::vector<u8> EncryptSignCCM(const std::vector<u8>& pdata, const CCMNonce& nonce,
size_t slot_id) { size_t slot_id) {
if (!IsNormalKeyAvailable(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 AESKey normal = GetNormalKey(slot_id);
std::vector<u8> cipher(pdata.size() + CCM_MAC_SIZE); std::vector<u8> cipher(pdata.size() + CCM_MAC_SIZE);
@ -67,7 +67,7 @@ std::vector<u8> EncryptSignCCM(const std::vector<u8>& pdata, const CCMNonce& non
std::vector<u8> DecryptVerifyCCM(const std::vector<u8>& cipher, const CCMNonce& nonce, std::vector<u8> DecryptVerifyCCM(const std::vector<u8>& cipher, const CCMNonce& nonce,
size_t slot_id) { size_t slot_id) {
if (!IsNormalKeyAvailable(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 AESKey normal = GetNormalKey(slot_id);
const std::size_t pdata_size = cipher.size() - CCM_MAC_SIZE; const std::size_t pdata_size = cipher.size() - CCM_MAC_SIZE;

View File

@ -65,7 +65,7 @@ static Math::Vec4<u8> DecodePixel(Regs::PixelFormat input_format, const u8* src_
return Color::DecodeRGBA4(src_pixel); return Color::DecodeRGBA4(src_pixel);
default: default:
LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", input_format); LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", static_cast<u32>(input_format));
return {0, 0, 0, 0}; return {0, 0, 0, 0};
} }
} }
@ -303,7 +303,7 @@ static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
default: default:
LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x",
config.output_format.Value()); static_cast<u32>(config.output_format.Value()));
break; break;
} }
} }

View File

@ -193,7 +193,7 @@ struct FramebufferRegs {
case ColorFormat::RGBA4: case ColorFormat::RGBA4:
return 2; return 2;
default: default:
LOG_CRITICAL(HW_GPU, "Unknown color format %u", format); LOG_CRITICAL(HW_GPU, "Unknown color format %u", static_cast<u32>(format));
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
} }
@ -258,7 +258,7 @@ struct FramebufferRegs {
return 4; return 4;
} }
ASSERT_MSG(false, "Unknown depth format %u", format); ASSERT_MSG(false, "Unknown depth format %u", static_cast<u32>(format));
} }
// Returns the number of bits per depth component of the specified depth format // Returns the number of bits per depth component of the specified depth format
@ -271,7 +271,7 @@ struct FramebufferRegs {
return 24; return 24;
} }
ASSERT_MSG(false, "Unknown depth format %u", format); ASSERT_MSG(false, "Unknown depth format %u", static_cast<u32>(format));
} }
INSERT_PADDING_WORDS(0x20); INSERT_PADDING_WORDS(0x20);

View File

@ -1338,7 +1338,8 @@ void RasterizerOpenGL::SyncCullMode() {
break; break;
default: default:
LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %d", regs.rasterizer.cull_mode.Value()); LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %u",
static_cast<u32>(regs.rasterizer.cull_mode.Value()));
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
} }

View File

@ -265,7 +265,7 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config,
break; break;
default: default:
out += "vec4(0.0)"; out += "vec4(0.0)";
LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", static_cast<u32>(source));
break; break;
} }
} }
@ -323,7 +323,7 @@ static void AppendColorModifier(std::string& out, const PicaShaderConfig& config
break; break;
default: default:
out += "vec3(0.0)"; 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<u32>(modifier));
break; break;
} }
} }
@ -372,7 +372,7 @@ static void AppendAlphaModifier(std::string& out, const PicaShaderConfig& config
break; break;
default: default:
out += "0.0"; out += "0.0";
LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", static_cast<u32>(modifier));
break; break;
} }
} }
@ -416,7 +416,8 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper
break; break;
default: default:
out += "vec3(0.0)"; 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<u32>(operation));
break; break;
} }
out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0 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; break;
default: default:
out += "0.0"; out += "0.0";
LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation); LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u",
static_cast<u32>(operation));
break; break;
} }
out += ", 0.0, 1.0)"; out += ", 0.0, 1.0)";
@ -486,7 +488,7 @@ static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareF
default: default:
out += "false"; out += "false";
LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", static_cast<u32>(func));
break; break;
} }
} }

View File

@ -103,7 +103,7 @@ inline GLenum BlendEquation(Pica::FramebufferRegs::BlendEquation equation) {
// Range check table for input // Range check table for input
if (static_cast<size_t>(equation) >= ARRAY_SIZE(blend_equation_table)) { if (static_cast<size_t>(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<u32>(equation));
UNREACHABLE(); UNREACHABLE();
return GL_FUNC_ADD; return GL_FUNC_ADD;
@ -133,7 +133,7 @@ inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) {
// Range check table for input // Range check table for input
if (static_cast<size_t>(factor) >= ARRAY_SIZE(blend_func_table)) { if (static_cast<size_t>(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<u32>(factor));
UNREACHABLE(); UNREACHABLE();
return GL_ONE; return GL_ONE;
@ -164,7 +164,7 @@ inline GLenum LogicOp(Pica::FramebufferRegs::LogicOp op) {
// Range check table for input // Range check table for input
if (static_cast<size_t>(op) >= ARRAY_SIZE(logic_op_table)) { if (static_cast<size_t>(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<u32>(op));
UNREACHABLE(); UNREACHABLE();
return GL_COPY; return GL_COPY;
@ -187,7 +187,7 @@ inline GLenum CompareFunc(Pica::FramebufferRegs::CompareFunc func) {
// Range check table for input // Range check table for input
if (static_cast<size_t>(func) >= ARRAY_SIZE(compare_func_table)) { if (static_cast<size_t>(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<u32>(func));
UNREACHABLE(); UNREACHABLE();
return GL_ALWAYS; return GL_ALWAYS;
@ -210,7 +210,7 @@ inline GLenum StencilOp(Pica::FramebufferRegs::StencilAction action) {
// Range check table for input // Range check table for input
if (static_cast<size_t>(action) >= ARRAY_SIZE(stencil_op_table)) { if (static_cast<size_t>(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<u32>(action));
UNREACHABLE(); UNREACHABLE();
return GL_KEEP; return GL_KEEP;

View File

@ -853,7 +853,7 @@ void JitShader::Compile_NextInstr() {
} else { } else {
// Unhandled instruction // Unhandled instruction
LOG_CRITICAL(HW_GPU, "Unhandled instruction: 0x%02x (0x%08x)", LOG_CRITICAL(HW_GPU, "Unhandled instruction: 0x%02x (0x%08x)",
instr.opcode.Value().EffectiveOpCode(), instr.hex); static_cast<u32>(instr.opcode.Value().EffectiveOpCode()), instr.hex);
} }
} }

View File

@ -58,7 +58,7 @@ void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
default: default:
LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
framebuffer.color_format.Value()); static_cast<u32>(framebuffer.color_format.Value()));
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
} }
@ -94,7 +94,7 @@ const Math::Vec4<u8> GetPixel(int x, int y) {
default: default:
LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
framebuffer.color_format.Value()); static_cast<u32>(framebuffer.color_format.Value()));
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
@ -123,7 +123,8 @@ u32 GetDepth(int x, int y) {
case FramebufferRegs::DepthFormat::D24S8: case FramebufferRegs::DepthFormat::D24S8:
return Color::DecodeD24S8(src_pixel).x; return Color::DecodeD24S8(src_pixel).x;
default: default:
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u",
static_cast<u32>(framebuffer.depth_format.Value()));
UNIMPLEMENTED(); UNIMPLEMENTED();
return 0; return 0;
} }
@ -151,7 +152,7 @@ u8 GetStencil(int x, int y) {
LOG_WARNING( LOG_WARNING(
HW_GPU, HW_GPU,
"GetStencil called for function which doesn't have a stencil component (format %u)", "GetStencil called for function which doesn't have a stencil component (format %u)",
framebuffer.depth_format); static_cast<u32>(framebuffer.depth_format.Value()));
return 0; return 0;
} }
} }
@ -184,7 +185,8 @@ void SetDepth(int x, int y, u32 value) {
break; break;
default: default:
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u",
static_cast<u32>(framebuffer.depth_format.Value()));
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
} }
@ -215,7 +217,8 @@ void SetStencil(int x, int y, u8 value) {
break; break;
default: default:
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format); LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u",
static_cast<u32>(framebuffer.depth_format.Value()));
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
} }
@ -294,7 +297,7 @@ Math::Vec4<u8> EvaluateBlendEquation(const Math::Vec4<u8>& src, const Math::Vec4
break; break;
default: default:
LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation); LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation 0x%x", static_cast<u8>(equation));
UNIMPLEMENTED(); UNIMPLEMENTED();
} }

View File

@ -43,7 +43,8 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f); surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f);
surface_tangent = perturbation; surface_tangent = perturbation;
} else { } else {
LOG_ERROR(HW_GPU, "Unknown bump mode %u", lighting.config0.bump_mode.Value()); LOG_ERROR(HW_GPU, "Unknown bump mode %u",
static_cast<u32>(lighting.config0.bump_mode.Value()));
} }
} else { } else {
surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f); surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f);

View File

@ -801,7 +801,7 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
return std::min(combiner_output.a(), static_cast<u8>(255 - dest.a())); return std::min(combiner_output.a(), static_cast<u8>(255 - dest.a()));
default: default:
LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", factor); LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", static_cast<u32>(factor));
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
} }