mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-26 22:10:06 +00:00
commit
88c794eff4
@ -28,6 +28,12 @@ matrix:
|
||||
install: "./.travis/macos/deps.sh"
|
||||
script: "./.travis/macos/build.sh"
|
||||
after_success: "./.travis/macos/upload.sh"
|
||||
- os: linux
|
||||
env: NAME="linux build (frozen versions of dependencies)"
|
||||
sudo: required
|
||||
dist: trusty
|
||||
services: docker
|
||||
script: "./.travis/linux-frozen/build.sh"
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
|
4
.travis/linux-frozen/build.sh
Executable file
4
.travis/linux-frozen/build.sh
Executable 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
27
.travis/linux-frozen/docker.sh
Executable 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
|
38
.travis/linux-frozen/install_package.py
Executable file
38
.travis/linux-frozen/install_package.py
Executable 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
BIN
dist/icons/citra.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
1
dist/icons/icons.qrc
vendored
1
dist/icons/icons.qrc
vendored
@ -2,5 +2,6 @@
|
||||
<qresource prefix="icons">
|
||||
<file>checked.png</file>
|
||||
<file>failed.png</file>
|
||||
<file>citra.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
@ -1 +1 @@
|
||||
Subproject commit 8f15e3f70cb96e56705e5de6ba97b5d09423a56b
|
||||
Subproject commit d3fb603287dd4b0b06b921b639ef79dc9a850cdc
|
2
externals/xbyak
vendored
2
externals/xbyak
vendored
@ -1 +1 @@
|
||||
Subproject commit fe4765d2fed4e990ea5e9661b6bc5fc9bf48ec16
|
||||
Subproject commit c5da3778e7f84013fe8c26fcf18a67881bd1e825
|
@ -23,8 +23,10 @@ set(SRCS
|
||||
debugger/profiler.cpp
|
||||
debugger/registers.cpp
|
||||
debugger/wait_tree.cpp
|
||||
updater/updater.cpp
|
||||
util/spinbox.cpp
|
||||
util/util.cpp
|
||||
aboutdialog.cpp
|
||||
bootmanager.cpp
|
||||
game_list.cpp
|
||||
hotkeys.cpp
|
||||
@ -55,8 +57,11 @@ set(HEADERS
|
||||
debugger/profiler.h
|
||||
debugger/registers.h
|
||||
debugger/wait_tree.h
|
||||
updater/updater.h
|
||||
updater/updater_p.h
|
||||
util/spinbox.h
|
||||
util/util.h
|
||||
aboutdialog.h
|
||||
bootmanager.h
|
||||
game_list.h
|
||||
game_list_p.h
|
||||
@ -75,6 +80,7 @@ set(UIS
|
||||
configuration/configure_system.ui
|
||||
configuration/configure_web.ui
|
||||
debugger/registers.ui
|
||||
aboutdialog.ui
|
||||
hotkeys.ui
|
||||
main.ui
|
||||
)
|
||||
|
19
src/citra_qt/aboutdialog.cpp
Normal file
19
src/citra_qt/aboutdialog.cpp
Normal 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;
|
||||
}
|
25
src/citra_qt/aboutdialog.h
Normal file
25
src/citra_qt/aboutdialog.h
Normal 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
156
src/citra_qt/aboutdialog.ui
Normal 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><html><head/><body><p><img src=":/icons/citra.png"/></p></body></html></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><html><head/><body><p><span style=" font-size:28pt;">Citra</span></p></body></html></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><html><head/><body><p>%1 | %2-%3</p></body></html></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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">Citra is a free and open source 3DS emulator </span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">licensed under GPLv2.0 or any later version.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">This software should not be used to play games </span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12pt;">you have not legally obtained.</span></p></body></html></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><html><head/><body><p><a href="https://citra-emu.org/"><span style=" text-decoration: underline; color:#0000ff;">Website</span></a> | <a href="https://community.citra-emu.org/"><span style=" text-decoration: underline; color:#0000ff;">Forum</span></a> | <a href="https://github.com/citra-emu"><span style=" text-decoration: underline; color:#0000ff;">Source Code</span></a> | <a href="//github.com/citra-emu/citra/graphs/contributors"><span style=" text-decoration: underline; color:#0000ff;">Contributors</span></a> | <a href="https://github.com/citra-emu/citra/blob/master/license.txt"><span style=" text-decoration: underline; color:#0000ff;">License</span></a></p></body></html></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><html><head/><body><p><span style=" font-size:7pt;">&quot;3DS&quot; is a trademark of Nintendo. Citra is not affiliated with Nintendo in any way.</span></p></body></html></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>
|
@ -157,6 +157,12 @@ void Config::ReadValues() {
|
||||
qt_config->beginGroup("UI");
|
||||
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
|
||||
|
||||
qt_config->beginGroup("Updater");
|
||||
UISettings::values.check_for_update_on_start =
|
||||
qt_config->value("check_for_update_on_start", true).toBool();
|
||||
UISettings::values.update_on_close = qt_config->value("update_on_close", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("UILayout");
|
||||
UISettings::values.geometry = qt_config->value("geometry").toByteArray();
|
||||
UISettings::values.state = qt_config->value("state").toByteArray();
|
||||
@ -307,6 +313,11 @@ void Config::SaveValues() {
|
||||
qt_config->beginGroup("UI");
|
||||
qt_config->setValue("theme", UISettings::values.theme);
|
||||
|
||||
qt_config->beginGroup("Updater");
|
||||
qt_config->setValue("check_for_update_on_start", UISettings::values.check_for_update_on_start);
|
||||
qt_config->setValue("update_on_close", UISettings::values.update_on_close);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("UILayout");
|
||||
qt_config->setValue("geometry", UISettings::values.geometry);
|
||||
qt_config->setValue("state", UISettings::values.state);
|
||||
|
@ -20,6 +20,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
this->setConfiguration();
|
||||
|
||||
ui->toggle_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->updateBox->setVisible(UISettings::values.updater_found);
|
||||
}
|
||||
|
||||
ConfigureGeneral::~ConfigureGeneral() {}
|
||||
@ -29,6 +30,9 @@ void ConfigureGeneral::setConfiguration() {
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||
|
||||
ui->toggle_update_check->setChecked(UISettings::values.check_for_update_on_start);
|
||||
ui->toggle_auto_update->setChecked(UISettings::values.update_on_close);
|
||||
|
||||
// The first item is "auto-select" with actual value -1, so plus one here will do the trick
|
||||
ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1);
|
||||
|
||||
@ -40,6 +44,10 @@ void ConfigureGeneral::applyConfiguration() {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.theme =
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
|
||||
UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked();
|
||||
UISettings::values.update_on_close = ui->toggle_auto_update->isChecked();
|
||||
|
||||
Settings::values.region_value = ui->region_combobox->currentIndex() - 1;
|
||||
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
|
||||
Settings::Apply();
|
||||
|
@ -24,6 +24,13 @@
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_check_exit">
|
||||
<property name="text">
|
||||
<string>Confirm exit while emulation is running</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_deepscan">
|
||||
<property name="text">
|
||||
@ -31,10 +38,30 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_check_exit">
|
||||
<widget class="QGroupBox" name="updateBox">
|
||||
<property name="title">
|
||||
<string>Updates</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_update">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_update">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_update_check">
|
||||
<property name="text">
|
||||
<string>Confirm exit while emulation is running</string>
|
||||
<string>Check for updates on start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_auto_update">
|
||||
<property name="text">
|
||||
<string>Silently auto update after closing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -149,8 +176,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="theme_combobox">
|
||||
</widget>
|
||||
<widget class="QComboBox" name="theme_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
#include "citra_qt/aboutdialog.h"
|
||||
#include "citra_qt/bootmanager.h"
|
||||
#include "citra_qt/configuration/config.h"
|
||||
#include "citra_qt/configuration/configure_dialog.h"
|
||||
@ -29,6 +30,7 @@
|
||||
#include "citra_qt/hotkeys.h"
|
||||
#include "citra_qt/main.h"
|
||||
#include "citra_qt/ui_settings.h"
|
||||
#include "citra_qt/updater/updater.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
@ -99,6 +101,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
|
||||
InitializeDebugWidgets();
|
||||
InitializeRecentFileMenuActions();
|
||||
InitializeHotkeys();
|
||||
ShowUpdaterWidgets();
|
||||
|
||||
SetDefaultUIGeometry();
|
||||
RestoreUIState();
|
||||
@ -117,6 +120,10 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
|
||||
// Show one-time "callout" messages to the user
|
||||
ShowCallouts();
|
||||
|
||||
if (UISettings::values.check_for_update_on_start) {
|
||||
CheckForUpdates();
|
||||
}
|
||||
|
||||
QStringList args = QApplication::arguments();
|
||||
if (args.length() >= 2) {
|
||||
BootGame(args[1]);
|
||||
@ -138,6 +145,10 @@ void GMainWindow::InitializeWidgets() {
|
||||
game_list = new GameList(this);
|
||||
ui.horizontalLayout->addWidget(game_list);
|
||||
|
||||
// Setup updater
|
||||
updater = new Updater(this);
|
||||
UISettings::values.updater_found = updater->HasUpdater();
|
||||
|
||||
// Create status bar
|
||||
message_label = new QLabel();
|
||||
// Configured separately for left alignment
|
||||
@ -267,6 +278,13 @@ void GMainWindow::InitializeHotkeys() {
|
||||
});
|
||||
}
|
||||
|
||||
void GMainWindow::ShowUpdaterWidgets() {
|
||||
ui.action_Check_For_Updates->setVisible(UISettings::values.updater_found);
|
||||
ui.action_Open_Maintenance_Tool->setVisible(UISettings::values.updater_found);
|
||||
|
||||
connect(updater, &Updater::CheckUpdatesDone, this, &GMainWindow::OnUpdateFound);
|
||||
}
|
||||
|
||||
void GMainWindow::SetDefaultUIGeometry() {
|
||||
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
|
||||
const QRect screenRect = QApplication::desktop()->screenGeometry(this);
|
||||
@ -340,6 +358,15 @@ void GMainWindow::ConnectMenuEvents() {
|
||||
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
|
||||
ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key());
|
||||
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
||||
|
||||
// Help
|
||||
connect(ui.action_FAQ, &QAction::triggered,
|
||||
[]() { QDesktopServices::openUrl(QUrl("https://citra-emu.org/wiki/faq/")); });
|
||||
connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnMenuAboutCitra);
|
||||
connect(ui.action_Check_For_Updates, &QAction::triggered, this,
|
||||
&GMainWindow::OnCheckForUpdates);
|
||||
connect(ui.action_Open_Maintenance_Tool, &QAction::triggered, this,
|
||||
&GMainWindow::OnOpenUpdater);
|
||||
}
|
||||
|
||||
void GMainWindow::OnDisplayTitleBars(bool show) {
|
||||
@ -362,6 +389,73 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnCheckForUpdates() {
|
||||
explicit_update_check = true;
|
||||
CheckForUpdates();
|
||||
}
|
||||
|
||||
void GMainWindow::CheckForUpdates() {
|
||||
if (updater->CheckForUpdates()) {
|
||||
LOG_INFO(Frontend, "Update check started");
|
||||
} else {
|
||||
LOG_WARNING(Frontend, "Unable to start check for updates");
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnUpdateFound(bool found, bool error) {
|
||||
if (error) {
|
||||
LOG_WARNING(Frontend, "Update check failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOG_INFO(Frontend, "No updates found");
|
||||
|
||||
// If the user explicitly clicked the "Check for Updates" button, we are
|
||||
// going to want to show them a prompt anyway.
|
||||
if (explicit_update_check) {
|
||||
explicit_update_check = false;
|
||||
ShowNoUpdatePrompt();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (emulation_running && !explicit_update_check) {
|
||||
LOG_INFO(Frontend, "Update found, deferring as game is running");
|
||||
defer_update_prompt = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Frontend, "Update found!");
|
||||
explicit_update_check = false;
|
||||
|
||||
ShowUpdatePrompt();
|
||||
}
|
||||
|
||||
void GMainWindow::ShowUpdatePrompt() {
|
||||
defer_update_prompt = false;
|
||||
|
||||
auto result = QMessageBox::question(
|
||||
this, tr("Update available!"),
|
||||
tr("An update for Citra is available. Do you wish to install it now?<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) {
|
||||
// Shutdown previous session if the emu thread is still active...
|
||||
if (emu_thread != nullptr)
|
||||
@ -517,6 +611,10 @@ void GMainWindow::ShutdownGame() {
|
||||
emu_frametime_label->setVisible(false);
|
||||
|
||||
emulation_running = false;
|
||||
|
||||
if (defer_update_prompt) {
|
||||
ShowUpdatePrompt();
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::StoreRecentFile(const QString& filename) {
|
||||
@ -801,6 +899,11 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnMenuAboutCitra() {
|
||||
AboutDialog about{this};
|
||||
about.exec();
|
||||
}
|
||||
|
||||
bool GMainWindow::ConfirmClose() {
|
||||
if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
|
||||
return true;
|
||||
|
@ -2,8 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifndef _CITRA_QT_MAIN_HXX_
|
||||
#define _CITRA_QT_MAIN_HXX_
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QMainWindow>
|
||||
@ -24,7 +23,9 @@ class GRenderWindow;
|
||||
class MicroProfileDialog;
|
||||
class ProfilerWidget;
|
||||
class RegistersWidget;
|
||||
class Updater;
|
||||
class WaitTreeWidget;
|
||||
class AboutDialog;
|
||||
|
||||
class GMainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
@ -81,6 +82,10 @@ private:
|
||||
void ShutdownGame();
|
||||
|
||||
void ShowCallouts();
|
||||
void ShowUpdaterWidgets();
|
||||
void ShowUpdatePrompt();
|
||||
void ShowNoUpdatePrompt();
|
||||
void CheckForUpdates();
|
||||
|
||||
/**
|
||||
* Stores the filename in the recently loaded files list.
|
||||
@ -131,6 +136,11 @@ private slots:
|
||||
void ToggleWindowMode();
|
||||
void OnCreateGraphicsSurfaceViewer();
|
||||
void OnCoreError(Core::System::ResultStatus, std::string);
|
||||
/// Called whenever a user selects Help->About Citra
|
||||
void OnMenuAboutCitra();
|
||||
void OnUpdateFound(bool found, bool error);
|
||||
void OnCheckForUpdates();
|
||||
void OnOpenUpdater();
|
||||
|
||||
private:
|
||||
void UpdateStatusBar();
|
||||
@ -163,6 +173,10 @@ private:
|
||||
GraphicsVertexShaderWidget* graphicsVertexShaderWidget;
|
||||
GraphicsTracingWidget* graphicsTracingWidget;
|
||||
WaitTreeWidget* waitTreeWidget;
|
||||
Updater* updater;
|
||||
|
||||
bool explicit_update_check = false;
|
||||
bool defer_update_prompt = false;
|
||||
|
||||
QAction* actions_recent_files[max_recent_files_item];
|
||||
|
||||
@ -171,5 +185,3 @@ protected:
|
||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||
void dragMoveEvent(QDragMoveEvent* event) override;
|
||||
};
|
||||
|
||||
#endif // _CITRA_QT_MAIN_HXX_
|
||||
|
@ -96,6 +96,10 @@
|
||||
<property name="title">
|
||||
<string>&Help</string>
|
||||
</property>
|
||||
<addaction name="action_Check_For_Updates"/>
|
||||
<addaction name="action_Open_Maintenance_Tool"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_FAQ"/>
|
||||
<addaction name="action_About"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
@ -142,6 +146,11 @@
|
||||
<string>&Stop</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_FAQ">
|
||||
<property name="text">
|
||||
<string>FAQ</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_About">
|
||||
<property name="text">
|
||||
<string>About Citra</string>
|
||||
@ -205,6 +214,19 @@
|
||||
<string>Fullscreen</string>
|
||||
</property>
|
||||
</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>
|
||||
<resources/>
|
||||
</ui>
|
||||
|
@ -39,6 +39,10 @@ struct Values {
|
||||
bool confirm_before_closing;
|
||||
bool first_start;
|
||||
|
||||
bool updater_found;
|
||||
bool update_on_close;
|
||||
bool check_for_update_on_start;
|
||||
|
||||
QString roms_path;
|
||||
QString symbols_path;
|
||||
QString gamedir;
|
||||
|
304
src/citra_qt/updater/updater.cpp
Normal file
304
src/citra_qt/updater/updater.cpp
Normal 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;
|
||||
}
|
169
src/citra_qt/updater/updater.h
Normal file
169
src/citra_qt/updater/updater.h
Normal 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;
|
||||
};
|
66
src/citra_qt/updater/updater_p.h
Normal file
66
src/citra_qt/updater/updater_p.h
Normal 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;
|
||||
};
|
@ -96,14 +96,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
|
||||
|
||||
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result);
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error %u)!",
|
||||
static_cast<u32>(init_result));
|
||||
System::Shutdown();
|
||||
return init_result;
|
||||
}
|
||||
|
||||
const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
|
||||
if (Loader::ResultStatus::Success != load_result) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error %u)!", static_cast<u32>(load_result));
|
||||
System::Shutdown();
|
||||
|
||||
switch (load_result) {
|
||||
|
@ -302,7 +302,7 @@ static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
|
||||
auto bp = p.find(addr);
|
||||
if (bp != p.end()) {
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n",
|
||||
bp->second.len, bp->second.addr, type);
|
||||
bp->second.len, bp->second.addr, static_cast<int>(type));
|
||||
p.erase(addr);
|
||||
}
|
||||
}
|
||||
@ -348,8 +348,8 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) {
|
||||
|
||||
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
|
||||
LOG_DEBUG(Debug_GDBStub,
|
||||
"Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type,
|
||||
addr, bp->second.addr, bp->second.addr + len, len);
|
||||
"Found breakpoint type %d @ %08x, range: %08x - %08x (%u bytes)\n",
|
||||
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -738,8 +738,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
|
||||
breakpoint.len = len;
|
||||
p.insert({addr, breakpoint});
|
||||
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len,
|
||||
breakpoint.addr);
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n",
|
||||
static_cast<int>(type), breakpoint.len, breakpoint.addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ ResultCode Applet::Create(Service::APT::AppletId id) {
|
||||
applets[id] = std::make_shared<Mint>(id);
|
||||
break;
|
||||
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
|
||||
return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet,
|
||||
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) {
|
||||
Service::APT::AppletId id = static_cast<Service::APT::AppletId>(applet_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();
|
||||
|
||||
|
@ -74,7 +74,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_ERROR(Kernel, "unknown type=%d", type);
|
||||
LOG_ERROR(Kernel, "unknown type=%d", static_cast<u32>(type));
|
||||
return ERR_INVALID_ENUM_VALUE_FND;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ enum {
|
||||
OutOfHandles = 19,
|
||||
SessionClosedByRemote = 26,
|
||||
PortNameTooLong = 30,
|
||||
WrongLockingThread = 31,
|
||||
NoPendingSessions = 35,
|
||||
WrongPermission = 46,
|
||||
InvalidBufferDescriptor = 48,
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/mutex.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
@ -58,9 +59,25 @@ void Mutex::Acquire(Thread* thread) {
|
||||
lock_count++;
|
||||
}
|
||||
|
||||
void Mutex::Release() {
|
||||
// Only release if the mutex is held
|
||||
if (lock_count > 0) {
|
||||
ResultCode Mutex::Release(Thread* thread) {
|
||||
// We can only release the mutex if it's held by the calling thread.
|
||||
if (thread != holding_thread) {
|
||||
if (holding_thread) {
|
||||
LOG_ERROR(
|
||||
Kernel,
|
||||
"Tried to release a mutex (owned by thread id %u) from a different thread id %u",
|
||||
holding_thread->thread_id, thread->thread_id);
|
||||
}
|
||||
return ResultCode(ErrCodes::WrongLockingThread, ErrorModule::Kernel,
|
||||
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
// Note: It should not be possible for the situation where the mutex has a holding thread with a
|
||||
// zero lock count to occur. The real kernel still checks for this, so we do too.
|
||||
if (lock_count <= 0)
|
||||
return ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::Kernel,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Permanent);
|
||||
|
||||
lock_count--;
|
||||
|
||||
// Yield to the next thread only if we've fully released the mutex
|
||||
@ -71,7 +88,6 @@ void Mutex::Release() {
|
||||
WakeupAllWaitingThreads();
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::AddWaitingThread(SharedPtr<Thread> thread) {
|
||||
@ -102,4 +118,4 @@ void Mutex::UpdatePriority() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Kernel
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -52,7 +53,12 @@ public:
|
||||
void AddWaitingThread(SharedPtr<Thread> thread) override;
|
||||
void RemoveWaitingThread(Thread* thread) override;
|
||||
|
||||
void Release();
|
||||
/**
|
||||
* Attempts to release the mutex from the specified thread.
|
||||
* @param thread Thread that wants to release the mutex.
|
||||
* @returns The result code of the operation.
|
||||
*/
|
||||
ResultCode Release(Thread* thread);
|
||||
|
||||
private:
|
||||
Mutex();
|
||||
@ -65,4 +71,4 @@ private:
|
||||
*/
|
||||
void ReleaseThreadMutexes(Thread* thread);
|
||||
|
||||
} // namespace
|
||||
} // namespace Kernel
|
||||
|
@ -2,11 +2,10 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -17,169 +16,170 @@
|
||||
|
||||
namespace Service {
|
||||
namespace AC {
|
||||
void Module::Interface::CreateDefaultConfig(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x1, 0, 0);
|
||||
|
||||
struct ACConfig {
|
||||
std::array<u8, 0x200> data;
|
||||
};
|
||||
std::size_t desc_size;
|
||||
VAddr ac_config_addr = rp.PeekStaticBuffer(0, &desc_size);
|
||||
|
||||
static ACConfig default_config{};
|
||||
ASSERT_MSG(desc_size >= sizeof(Module::ACConfig),
|
||||
"Output buffer size can't fit ACConfig structure");
|
||||
|
||||
static bool ac_connected = false;
|
||||
Memory::WriteBlock(ac_config_addr, &ac->default_config, sizeof(ACConfig));
|
||||
|
||||
static Kernel::SharedPtr<Kernel::Event> close_event;
|
||||
static Kernel::SharedPtr<Kernel::Event> connect_event;
|
||||
static Kernel::SharedPtr<Kernel::Event> disconnect_event;
|
||||
|
||||
void CreateDefaultConfig(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
u32 ac_config_addr = cmd_buff[65];
|
||||
|
||||
ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2),
|
||||
"Output buffer size not equal ACConfig size");
|
||||
|
||||
Memory::WriteBlock(ac_config_addr, &default_config, sizeof(ACConfig));
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushStaticBuffer(ac_config_addr, sizeof(ACConfig), 0);
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ConnectAsync(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
void Module::Interface::ConnectAsync(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x4, 0, 6);
|
||||
|
||||
connect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
|
||||
if (connect_event) {
|
||||
connect_event->name = "AC:connect_event";
|
||||
connect_event->Signal();
|
||||
ac_connected = true;
|
||||
}
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
rp.Skip(2, false); // ProcessId descriptor
|
||||
ac->connect_event = rp.PopObject<Kernel::Event>();
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void GetConnectResult(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void CloseAsync(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
if (ac_connected && disconnect_event) {
|
||||
disconnect_event->Signal();
|
||||
if (ac->connect_event) {
|
||||
ac->connect_event->name = "AC:connect_event";
|
||||
ac->connect_event->Signal();
|
||||
ac->ac_connected = true;
|
||||
}
|
||||
|
||||
close_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
|
||||
if (close_event) {
|
||||
close_event->name = "AC:close_event";
|
||||
close_event->Signal();
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void Module::Interface::GetConnectResult(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x5, 0, 2);
|
||||
rp.Skip(2, false); // ProcessId descriptor
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::CloseAsync(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x8, 0, 4);
|
||||
rp.Skip(2, false); // ProcessId descriptor
|
||||
|
||||
ac->close_event = rp.PopObject<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
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
ac->ac_connected = false;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCloseResult(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
void Module::Interface::GetCloseResult(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x9, 0, 2);
|
||||
rp.Skip(2, false); // ProcessId descriptor
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void GetWifiStatus(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
void Module::Interface::GetWifiStatus(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0xD, 0, 0);
|
||||
|
||||
// TODO(purpasmart96): This function is only a stub,
|
||||
// it returns a valid result without implementing full functionality.
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = 0; // Connection type set to none
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0); // Connection type set to none
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void GetInfraPriority(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
void Module::Interface::GetInfraPriority(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x27, 0, 2);
|
||||
VAddr ac_config = rp.PopStaticBuffer();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = 0; // Infra Priority, default 0
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0); // Infra Priority, default 0
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetRequestEulaVersion(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
void Module::Interface::SetRequestEulaVersion(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x2D, 2, 2);
|
||||
|
||||
u32 major = cmd_buff[1] & 0xFF;
|
||||
u32 minor = cmd_buff[2] & 0xFF;
|
||||
u32 major = rp.Pop<u8>();
|
||||
u32 minor = rp.Pop<u8>();
|
||||
|
||||
ASSERT_MSG(cmd_buff[3] == (sizeof(ACConfig) << 14 | 2),
|
||||
"Input buffer size not equal ACConfig size");
|
||||
ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2),
|
||||
"Output buffer size not equal ACConfig size");
|
||||
VAddr ac_config = rp.PopStaticBuffer();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = 0; // Infra Priority
|
||||
// TODO(Subv): Copy over the input ACConfig to the stored ACConfig.
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushStaticBuffer(ac_config, sizeof(ACConfig), 0);
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor);
|
||||
}
|
||||
|
||||
void RegisterDisconnectEvent(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
void Module::Interface::RegisterDisconnectEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x30, 0, 4);
|
||||
rp.Skip(2, false); // ProcessId descriptor
|
||||
|
||||
disconnect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
|
||||
if (disconnect_event) {
|
||||
disconnect_event->name = "AC:disconnect_event";
|
||||
ac->disconnect_event = rp.PopObject<Kernel::Event>();
|
||||
if (ac->disconnect_event) {
|
||||
ac->disconnect_event->name = "AC:disconnect_event";
|
||||
}
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IsConnected(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
void Module::Interface::IsConnected(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x3E, 1, 2);
|
||||
u32 unk = rp.Pop<u32>();
|
||||
u32 unk_descriptor = rp.Pop<u32>();
|
||||
u32 unk_param = rp.Pop<u32>();
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = ac_connected;
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(ac->ac_connected);
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called unk=%08X descriptor=%08X param=%08X", unk,
|
||||
unk_descriptor, unk_param);
|
||||
}
|
||||
|
||||
void SetClientVersion(Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
void Module::Interface::SetClientVersion(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x40, 1, 2);
|
||||
|
||||
const u32 version = cmd_buff[1];
|
||||
self->SetVersion(version);
|
||||
u32 version = rp.Pop<u32>();
|
||||
rp.Skip(2, false); // ProcessId descriptor
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called, version: 0x%08X", version);
|
||||
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Init() {
|
||||
AddService(new AC_I);
|
||||
AddService(new AC_U);
|
||||
Module::Interface::Interface(std::shared_ptr<Module> ac, const char* name, u32 max_session)
|
||||
: ac(std::move(ac)), ServiceFramework(name, max_session) {}
|
||||
|
||||
ac_connected = false;
|
||||
|
||||
close_event = nullptr;
|
||||
connect_event = nullptr;
|
||||
disconnect_event = nullptr;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
ac_connected = false;
|
||||
|
||||
close_event = nullptr;
|
||||
connect_event = nullptr;
|
||||
disconnect_event = nullptr;
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
auto ac = std::make_shared<Module>();
|
||||
std::make_shared<AC_I>(ac)->InstallAsService(service_manager);
|
||||
std::make_shared<AC_U>(ac)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace AC
|
||||
|
@ -4,13 +4,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
|
||||
class Interface;
|
||||
|
||||
namespace AC {
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
Interface(std::shared_ptr<Module> ac, const char* name, u32 max_session);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::CreateDefaultConfig service function
|
||||
* Inputs:
|
||||
* 64 : ACConfig size << 14 | 2
|
||||
@ -18,9 +29,9 @@ namespace AC {
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void CreateDefaultConfig(Interface* self);
|
||||
void CreateDefaultConfig(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::ConnectAsync service function
|
||||
* Inputs:
|
||||
* 1 : ProcessId Header
|
||||
@ -31,18 +42,18 @@ void CreateDefaultConfig(Interface* self);
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void ConnectAsync(Interface* self);
|
||||
void ConnectAsync(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::GetConnectResult service function
|
||||
* Inputs:
|
||||
* 1 : ProcessId Header
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetConnectResult(Interface* self);
|
||||
void GetConnectResult(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::CloseAsync service function
|
||||
* Inputs:
|
||||
* 1 : ProcessId Header
|
||||
@ -51,26 +62,26 @@ void GetConnectResult(Interface* self);
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void CloseAsync(Interface* self);
|
||||
void CloseAsync(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::GetCloseResult service function
|
||||
* Inputs:
|
||||
* 1 : ProcessId Header
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void GetCloseResult(Interface* self);
|
||||
void GetCloseResult(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::GetWifiStatus service function
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
|
||||
*/
|
||||
void GetWifiStatus(Interface* self);
|
||||
void GetWifiStatus(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::GetInfraPriority service function
|
||||
* Inputs:
|
||||
* 1 : ACConfig size << 14 | 2
|
||||
@ -79,9 +90,9 @@ void GetWifiStatus(Interface* self);
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Infra Priority
|
||||
*/
|
||||
void GetInfraPriority(Interface* self);
|
||||
void GetInfraPriority(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::SetRequestEulaVersion service function
|
||||
* Inputs:
|
||||
* 1 : Eula Version major
|
||||
@ -94,9 +105,9 @@ void GetInfraPriority(Interface* self);
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Infra Priority
|
||||
*/
|
||||
void SetRequestEulaVersion(Interface* self);
|
||||
void SetRequestEulaVersion(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::RegisterDisconnectEvent service function
|
||||
* Inputs:
|
||||
* 1 : ProcessId Header
|
||||
@ -105,30 +116,44 @@ void SetRequestEulaVersion(Interface* self);
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void RegisterDisconnectEvent(Interface* self);
|
||||
void RegisterDisconnectEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::IsConnected service function
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : bool, is connected
|
||||
*/
|
||||
void IsConnected(Interface* self);
|
||||
void IsConnected(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
/**
|
||||
* AC::SetClientVersion service function
|
||||
* Inputs:
|
||||
* 1 : Used SDK Version
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void SetClientVersion(Interface* self);
|
||||
void SetClientVersion(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/// Initialize AC service
|
||||
void Init();
|
||||
protected:
|
||||
std::shared_ptr<Module> ac;
|
||||
};
|
||||
|
||||
/// Shutdown AC service
|
||||
void Shutdown();
|
||||
protected:
|
||||
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 Service
|
||||
|
@ -2,37 +2,36 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/ac/ac.h"
|
||||
#include "core/hle/service/ac/ac_i.h"
|
||||
|
||||
namespace Service {
|
||||
namespace AC {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010000, CreateDefaultConfig, "CreateDefaultConfig"},
|
||||
{0x00040006, ConnectAsync, "ConnectAsync"},
|
||||
{0x00050002, GetConnectResult, "GetConnectResult"},
|
||||
// TODO(Subv): Find out the correct number of concurrent sessions allowed
|
||||
AC_I::AC_I(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:i", 1) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00010000, &AC_I::CreateDefaultConfig, "CreateDefaultConfig"},
|
||||
{0x00040006, &AC_I::ConnectAsync, "ConnectAsync"},
|
||||
{0x00050002, &AC_I::GetConnectResult, "GetConnectResult"},
|
||||
{0x00070002, nullptr, "CancelConnectAsync"},
|
||||
{0x00080004, CloseAsync, "CloseAsync"},
|
||||
{0x00090002, GetCloseResult, "GetCloseResult"},
|
||||
{0x00080004, &AC_I::CloseAsync, "CloseAsync"},
|
||||
{0x00090002, &AC_I::GetCloseResult, "GetCloseResult"},
|
||||
{0x000A0000, nullptr, "GetLastErrorCode"},
|
||||
{0x000C0000, nullptr, "GetStatus"},
|
||||
{0x000D0000, GetWifiStatus, "GetWifiStatus"},
|
||||
{0x000D0000, &AC_I::GetWifiStatus, "GetWifiStatus"},
|
||||
{0x000E0042, nullptr, "GetCurrentAPInfo"},
|
||||
{0x00100042, nullptr, "GetCurrentNZoneInfo"},
|
||||
{0x00110042, nullptr, "GetNZoneApNumService"},
|
||||
{0x001D0042, nullptr, "ScanAPs"},
|
||||
{0x00240042, nullptr, "AddDenyApType"},
|
||||
{0x00270002, GetInfraPriority, "GetInfraPriority"},
|
||||
{0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"},
|
||||
{0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"},
|
||||
{0x00270002, &AC_I::GetInfraPriority, "GetInfraPriority"},
|
||||
{0x002D0082, &AC_I::SetRequestEulaVersion, "SetRequestEulaVersion"},
|
||||
{0x00300004, &AC_I::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
|
||||
{0x003C0042, nullptr, "GetAPSSIDList"},
|
||||
{0x003E0042, IsConnected, "IsConnected"},
|
||||
{0x00400042, SetClientVersion, "SetClientVersion"},
|
||||
};
|
||||
|
||||
AC_I::AC_I() {
|
||||
Register(FunctionTable);
|
||||
{0x003E0042, &AC_I::IsConnected, "IsConnected"},
|
||||
{0x00400042, &AC_I::SetClientVersion, "SetClientVersion"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
} // namespace AC
|
||||
|
@ -4,18 +4,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
#include <memory>
|
||||
#include "core/hle/service/ac/ac.h"
|
||||
|
||||
namespace Service {
|
||||
namespace AC {
|
||||
|
||||
class AC_I final : public Interface {
|
||||
class AC_I final : public Module::Interface {
|
||||
public:
|
||||
AC_I();
|
||||
|
||||
std::string GetPortName() const override {
|
||||
return "ac:i";
|
||||
}
|
||||
explicit AC_I(std::shared_ptr<Module> ac);
|
||||
};
|
||||
|
||||
} // namespace AC
|
||||
|
@ -2,37 +2,36 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/ac/ac.h"
|
||||
#include "core/hle/service/ac/ac_u.h"
|
||||
|
||||
namespace Service {
|
||||
namespace AC {
|
||||
|
||||
const Interface::FunctionInfo FunctionTable[] = {
|
||||
{0x00010000, CreateDefaultConfig, "CreateDefaultConfig"},
|
||||
{0x00040006, ConnectAsync, "ConnectAsync"},
|
||||
{0x00050002, GetConnectResult, "GetConnectResult"},
|
||||
// TODO(Subv): Find out the correct number of concurrent sessions allowed
|
||||
AC_U::AC_U(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:u", 1) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00010000, &AC_U::CreateDefaultConfig, "CreateDefaultConfig"},
|
||||
{0x00040006, &AC_U::ConnectAsync, "ConnectAsync"},
|
||||
{0x00050002, &AC_U::GetConnectResult, "GetConnectResult"},
|
||||
{0x00070002, nullptr, "CancelConnectAsync"},
|
||||
{0x00080004, CloseAsync, "CloseAsync"},
|
||||
{0x00090002, GetCloseResult, "GetCloseResult"},
|
||||
{0x00080004, &AC_U::CloseAsync, "CloseAsync"},
|
||||
{0x00090002, &AC_U::GetCloseResult, "GetCloseResult"},
|
||||
{0x000A0000, nullptr, "GetLastErrorCode"},
|
||||
{0x000C0000, nullptr, "GetStatus"},
|
||||
{0x000D0000, GetWifiStatus, "GetWifiStatus"},
|
||||
{0x000D0000, &AC_U::GetWifiStatus, "GetWifiStatus"},
|
||||
{0x000E0042, nullptr, "GetCurrentAPInfo"},
|
||||
{0x00100042, nullptr, "GetCurrentNZoneInfo"},
|
||||
{0x00110042, nullptr, "GetNZoneApNumService"},
|
||||
{0x001D0042, nullptr, "ScanAPs"},
|
||||
{0x00240042, nullptr, "AddDenyApType"},
|
||||
{0x00270002, GetInfraPriority, "GetInfraPriority"},
|
||||
{0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"},
|
||||
{0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"},
|
||||
{0x00270002, &AC_U::GetInfraPriority, "GetInfraPriority"},
|
||||
{0x002D0082, &AC_U::SetRequestEulaVersion, "SetRequestEulaVersion"},
|
||||
{0x00300004, &AC_U::RegisterDisconnectEvent, "RegisterDisconnectEvent"},
|
||||
{0x003C0042, nullptr, "GetAPSSIDList"},
|
||||
{0x003E0042, IsConnected, "IsConnected"},
|
||||
{0x00400042, SetClientVersion, "SetClientVersion"},
|
||||
};
|
||||
|
||||
AC_U::AC_U() {
|
||||
Register(FunctionTable);
|
||||
{0x003E0042, &AC_U::IsConnected, "IsConnected"},
|
||||
{0x00400042, &AC_U::SetClientVersion, "SetClientVersion"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
} // namespace AC
|
||||
|
@ -4,18 +4,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
#include <memory>
|
||||
#include "core/hle/service/ac/ac.h"
|
||||
|
||||
namespace Service {
|
||||
namespace AC {
|
||||
|
||||
class AC_U final : public Interface {
|
||||
class AC_U final : public Module::Interface {
|
||||
public:
|
||||
AC_U();
|
||||
|
||||
std::string GetPortName() const override {
|
||||
return "ac:u";
|
||||
}
|
||||
explicit AC_U(std::shared_ptr<Module> ac);
|
||||
};
|
||||
|
||||
} // namespace AC
|
||||
|
@ -554,7 +554,7 @@ void ReceiveParameter(Service::Interface* self) {
|
||||
"buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
|
||||
buffer_size, static_buff_size);
|
||||
|
||||
LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
|
||||
LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
|
||||
|
||||
if (!next_parameter) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
@ -603,7 +603,7 @@ void GlanceParameter(Service::Interface* self) {
|
||||
"buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
|
||||
buffer_size, static_buff_size);
|
||||
|
||||
LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size);
|
||||
LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
|
||||
|
||||
if (!next_parameter) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
@ -767,7 +767,7 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
|
||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040
|
||||
AppletId applet_id = static_cast<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);
|
||||
|
||||
@ -775,7 +775,8 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
|
||||
|
||||
auto applet = HLE::Applets::Applet::Get(applet_id);
|
||||
if (applet) {
|
||||
LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
|
||||
LOG_WARNING(Service_APT, "applet has already been started id=%08X",
|
||||
static_cast<u32>(applet_id));
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
} else {
|
||||
rb.Push(HLE::Applets::Applet::Create(applet_id));
|
||||
@ -800,7 +801,7 @@ void PreloadLibraryApplet(Service::Interface* self) {
|
||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040
|
||||
AppletId applet_id = static_cast<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);
|
||||
|
||||
@ -808,7 +809,8 @@ void PreloadLibraryApplet(Service::Interface* self) {
|
||||
|
||||
auto applet = HLE::Applets::Applet::Get(applet_id);
|
||||
if (applet) {
|
||||
LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
|
||||
LOG_WARNING(Service_APT, "applet has already been started id=%08X",
|
||||
static_cast<u32>(applet_id));
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
} else {
|
||||
rb.Push(HLE::Applets::Applet::Create(applet_id));
|
||||
@ -823,7 +825,7 @@ void StartLibraryApplet(Service::Interface* self) {
|
||||
Kernel::Handle handle = rp.PopHandle();
|
||||
VAddr buffer_addr = rp.PopStaticBuffer();
|
||||
|
||||
LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
|
||||
LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast<u32>(applet_id));
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
||||
@ -867,7 +869,7 @@ void SetScreenCapPostPermission(Service::Interface* self) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS); // No error
|
||||
LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u",
|
||||
screen_capture_post_permission);
|
||||
static_cast<u32>(screen_capture_post_permission));
|
||||
}
|
||||
|
||||
void GetScreenCapPostPermission(Service::Interface* self) {
|
||||
@ -877,7 +879,7 @@ void GetScreenCapPostPermission(Service::Interface* self) {
|
||||
rb.Push(RESULT_SUCCESS); // No error
|
||||
rb.Push(static_cast<u32>(screen_capture_post_permission));
|
||||
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) {
|
||||
@ -899,7 +901,7 @@ void GetAppletInfo(Service::Interface* self) {
|
||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
|
||||
ErrorLevel::Status));
|
||||
}
|
||||
LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id);
|
||||
LOG_WARNING(Service_APT, "(stubbed) called appid=%u", static_cast<u32>(app_id));
|
||||
}
|
||||
|
||||
void GetStartupArgument(Service::Interface* self) {
|
||||
@ -928,7 +930,7 @@ void GetStartupArgument(Service::Interface* self) {
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x",
|
||||
startup_argument_type, parameter_size);
|
||||
static_cast<u32>(startup_argument_type), parameter_size);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
@ -301,7 +301,7 @@ static void WriteProcessPipe(Service::Interface* self) {
|
||||
}
|
||||
|
||||
ASSERT_MSG(Memory::IsValidVirtualAddress(buffer),
|
||||
"Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
|
||||
"Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer);
|
||||
|
||||
std::vector<u8> message(size);
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
@ -359,8 +359,8 @@ static void ReadPipeIfPossible(Service::Interface* self) {
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
|
||||
|
||||
ASSERT_MSG(Memory::IsValidVirtualAddress(addr),
|
||||
"Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown,
|
||||
size, addr);
|
||||
"Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index,
|
||||
unknown, size, addr);
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
@ -403,8 +403,8 @@ static void ReadPipe(Service::Interface* self) {
|
||||
DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index);
|
||||
|
||||
ASSERT_MSG(Memory::IsValidVirtualAddress(addr),
|
||||
"Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown,
|
||||
size, addr);
|
||||
"Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index,
|
||||
unknown, size, addr);
|
||||
|
||||
if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
|
||||
std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
@ -105,8 +106,8 @@ void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_ses
|
||||
offset, length, address);
|
||||
|
||||
if (offset + length > backend->GetSize()) {
|
||||
LOG_ERROR(Service_FS,
|
||||
"Reading from out of bounds offset=0x%llX length=0x%08X file_size=0x%llX",
|
||||
LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%" PRIx64
|
||||
" length=0x%08X file_size=0x%" PRIx64,
|
||||
offset, length, backend->GetSize());
|
||||
}
|
||||
|
||||
@ -191,7 +192,7 @@ void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_ses
|
||||
|
||||
// Unknown command...
|
||||
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);
|
||||
cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
|
||||
return;
|
||||
@ -231,7 +232,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> serve
|
||||
|
||||
// Unknown command...
|
||||
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);
|
||||
cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
|
||||
return;
|
||||
@ -297,7 +298,7 @@ ResultCode RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factor
|
||||
|
||||
auto& archive = result.first->second;
|
||||
LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(),
|
||||
id_code);
|
||||
static_cast<u32>(id_code));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -472,7 +473,7 @@ ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) {
|
||||
} else if (media_type == MediaType::SDMC) {
|
||||
media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Unsupported media type %u", media_type);
|
||||
LOG_ERROR(Service_FS, "Unsupported media type %u", static_cast<u32>(media_type));
|
||||
return ResultCode(-1); // TODO(Subv): Find the right error code
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
@ -311,8 +312,8 @@ static void CreateFile(Service::Interface* self) {
|
||||
|
||||
FileSys::Path file_path(filename_type, filename_size, filename_ptr);
|
||||
|
||||
LOG_DEBUG(Service_FS, "type=%u size=%llu data=%s", static_cast<u32>(filename_type), file_size,
|
||||
file_path.DebugStr().c_str());
|
||||
LOG_DEBUG(Service_FS, "type=%u size=%" PRIx64 " data=%s", static_cast<u32>(filename_type),
|
||||
file_size, file_path.DebugStr().c_str());
|
||||
|
||||
cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ void EnterExclusiveState(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast<u32>(exclusive_state));
|
||||
}
|
||||
|
||||
void LeaveExclusiveState(Service::Interface* self) {
|
||||
@ -43,7 +43,7 @@ void LeaveExclusiveState(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X ", exclusive_state);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) exclusive_state=0x%08X", static_cast<u32>(exclusive_state));
|
||||
}
|
||||
|
||||
void QueryExclusiveMode(Service::Interface* self) {
|
||||
@ -52,7 +52,7 @@ void QueryExclusiveMode(Service::Interface* self) {
|
||||
cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = static_cast<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) {
|
||||
@ -61,7 +61,7 @@ void LockState(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void UnlockState(Service::Interface* self) {
|
||||
@ -70,7 +70,7 @@ void UnlockState(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) daemon_lock_enabled=0x%08X ", daemon_lock_enabled);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SuspendDaemons(Service::Interface* self) {
|
||||
@ -86,7 +86,7 @@ void SuspendDaemons(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x6, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X", static_cast<u32>(daemon_bit_mask));
|
||||
}
|
||||
|
||||
void ResumeDaemons(Service::Interface* self) {
|
||||
@ -101,7 +101,7 @@ void ResumeDaemons(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X ", daemon_bit_mask);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) daemon_bit_mask=0x%08X", static_cast<u32>(daemon_bit_mask));
|
||||
}
|
||||
|
||||
void SuspendScheduler(Service::Interface* self) {
|
||||
@ -157,7 +157,7 @@ void SetScanInterval(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X", scan_interval);
|
||||
}
|
||||
|
||||
void GetScanInterval(Service::Interface* self) {
|
||||
@ -166,7 +166,7 @@ void GetScanInterval(Service::Interface* self) {
|
||||
cmd_buff[0] = IPC::MakeHeader(0x11, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = scan_interval;
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X ", scan_interval);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) scan_interval=0x%08X", scan_interval);
|
||||
}
|
||||
|
||||
void SetRetryInterval(Service::Interface* self) {
|
||||
@ -175,7 +175,7 @@ void SetRetryInterval(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X", retry_interval);
|
||||
}
|
||||
|
||||
void GetRetryInterval(Service::Interface* self) {
|
||||
@ -184,7 +184,7 @@ void GetRetryInterval(Service::Interface* self) {
|
||||
cmd_buff[0] = IPC::MakeHeader(0x13, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = retry_interval;
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X ", retry_interval);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) retry_interval=0x%08X", retry_interval);
|
||||
}
|
||||
|
||||
void OverrideDefaultDaemons(Service::Interface* self) {
|
||||
@ -200,7 +200,8 @@ void OverrideDefaultDaemons(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ",
|
||||
static_cast<u32>(default_daemon_bit_mask));
|
||||
}
|
||||
|
||||
void ResetDefaultDaemons(Service::Interface* self) {
|
||||
@ -209,7 +210,8 @@ void ResetDefaultDaemons(Service::Interface* self) {
|
||||
|
||||
cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X ", default_daemon_bit_mask);
|
||||
LOG_WARNING(Service_NDM, "(STUBBED) default_daemon_bit_mask=0x%08X",
|
||||
static_cast<u32>(default_daemon_bit_mask));
|
||||
}
|
||||
|
||||
void GetDefaultDaemons(Service::Interface* self) {
|
||||
@ -218,7 +220,8 @@ void GetDefaultDaemons(Service::Interface* self) {
|
||||
cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
|
||||
cmd_buff[2] = static_cast<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) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
@ -27,6 +28,12 @@
|
||||
namespace Service {
|
||||
namespace NWM {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
NotInitialized = 2,
|
||||
};
|
||||
} // namespace ErrCodes
|
||||
|
||||
// Event that is signaled every time the connection status changes.
|
||||
static Kernel::SharedPtr<Kernel::Event> connection_status_event;
|
||||
|
||||
@ -37,6 +44,8 @@ static Kernel::SharedPtr<Kernel::SharedMemory> recv_buffer_memory;
|
||||
// Connection status of this 3DS.
|
||||
static ConnectionStatus connection_status{};
|
||||
|
||||
static std::atomic<bool> initialized(false);
|
||||
|
||||
/* Node information about the current network.
|
||||
* The amount of elements in this vector is always the maximum number
|
||||
* of nodes specified in the network configuration.
|
||||
@ -47,8 +56,17 @@ static NodeList node_info;
|
||||
// Node information about our own system.
|
||||
static NodeInfo current_node;
|
||||
|
||||
// Mapping of bind node ids to their respective events.
|
||||
static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events;
|
||||
struct BindNodeData {
|
||||
u32 bind_node_id; ///< Id of the bind node associated with this data.
|
||||
u8 channel; ///< Channel that this bind node was bound to.
|
||||
u16 network_node_id; ///< Node id this bind node is associated with, only packets from this
|
||||
/// network node will be received.
|
||||
Kernel::SharedPtr<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.
|
||||
// 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.
|
||||
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.
|
||||
*/
|
||||
@ -159,7 +180,9 @@ void HandleAssociationResponseFrame(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 (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
|
||||
@ -205,10 +228,9 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
|
||||
SendPacket(eapol_logoff);
|
||||
// TODO(B3N30): Broadcast updated node list
|
||||
// The 3ds does this presumably to support spectators.
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
connection_status_event->Signal();
|
||||
} 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",
|
||||
connection_status.status);
|
||||
return;
|
||||
@ -237,11 +259,63 @@ static void HandleEAPoLPacket(const Network::WifiPacket& packet) {
|
||||
// Some games require ConnectToNetwork to block, for now it doesn't
|
||||
// If blocking is implemented this lock needs to be changed,
|
||||
// otherwise it might cause deadlocks
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
connection_status_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleSecureDataPacket(const Network::WifiPacket& packet) {
|
||||
auto secure_data = ParseSecureDataHeader(packet.data);
|
||||
std::unique_lock<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
|
||||
* authentication frame with SEQ1.
|
||||
@ -251,7 +325,7 @@ void StartConnectionSequence(const MacAddress& server) {
|
||||
WifiPacket auth_request;
|
||||
{
|
||||
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.
|
||||
|
||||
@ -329,7 +403,7 @@ static void HandleDataFrame(const Network::WifiPacket& packet) {
|
||||
HandleEAPoLPacket(packet);
|
||||
break;
|
||||
case EtherType::SecureData:
|
||||
// TODO(B3N30): Handle SecureData packets
|
||||
HandleSecureDataPacket(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -482,6 +556,8 @@ static void InitializeWithVersion(Interface* self) {
|
||||
|
||||
recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle);
|
||||
|
||||
initialized = true;
|
||||
|
||||
ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size.");
|
||||
|
||||
{
|
||||
@ -535,6 +611,48 @@ static void GetConnectionStatus(Interface* self) {
|
||||
LOG_DEBUG(Service_NWM, "called");
|
||||
}
|
||||
|
||||
/**
|
||||
* NWM_UDS::GetNodeInformation service function.
|
||||
* Returns the node inforamtion structure for the currently connected node.
|
||||
* Inputs:
|
||||
* 0 : Command header.
|
||||
* 1 : Node ID.
|
||||
* Outputs:
|
||||
* 0 : Return header
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2-11 : NodeInfo structure.
|
||||
*/
|
||||
static void GetNodeInformation(Interface* self) {
|
||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 1, 0);
|
||||
u16 network_node_id = rp.Pop<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.
|
||||
* 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>();
|
||||
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");
|
||||
|
||||
if (data_channel == 0) {
|
||||
if (data_channel == 0 || bind_node_id == 0) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
|
||||
ErrorSummary::WrongArgument, ErrorLevel::Usage));
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr size_t MaxBindNodes = 16;
|
||||
if (channel_data.size() >= MaxBindNodes) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultCode(ErrorDescription::OutOfMemory, ErrorModule::UDS,
|
||||
ErrorSummary::OutOfResource, ErrorLevel::Status));
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr u32 MinRecvBufferSize = 0x5F4;
|
||||
if (recv_buffer_size < MinRecvBufferSize) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS,
|
||||
ErrorSummary::WrongArgument, ErrorLevel::Usage));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new event for this bind node.
|
||||
// TODO(Subv): Signal this event when new data is received on this data channel.
|
||||
auto event = Kernel::Event::Create(Kernel::ResetType::OneShot,
|
||||
"NWM::BindNodeEvent" + std::to_string(bind_node_id));
|
||||
bind_node_events[bind_node_id] = event;
|
||||
std::lock_guard<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);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap());
|
||||
}
|
||||
|
||||
/**
|
||||
* NWM_UDS::Unbind service function.
|
||||
* Unbinds a BindNodeId from a data channel.
|
||||
* Inputs:
|
||||
* 1 : BindNodeId
|
||||
* Outputs:
|
||||
* 0 : Return header
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
static void Unbind(Interface* self) {
|
||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 1, 0);
|
||||
|
||||
u32 bind_node_id = rp.Pop<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.
|
||||
* Creates a network and starts broadcasting its presence.
|
||||
@ -606,13 +780,14 @@ static void BeginHostingNetwork(Interface* self) {
|
||||
|
||||
LOG_DEBUG(Service_NWM, "called");
|
||||
|
||||
{
|
||||
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.");
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connection_status_mutex);
|
||||
connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
|
||||
|
||||
// Ensure the application data size is less than the maximum value.
|
||||
@ -626,11 +801,13 @@ static void BeginHostingNetwork(Interface* self) {
|
||||
connection_status.max_nodes = network_info.max_nodes;
|
||||
|
||||
// Resize the nodes list to hold max_nodes.
|
||||
node_info.clear();
|
||||
node_info.resize(network_info.max_nodes);
|
||||
|
||||
// There's currently only one node in the network (the host).
|
||||
connection_status.total_nodes = 1;
|
||||
network_info.total_nodes = 1;
|
||||
|
||||
// The host is always the first node
|
||||
connection_status.network_node_id = 1;
|
||||
current_node.network_node_id = 1;
|
||||
@ -639,12 +816,22 @@ static void BeginHostingNetwork(Interface* self) {
|
||||
connection_status.node_bitmask |= 1;
|
||||
// Notify the application that the first node was set.
|
||||
connection_status.changed_nodes |= 1;
|
||||
node_info[0] = current_node;
|
||||
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
if (room_member->IsConnected()) {
|
||||
network_info.host_mac_address = room_member->GetMacAddress();
|
||||
} else {
|
||||
network_info.host_mac_address = {{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
}
|
||||
}
|
||||
node_info[0] = current_node;
|
||||
|
||||
// If the game has a preferred channel, use that instead.
|
||||
if (network_info.channel != 0)
|
||||
network_channel = network_info.channel;
|
||||
else
|
||||
network_info.channel = DefaultNetworkChannel;
|
||||
}
|
||||
|
||||
connection_status_event->Signal();
|
||||
|
||||
@ -652,8 +839,7 @@ static void BeginHostingNetwork(Interface* self) {
|
||||
CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU),
|
||||
beacon_broadcast_event, 0);
|
||||
|
||||
LOG_WARNING(Service_NWM,
|
||||
"An UDS network has been created, but broadcasting it is unimplemented.");
|
||||
LOG_DEBUG(Service_NWM, "An UDS network has been created.");
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@ -722,13 +908,10 @@ static void SendTo(Interface* self) {
|
||||
|
||||
size_t desc_size;
|
||||
const VAddr input_address = rp.PopStaticBuffer(&desc_size, false);
|
||||
ASSERT(desc_size == data_size);
|
||||
ASSERT(desc_size >= data_size);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
||||
u16 network_node_id;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connection_status_mutex);
|
||||
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
|
||||
connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
|
||||
@ -743,10 +926,7 @@ static void SendTo(Interface* self) {
|
||||
return;
|
||||
}
|
||||
|
||||
network_node_id = connection_status.network_node_id;
|
||||
}
|
||||
|
||||
// TODO(Subv): Do something with the flags.
|
||||
// TODO(B3N30): Do something with the flags.
|
||||
|
||||
constexpr size_t MaxSize = 0x5C6;
|
||||
if (data_size > MaxSize) {
|
||||
@ -758,20 +938,116 @@ static void SendTo(Interface* self) {
|
||||
std::vector<u8> data(data_size);
|
||||
Memory::ReadBlock(input_address, data.data(), data.size());
|
||||
|
||||
// TODO(Subv): Increment the sequence number after each sent packet.
|
||||
// TODO(B3N30): Increment the sequence number after each sent packet.
|
||||
u16 sequence_number = 0;
|
||||
std::vector<u8> data_payload =
|
||||
GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number);
|
||||
std::vector<u8> data_payload = GenerateDataPayload(
|
||||
data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number);
|
||||
|
||||
// TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
|
||||
// TODO(B3N30): Retrieve the MAC address of the dest_node_id and our own to encrypt
|
||||
// and encapsulate the payload.
|
||||
|
||||
// TODO(Subv): Send the frame.
|
||||
Network::WifiPacket packet;
|
||||
// Data frames are sent to the host, who then decides where to route it to. If we're the host,
|
||||
// just directly broadcast the frame.
|
||||
if (connection_status.status == static_cast<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);
|
||||
}
|
||||
|
||||
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)"},
|
||||
{0x000A0000, nullptr, "DisconnectNetwork"},
|
||||
{0x000B0000, GetConnectionStatus, "GetConnectionStatus"},
|
||||
{0x000D0040, nullptr, "GetNodeInformation"},
|
||||
{0x000D0040, GetNodeInformation, "GetNodeInformation"},
|
||||
{0x000E0006, nullptr, "DecryptBeaconData (deprecated)"},
|
||||
{0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
|
||||
{0x00100042, SetApplicationData, "SetApplicationData"},
|
||||
{0x00110040, nullptr, "GetApplicationData"},
|
||||
{0x00120100, Bind, "Bind"},
|
||||
{0x00130040, nullptr, "Unbind"},
|
||||
{0x001400C0, nullptr, "PullPacket"},
|
||||
{0x00130040, Unbind, "Unbind"},
|
||||
{0x001400C0, PullPacket, "PullPacket"},
|
||||
{0x00150080, nullptr, "SetMaxSendDelay"},
|
||||
{0x00170182, SendTo, "SendTo"},
|
||||
{0x001A0000, GetChannel, "GetChannel"},
|
||||
@ -1018,9 +1294,10 @@ NWM_UDS::NWM_UDS() {
|
||||
|
||||
NWM_UDS::~NWM_UDS() {
|
||||
network_info = {};
|
||||
bind_node_events.clear();
|
||||
channel_data.clear();
|
||||
connection_status_event = nullptr;
|
||||
recv_buffer_memory = nullptr;
|
||||
initialized = false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(connection_status_mutex);
|
||||
|
@ -279,6 +279,15 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16
|
||||
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) {
|
||||
EAPoLStartPacket eapol_start{};
|
||||
eapol_start.association_id = association_id;
|
||||
|
@ -51,6 +51,10 @@ struct SecureDataHeader {
|
||||
u16_be sequence_number;
|
||||
u16_be dest_node_id;
|
||||
u16_be src_node_id;
|
||||
|
||||
u32 GetActualDataSize() const {
|
||||
return protocol_size - sizeof(SecureDataHeader);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size");
|
||||
@ -118,6 +122,11 @@ static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wro
|
||||
std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
|
||||
u16 src_node, u16 sequence_number);
|
||||
|
||||
/*
|
||||
* Returns the SecureDataHeader stored in an 802.11 data frame.
|
||||
*/
|
||||
SecureDataHeader ParseSecureDataHeader(const std::vector<u8>& data);
|
||||
|
||||
/*
|
||||
* Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS
|
||||
* communication.
|
||||
|
@ -216,11 +216,11 @@ void Init() {
|
||||
SM::ServiceManager::InstallInterfaces(SM::g_service_manager);
|
||||
|
||||
NS::InstallInterfaces(*SM::g_service_manager);
|
||||
AC::InstallInterfaces(*SM::g_service_manager);
|
||||
|
||||
AddNamedPort(new ERR::ERR_F);
|
||||
|
||||
FS::ArchiveInit();
|
||||
AC::Init();
|
||||
ACT::Init();
|
||||
AM::Init();
|
||||
APT::Init();
|
||||
@ -273,7 +273,6 @@ void Shutdown() {
|
||||
BOSS::Shutdown();
|
||||
APT::Shutdown();
|
||||
AM::Shutdown();
|
||||
AC::Shutdown();
|
||||
FS::ArchiveShutdown();
|
||||
|
||||
SM::g_service_manager = nullptr;
|
||||
|
@ -92,7 +92,7 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
|
||||
if (name_len > Service::kMaxPortSize) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_NAME_SIZE);
|
||||
LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len);
|
||||
LOG_ERROR(Service_SRV, "called name_len=0x%zX -> ERR_INVALID_NAME_SIZE", name_len);
|
||||
return;
|
||||
}
|
||||
std::string name(name_buf.data(), name_len);
|
||||
@ -122,7 +122,8 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ERR_MAX_CONNECTIONS_REACHED);
|
||||
rb.PushObjects(std::move(client_port).Unwrap());
|
||||
} else {
|
||||
LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code());
|
||||
LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(),
|
||||
session.Code().raw);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(session.Code());
|
||||
}
|
||||
|
@ -779,11 +779,7 @@ static void Connect(Interface* self) {
|
||||
}
|
||||
|
||||
static void InitializeSockets(Interface* self) {
|
||||
// TODO(Subv): Implement
|
||||
#ifdef _WIN32
|
||||
WSADATA data;
|
||||
WSAStartup(MAKEWORD(2, 2), &data);
|
||||
#endif
|
||||
// TODO(Subv): Implement
|
||||
|
||||
u32* cmd_buffer = Kernel::GetCommandBuffer();
|
||||
cmd_buffer[0] = IPC::MakeHeader(1, 1, 0);
|
||||
@ -794,10 +790,6 @@ static void ShutdownSockets(Interface* self) {
|
||||
// TODO(Subv): Implement
|
||||
CleanupSockets();
|
||||
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
u32* cmd_buffer = Kernel::GetCommandBuffer();
|
||||
cmd_buffer[1] = 0;
|
||||
}
|
||||
@ -904,6 +896,11 @@ const Interface::FunctionInfo FunctionTable[] = {
|
||||
|
||||
SOC_U::SOC_U() {
|
||||
Register(FunctionTable);
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA data;
|
||||
WSAStartup(MAKEWORD(2, 2), &data);
|
||||
#endif
|
||||
}
|
||||
|
||||
SOC_U::~SOC_U() {
|
||||
|
@ -91,7 +91,7 @@ static void SetInputFormat(Interface* self) {
|
||||
cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
|
||||
LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format);
|
||||
LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast<u8>(conversion.input_format));
|
||||
}
|
||||
|
||||
static void GetInputFormat(Interface* self) {
|
||||
@ -101,7 +101,7 @@ static void GetInputFormat(Interface* self) {
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = static_cast<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) {
|
||||
@ -112,7 +112,7 @@ static void SetOutputFormat(Interface* self) {
|
||||
cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
|
||||
LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format);
|
||||
LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast<u8>(conversion.output_format));
|
||||
}
|
||||
|
||||
static void GetOutputFormat(Interface* self) {
|
||||
@ -122,7 +122,7 @@ static void GetOutputFormat(Interface* self) {
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = static_cast<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) {
|
||||
@ -133,7 +133,7 @@ static void SetRotation(Interface* self) {
|
||||
cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
|
||||
LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation);
|
||||
LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast<u8>(conversion.rotation));
|
||||
}
|
||||
|
||||
static void GetRotation(Interface* self) {
|
||||
@ -143,7 +143,7 @@ static void GetRotation(Interface* self) {
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = static_cast<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) {
|
||||
@ -154,7 +154,8 @@ static void SetBlockAlignment(Interface* self) {
|
||||
cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
|
||||
LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment);
|
||||
LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu",
|
||||
static_cast<u8>(conversion.block_alignment));
|
||||
}
|
||||
|
||||
static void GetBlockAlignment(Interface* self) {
|
||||
@ -164,7 +165,8 @@ static void GetBlockAlignment(Interface* self) {
|
||||
cmd_buff[1] = RESULT_SUCCESS.raw;
|
||||
cmd_buff[2] = static_cast<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,
|
||||
"called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu "
|
||||
"input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX",
|
||||
params->input_format, params->output_format, params->rotation, params->block_alignment,
|
||||
params->input_line_width, params->input_lines, params->standard_coefficient,
|
||||
params->padding, params->alpha);
|
||||
static_cast<u8>(params->input_format), static_cast<u8>(params->output_format),
|
||||
static_cast<u8>(params->rotation), static_cast<u8>(params->block_alignment),
|
||||
params->input_line_width, params->input_lines,
|
||||
static_cast<u8>(params->standard_coefficient), params->padding, params->alpha);
|
||||
}
|
||||
|
||||
static void PingProcess(Interface* self) {
|
||||
|
@ -818,9 +818,7 @@ static ResultCode ReleaseMutex(Kernel::Handle handle) {
|
||||
if (mutex == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
|
||||
mutex->Release();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
return mutex->Release(Kernel::GetCurrentThread());
|
||||
}
|
||||
|
||||
/// Get the ID of the specified process
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
std::vector<u8> EncryptSignCCM(const std::vector<u8>& pdata, const CCMNonce& nonce,
|
||||
size_t slot_id) {
|
||||
if (!IsNormalKeyAvailable(slot_id)) {
|
||||
LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id);
|
||||
LOG_ERROR(HW_AES, "Key slot %zu not available. Will use zero key.", slot_id);
|
||||
}
|
||||
const AESKey normal = GetNormalKey(slot_id);
|
||||
std::vector<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,
|
||||
size_t slot_id) {
|
||||
if (!IsNormalKeyAvailable(slot_id)) {
|
||||
LOG_ERROR(HW_AES, "Key slot %d not available. Will use zero key.", slot_id);
|
||||
LOG_ERROR(HW_AES, "Key slot %zu not available. Will use zero key.", slot_id);
|
||||
}
|
||||
const AESKey normal = GetNormalKey(slot_id);
|
||||
const std::size_t pdata_size = cipher.size() - CCM_MAC_SIZE;
|
||||
|
@ -65,7 +65,7 @@ static Math::Vec4<u8> DecodePixel(Regs::PixelFormat input_format, const u8* src_
|
||||
return Color::DecodeRGBA4(src_pixel);
|
||||
|
||||
default:
|
||||
LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", input_format);
|
||||
LOG_ERROR(HW_GPU, "Unknown source framebuffer format %x", static_cast<u32>(input_format));
|
||||
return {0, 0, 0, 0};
|
||||
}
|
||||
}
|
||||
@ -303,7 +303,7 @@ static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
|
||||
|
||||
default:
|
||||
LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x",
|
||||
config.output_format.Value());
|
||||
static_cast<u32>(config.output_format.Value()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ struct FramebufferRegs {
|
||||
case ColorFormat::RGBA4:
|
||||
return 2;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unknown color format %u", format);
|
||||
LOG_CRITICAL(HW_GPU, "Unknown color format %u", static_cast<u32>(format));
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
@ -258,7 +258,7 @@ struct FramebufferRegs {
|
||||
return 4;
|
||||
}
|
||||
|
||||
ASSERT_MSG(false, "Unknown depth format %u", format);
|
||||
ASSERT_MSG(false, "Unknown depth format %u", static_cast<u32>(format));
|
||||
}
|
||||
|
||||
// Returns the number of bits per depth component of the specified depth format
|
||||
@ -271,7 +271,7 @@ struct FramebufferRegs {
|
||||
return 24;
|
||||
}
|
||||
|
||||
ASSERT_MSG(false, "Unknown depth format %u", format);
|
||||
ASSERT_MSG(false, "Unknown depth format %u", static_cast<u32>(format));
|
||||
}
|
||||
|
||||
INSERT_PADDING_WORDS(0x20);
|
||||
|
@ -1338,7 +1338,8 @@ void RasterizerOpenGL::SyncCullMode() {
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %d", regs.rasterizer.cull_mode.Value());
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %u",
|
||||
static_cast<u32>(regs.rasterizer.cull_mode.Value()));
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config,
|
||||
break;
|
||||
default:
|
||||
out += "vec4(0.0)";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", static_cast<u32>(source));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -323,7 +323,7 @@ static void AppendColorModifier(std::string& out, const PicaShaderConfig& config
|
||||
break;
|
||||
default:
|
||||
out += "vec3(0.0)";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", static_cast<u32>(modifier));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -372,7 +372,7 @@ static void AppendAlphaModifier(std::string& out, const PicaShaderConfig& config
|
||||
break;
|
||||
default:
|
||||
out += "0.0";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", static_cast<u32>(modifier));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -416,7 +416,8 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper
|
||||
break;
|
||||
default:
|
||||
out += "vec3(0.0)";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u",
|
||||
static_cast<u32>(operation));
|
||||
break;
|
||||
}
|
||||
out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0
|
||||
@ -456,7 +457,8 @@ static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation oper
|
||||
break;
|
||||
default:
|
||||
out += "0.0";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u",
|
||||
static_cast<u32>(operation));
|
||||
break;
|
||||
}
|
||||
out += ", 0.0, 1.0)";
|
||||
@ -486,7 +488,7 @@ static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareF
|
||||
|
||||
default:
|
||||
out += "false";
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", static_cast<u32>(func));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ inline GLenum BlendEquation(Pica::FramebufferRegs::BlendEquation equation) {
|
||||
|
||||
// Range check table for input
|
||||
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();
|
||||
|
||||
return GL_FUNC_ADD;
|
||||
@ -133,7 +133,7 @@ inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) {
|
||||
|
||||
// Range check table for input
|
||||
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();
|
||||
|
||||
return GL_ONE;
|
||||
@ -164,7 +164,7 @@ inline GLenum LogicOp(Pica::FramebufferRegs::LogicOp op) {
|
||||
|
||||
// Range check table for input
|
||||
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();
|
||||
|
||||
return GL_COPY;
|
||||
@ -187,7 +187,7 @@ inline GLenum CompareFunc(Pica::FramebufferRegs::CompareFunc func) {
|
||||
|
||||
// Range check table for input
|
||||
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();
|
||||
|
||||
return GL_ALWAYS;
|
||||
@ -210,7 +210,7 @@ inline GLenum StencilOp(Pica::FramebufferRegs::StencilAction action) {
|
||||
|
||||
// Range check table for input
|
||||
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();
|
||||
|
||||
return GL_KEEP;
|
||||
|
@ -853,7 +853,7 @@ void JitShader::Compile_NextInstr() {
|
||||
} else {
|
||||
// Unhandled instruction
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled instruction: 0x%02x (0x%08x)",
|
||||
instr.opcode.Value().EffectiveOpCode(), instr.hex);
|
||||
static_cast<u32>(instr.opcode.Value().EffectiveOpCode()), instr.hex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
|
||||
|
||||
default:
|
||||
LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
|
||||
framebuffer.color_format.Value());
|
||||
static_cast<u32>(framebuffer.color_format.Value()));
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
@ -94,7 +94,7 @@ const Math::Vec4<u8> GetPixel(int x, int y) {
|
||||
|
||||
default:
|
||||
LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
|
||||
framebuffer.color_format.Value());
|
||||
static_cast<u32>(framebuffer.color_format.Value()));
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
@ -123,7 +123,8 @@ u32 GetDepth(int x, int y) {
|
||||
case FramebufferRegs::DepthFormat::D24S8:
|
||||
return Color::DecodeD24S8(src_pixel).x;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u",
|
||||
static_cast<u32>(framebuffer.depth_format.Value()));
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
}
|
||||
@ -151,7 +152,7 @@ u8 GetStencil(int x, int y) {
|
||||
LOG_WARNING(
|
||||
HW_GPU,
|
||||
"GetStencil called for function which doesn't have a stencil component (format %u)",
|
||||
framebuffer.depth_format);
|
||||
static_cast<u32>(framebuffer.depth_format.Value()));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -184,7 +185,8 @@ void SetDepth(int x, int y, u32 value) {
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u",
|
||||
static_cast<u32>(framebuffer.depth_format.Value()));
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
@ -215,7 +217,8 @@ void SetStencil(int x, int y, u8 value) {
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u",
|
||||
static_cast<u32>(framebuffer.depth_format.Value()));
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
@ -294,7 +297,7 @@ Math::Vec4<u8> EvaluateBlendEquation(const Math::Vec4<u8>& src, const Math::Vec4
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation);
|
||||
LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation 0x%x", static_cast<u8>(equation));
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,8 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
|
||||
surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f);
|
||||
surface_tangent = perturbation;
|
||||
} else {
|
||||
LOG_ERROR(HW_GPU, "Unknown bump mode %u", lighting.config0.bump_mode.Value());
|
||||
LOG_ERROR(HW_GPU, "Unknown bump mode %u",
|
||||
static_cast<u32>(lighting.config0.bump_mode.Value()));
|
||||
}
|
||||
} else {
|
||||
surface_normal = Math::MakeVec(0.0f, 0.0f, 1.0f);
|
||||
|
@ -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()));
|
||||
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", factor);
|
||||
LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", static_cast<u32>(factor));
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user